1 // dear imgui, v1.75
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 for demo code. All applications in examples/ are doing that.
8 
9 // Resources:
10 // - FAQ                   http://dearimgui.org/faq
11 // - Homepage & latest     https://github.com/ocornut/imgui
12 // - Releases & changelog  https://github.com/ocornut/imgui/releases
13 // - Gallery               https://github.com/ocornut/imgui/issues/2847 (please post your screenshots/video there!)
14 // - Glossary              https://github.com/ocornut/imgui/wiki/Glossary
15 // - Wiki                  https://github.com/ocornut/imgui/wiki
16 // - Issues & support      https://github.com/ocornut/imgui/issues
17 
18 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
19 // See LICENSE.txt for copyright and licensing details (standard MIT License).
20 // This library is free but I need your support to sustain development and maintenance.
21 // Businesses: you can support continued development via invoiced technical support, maintenance and sponsoring contracts. Please reach out to "contact AT dearimgui.org".
22 // Individuals: you can support continued development via donations. See docs/README or web page.
23 
24 // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
25 // Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
26 // modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
27 // come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
28 // to a better solution or official support for them.
29 
30 /*
31 
32 Index of this file:
33 
34 DOCUMENTATION
35 
36 - MISSION STATEMENT
37 - END-USER GUIDE
38 - PROGRAMMER GUIDE
39   - READ FIRST
40   - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
41   - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
42   - HOW A SIMPLE APPLICATION MAY LOOK LIKE (2 variations)
43   - HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
44   - USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
45 - API BREAKING CHANGES (read me when you update!)
46 - FREQUENTLY ASKED QUESTIONS (FAQ)
47   - Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer)
48 
49 CODE
50 (search for "[SECTION]" in the code to find them)
51 
52 // [SECTION] FORWARD DECLARATIONS
53 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
54 // [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
55 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
56 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
57 // [SECTION] MISC HELPERS/UTILITIES (File functions)
58 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
59 // [SECTION] MISC HELPERS/UTILITIES (Color functions)
60 // [SECTION] ImGuiStorage
61 // [SECTION] ImGuiTextFilter
62 // [SECTION] ImGuiTextBuffer
63 // [SECTION] ImGuiListClipper
64 // [SECTION] RENDER HELPERS
65 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
66 // [SECTION] ERROR CHECKING
67 // [SECTION] SCROLLING
68 // [SECTION] TOOLTIPS
69 // [SECTION] POPUPS
70 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
71 // [SECTION] DRAG AND DROP
72 // [SECTION] LOGGING/CAPTURING
73 // [SECTION] SETTINGS
74 // [SECTION] PLATFORM DEPENDENT HELPERS
75 // [SECTION] METRICS/DEBUG WINDOW
76 
77 */
78 
79 //-----------------------------------------------------------------------------
80 // DOCUMENTATION
81 //-----------------------------------------------------------------------------
82 
83 /*
84 
85  MISSION STATEMENT
86  =================
87 
88  - Easy to use to create code-driven and data-driven tools.
89  - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
90  - Easy to hack and improve.
91  - Minimize screen real-estate usage.
92  - Minimize setup and maintenance.
93  - Minimize state storage on user side.
94  - Portable, minimize dependencies, run on target (consoles, phones, etc.).
95  - Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window,.
96    opening a tree node for the first time, etc. but a typical frame should not allocate anything).
97 
98  Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
99  - Doesn't look fancy, doesn't animate.
100  - Limited layout features, intricate layouts are typically crafted in code.
101 
102 
103  END-USER GUIDE
104  ==============
105 
106  - Double-click on title bar to collapse window.
107  - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin().
108  - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents).
109  - Click and drag on any empty space to move window.
110  - TAB/SHIFT+TAB to cycle through keyboard editable fields.
111  - CTRL+Click on a slider or drag box to input value as text.
112  - Use mouse wheel to scroll.
113  - Text editor:
114    - Hold SHIFT or use mouse to select text.
115    - CTRL+Left/Right to word jump.
116    - CTRL+Shift+Left/Right to select words.
117    - CTRL+A our Double-Click to select all.
118    - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
119    - CTRL+Z,CTRL+Y to undo/redo.
120    - ESCAPE to revert text to its original value.
121    - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
122    - Controls are automatically adjusted for OSX to match standard OSX text editing operations.
123  - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard.
124  - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://goo.gl/9LgVZW
125 
126 
127  PROGRAMMER GUIDE
128  ================
129 
130  READ FIRST
131  ----------
132  - Remember to read the FAQ (https://www.dearimgui.org/faq)
133  - 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
134    or destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, less bugs.
135  - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
136  - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
137  - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
138    You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links docs/README.md.
139  - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
140    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,
141    where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
142  - Our origin are on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
143  - This codebase is also optimized to yield decent performances with typical "Debug" builds settings.
144  - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).
145    If you get an assert, read the messages and comments around the assert.
146  - 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.
147  - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
148    See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
149    However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase.
150  - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!).
151 
152  HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
153  ----------------------------------------------
154  - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
155  - Or maintain your own branch where you have imconfig.h modified.
156  - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
157    If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
158    from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
159    likely be a comment about it. Please report any issue to the GitHub page!
160  - Try to keep your copy of dear imgui reasonably up to date.
161 
162  GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
163  ---------------------------------------------------------------
164  - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
165  - In the majority of cases you should be able to use unmodified back-ends files available in the examples/ folder.
166  - Add the Dear ImGui source files to your projects or using your preferred build system.
167    It is recommended you build and statically link the .cpp files as part of your project and NOT as shared library (DLL).
168  - 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.
169  - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
170  - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
171    Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
172    phases of your own application. All rendering information are stored into command-lists that you will retrieve after calling ImGui::Render().
173  - Refer to the bindings and demo applications in the examples/ folder for instruction on how to setup your code.
174  - 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.
175 
176  HOW A SIMPLE APPLICATION MAY LOOK LIKE
177  --------------------------------------
178  EXHIBIT 1: USING THE EXAMPLE BINDINGS (imgui_impl_XXX.cpp files from the examples/ folder).
179 
180      // Application init: create a dear imgui context, setup some options, load fonts
181      ImGui::CreateContext();
182      ImGuiIO& io = ImGui::GetIO();
183      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
184      // TODO: Fill optional fields of the io structure later.
185      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
186 
187      // Initialize helper Platform and Renderer bindings (here we are using imgui_impl_win32.cpp and imgui_impl_dx11.cpp)
188      ImGui_ImplWin32_Init(hwnd);
189      ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
190 
191      // Application main loop
192      while (true)
193      {
194          // Feed inputs to dear imgui, start new frame
195          ImGui_ImplDX11_NewFrame();
196          ImGui_ImplWin32_NewFrame();
197          ImGui::NewFrame();
198 
199          // Any application code here
200          ImGui::Text("Hello, world!");
201 
202          // Render dear imgui into screen
203          ImGui::Render();
204          ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
205          g_pSwapChain->Present(1, 0);
206      }
207 
208      // Shutdown
209      ImGui_ImplDX11_Shutdown();
210      ImGui_ImplWin32_Shutdown();
211      ImGui::DestroyContext();
212 
213  EXHIBIT 2: IMPLEMENTING CUSTOM BINDING / CUSTOM ENGINE
214 
215      // Application init: create a dear imgui context, setup some options, load fonts
216      ImGui::CreateContext();
217      ImGuiIO& io = ImGui::GetIO();
218      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
219      // TODO: Fill optional fields of the io structure later.
220      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
221 
222      // Build and load the texture atlas into a texture
223      // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
224      int width, height;
225      unsigned char* pixels = NULL;
226      io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
227 
228      // At this point you've got the texture data and you need to upload that your your graphic system:
229      // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
230      // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID.
231      MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
232      io.Fonts->TexID = (void*)texture;
233 
234      // Application main loop
235      while (true)
236      {
237         // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
238         // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform bindings)
239         io.DeltaTime = 1.0f/60.0f;              // set the time elapsed since the previous frame (in seconds)
240         io.DisplaySize.x = 1920.0f;             // set the current display width
241         io.DisplaySize.y = 1280.0f;             // set the current display height here
242         io.MousePos = my_mouse_pos;             // set the mouse position
243         io.MouseDown[0] = my_mouse_buttons[0];  // set the mouse button states
244         io.MouseDown[1] = my_mouse_buttons[1];
245 
246         // Call NewFrame(), after this point you can use ImGui::* functions anytime
247         // (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use Dear ImGui everywhere)
248         ImGui::NewFrame();
249 
250         // Most of your application code here
251         ImGui::Text("Hello, world!");
252         MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
253         MyGameRender(); // may use any Dear ImGui functions as well!
254 
255         // Render dear imgui, swap buffers
256         // (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)
257         ImGui::EndFrame();
258         ImGui::Render();
259         ImDrawData* draw_data = ImGui::GetDrawData();
260         MyImGuiRenderFunction(draw_data);
261         SwapBuffers();
262      }
263 
264      // Shutdown
265      ImGui::DestroyContext();
266 
267  HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
268  ---------------------------------------------
269     void void MyImGuiRenderFunction(ImDrawData* draw_data)
270     {
271        // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
272        // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
273        // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
274        // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
275        for (int n = 0; n < draw_data->CmdListsCount; n++)
276        {
277           const ImDrawList* cmd_list = draw_data->CmdLists[n];
278           const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;  // vertex buffer generated by Dear ImGui
279           const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;   // index buffer generated by Dear ImGui
280           for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
281           {
282              const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
283              if (pcmd->UserCallback)
284              {
285                  pcmd->UserCallback(cmd_list, pcmd);
286              }
287              else
288              {
289                  // The texture for the draw call is specified by pcmd->TextureId.
290                  // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization.
291                  MyEngineBindTexture((MyTexture*)pcmd->TextureId);
292 
293                  // We are using scissoring to clip some objects. All low-level graphics API should supports it.
294                  // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
295                  //   (some elements visible outside their bounds) but you can fix that once everything else works!
296                  // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize)
297                  //   In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize.
298                  //   However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github),
299                  //   always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
300                  // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
301                  ImVec2 pos = draw_data->DisplayPos;
302                  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));
303 
304                  // Render 'pcmd->ElemCount/3' indexed triangles.
305                  // 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.
306                  MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
307              }
308              idx_buffer += pcmd->ElemCount;
309           }
310        }
311     }
312 
313  - The examples/ folders contains many actual implementation of the pseudo-codes above.
314  - When calling NewFrame(), the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags are updated.
315    They tell you if Dear ImGui intends to use your inputs. When a flag is set you want to hide the corresponding inputs from the
316    rest of your application. In every cases you need to pass on the inputs to Dear ImGui.
317  - Refer to the FAQ for more information. Amusingly, it is called a FAQ because people frequently run into the same issues!
318 
319  USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
320  ------------------------------------------
321  - The gamepad/keyboard navigation is fairly functional and keeps being improved.
322  - Gamepad support is particularly useful to use Dear ImGui on a console system (e.g. PS4, Switch, XB1) without a mouse!
323  - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787
324  - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
325  - Keyboard:
326     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
327       NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
328     - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
329       will be set. For more advanced uses, you may want to read from:
330        - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
331        - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
332        - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions.
333       Please reach out if you think the game vs navigation input sharing could be improved.
334  - Gamepad:
335     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
336     - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
337       Note that io.NavInputs[] is cleared by EndFrame().
338     - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
339          0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
340     - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
341       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.).
342     - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://goo.gl/9LgVZW.
343     - 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
344       to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
345  - Mouse:
346     - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
347     - 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.
348     - 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.
349       Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
350       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.
351       When that happens your back-end NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the binding in examples/ do that.
352       (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!)
353       (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
354        to set a boolean to ignore your other external mouse positions until the external source is moved again.)
355 
356 
357  API BREAKING CHANGES
358  ====================
359 
360  Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
361  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.
362  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.
363  You can read releases logs https://github.com/ocornut/imgui/releases for more details.
364 
365  - 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more.
366  - 2019/12/17 (1.75) - 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.
367  - 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.
368  - 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017):
369                        - ShowTestWindow()                    -> use ShowDemoWindow()
370                        - IsRootWindowFocused()               -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow)
371                        - IsRootWindowOrAnyChildFocused()     -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
372                        - SetNextWindowContentWidth(w)        -> use SetNextWindowContentSize(ImVec2(w, 0.0f)
373                        - GetItemsLineHeightWithSpacing()     -> use GetFrameHeightWithSpacing()
374                        - ImGuiCol_ChildWindowBg              -> use ImGuiCol_ChildBg
375                        - ImGuiStyleVar_ChildWindowRounding   -> use ImGuiStyleVar_ChildRounding
376                        - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap
377                        - IMGUI_DISABLE_TEST_WINDOWS          -> use IMGUI_DISABLE_DEMO_WINDOWS
378  - 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.
379  - 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).
380  - 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.
381  - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency.
382  - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency.
383  - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017):
384                        - Begin() [old 5 args version]        -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed
385                        - IsRootWindowOrAnyChildHovered()     -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows)
386                        - AlignFirstTextHeightToWidgets()     -> use AlignTextToFramePadding()
387                        - SetNextWindowPosCenter()            -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f)
388                        - ImFont::Glyph                       -> use ImFontGlyph
389  - 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.
390                        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.
391                        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).
392                        If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you.
393  - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete).
394  - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete).
395  - 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.
396  - 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
397                        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.
398                        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.
399                        Please reach out if you are affected.
400  - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete).
401  - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c).
402  - 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.
403  - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).
404  - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).
405  - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).
406  - 2019/02/14 (1.68) - made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). If for some reason your time step calculation gives you a zero value, replace it with a dummy small value!
407  - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
408  - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
409  - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).
410  - 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.
411  - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
412  - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
413  - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
414  - 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.
415                        If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
416  - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
417  - 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.
418                        NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
419                        Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
420  - 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).
421  - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
422  - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
423  - 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.
424  - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
425  - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
426  - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
427  - 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.).
428                        old bindings will still work as is, however prefer using the separated bindings as they will be updated to support multi-viewports.
429                        when adopting new bindings follow the main.cpp code of your preferred examples/ folder to know which functions to call.
430                        in particular, note that old bindings called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function.
431  - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
432  - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
433  - 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.
434                        If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
435                        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.
436                        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.
437  - 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",
438                        consistent with other functions. Kept redirection functions (will obsolete).
439  - 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.
440  - 2018/03/20 (1.60) - renamed io.WantMoveMouse to io.WantSetMousePos for consistency and ease of understanding (was added in 1.52, _not_ used by core and only honored by some binding ahead of merging the Nav branch).
441  - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
442  - 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.
443  - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
444  - 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.
445  - 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.
446  - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
447                        - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
448                        - removed Shutdown() function, as DestroyContext() serve this purpose.
449                        - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
450                        - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
451                        - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
452  - 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.
453  - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
454  - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
455  - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
456  - 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.
457  - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
458  - 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
459  - 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.
460  - 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.
461  - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
462  - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
463                      - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
464  - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
465  - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
466  - 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.
467  - 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.
468                        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.
469  - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
470  - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
471  - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
472  - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
473  - 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.
474  - 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.
475  - 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.
476                        removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
477                          IsItemHoveredRect()        --> IsItemHovered(ImGuiHoveredFlags_RectOnly)
478                          IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
479                          IsMouseHoveringWindow()    --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior]
480  - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
481  - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
482  - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).
483  - 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).
484  - 2017/08/25 (1.52) - io.MousePos needs to be set to ImVec2(-FLT_MAX,-FLT_MAX) when mouse is unavailable/missing. Previously ImVec2(-1,-1) was enough but we now accept negative mouse coordinates. In your binding if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)".
485  - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
486                      - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
487                      - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
488  - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
489  - 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.
490  - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type.
491  - 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.
492  - 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).
493  - 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).
494  - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
495  - 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.
496                      - 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.
497                      - 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))'
498  - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
499  - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
500  - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
501  - 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().
502  - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
503  - 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.
504  - 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.
505  - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
506                        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.
507                        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:
508                        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); }
509                        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.
510  - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
511  - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
512  - 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).
513  - 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.
514  - 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).
515  - 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)
516  - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
517  - 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.
518  - 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.
519  - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
520  - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
521  - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
522                        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.
523                        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!
524  - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
525  - 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.
526  - 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
527  - 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.
528                        you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
529  - 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.
530                        this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
531                      - if you are using a vanilla copy of one of the imgui_impl_XXXX.cpp provided in the example, you just need to update your copy and you can ignore the rest.
532                      - the signature of the io.RenderDrawListsFn handler has changed!
533                        old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
534                        new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
535                          parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
536                          ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
537                          ImDrawCmd:  'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
538                      - 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.
539                      - 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!
540                      - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
541  - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
542  - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
543  - 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.
544  - 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
545  - 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!
546  - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
547  - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
548  - 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.
549  - 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.
550  - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
551  - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
552  - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
553  - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
554  - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
555  - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
556  - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
557  - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
558  - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
559  - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
560  - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
561  - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
562  - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
563  - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
564  - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
565  - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
566  - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
567  - 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
568                        - old:  const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..];
569                        - new:  unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->TexId = YourTexIdentifier;
570                        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.
571  - 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.
572  - 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
573  - 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
574  - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
575  - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
576  - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
577  - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
578  - 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)
579  - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
580  - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
581  - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
582  - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
583  - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
584  - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
585 
586 
587  FREQUENTLY ASKED QUESTIONS (FAQ)
588  ================================
589 
590  Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer)
591  Some answers are copied down here to facilitate searching in code.
592 
593  Q&A: Basics
594  ===========
595 
596  Q: Where is the documentation?
597  A: This library is poorly documented at the moment and expects of the user to be acquainted with C/C++.
598     - Run the examples/ and explore them.
599     - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function.
600     - The demo covers most features of Dear ImGui, so you can read the code and see its output.
601     - See documentation and comments at the top of imgui.cpp + effectively imgui.h.
602     - Dozens of standalone example applications using e.g. OpenGL/DirectX are provided in the
603       examples/ folder to explain how to integrate Dear ImGui with your own engine/application.
604     - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links.
605     - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful.
606     - Your programming IDE is your friend, find the type or function declaration to find comments
607       associated to it.
608 
609  Q: What is this library called?
610  Q: Which version should I get?
611  >> This library is called "Dear ImGui", please don't call it "ImGui" :)
612  >> See https://www.dearimgui.org/faq
613 
614  Q&A: Integration
615  ================
616 
617  Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or to my application?
618  A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
619  >> See https://www.dearimgui.org/faq for fully detailed answer. You really want to read this.
620 
621  Q. How can I enable keyboard controls?
622  Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display)
623  Q: I integrated Dear ImGui in my engine and the text or lines are blurry..
624  Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
625  >> See https://www.dearimgui.org/faq
626 
627  Q&A: Usage
628  ----------
629 
630  Q: Why are multiple widgets reacting when I interact with a single one?
631  Q: How can I have multiple widgets with the same label or with an empty label?
632  A: A primer on labels and the ID Stack...
633 
634     Dear ImGui internally need to uniquely identify UI elements.
635     Elements that are typically not clickable (such as calls to the Text functions) don't need an ID.
636     Interactive widgets (such as calls to Button buttons) need a unique ID.
637     Unique ID are used internally to track active widgets and occasionally associate state to widgets.
638     Unique ID are implicitly built from the hash of multiple elements that identify the "path" to the UI element.
639 
640    - Unique ID are often derived from a string label:
641 
642        Button("OK");          // Label = "OK",     ID = hash of (..., "OK")
643        Button("Cancel");      // Label = "Cancel", ID = hash of (..., "Cancel")
644 
645    - ID are uniquely scoped within windows, tree nodes, etc. which all pushes to the ID stack. Having
646      two buttons labeled "OK" in different windows or different tree locations is fine.
647      We used "..." above to signify whatever was already pushed to the ID stack previously:
648 
649        Begin("MyWindow");
650        Button("OK");          // Label = "OK",     ID = hash of ("MyWindow", "OK")
651        End();
652        Begin("MyOtherWindow");
653        Button("OK");          // Label = "OK",     ID = hash of ("MyOtherWindow", "OK")
654        End();
655 
656    - If you have a same ID twice in the same location, you'll have a conflict:
657 
658        Button("OK");
659        Button("OK");          // ID collision! Interacting with either button will trigger the first one.
660 
661      Fear not! this is easy to solve and there are many ways to solve it!
662 
663    - Solving ID conflict in a simple/local context:
664      When passing a label you can optionally specify extra ID information within string itself.
665      Use "##" to pass a complement to the ID that won't be visible to the end-user.
666      This helps solving the simple collision cases when you know e.g. at compilation time which items
667      are going to be created:
668 
669        Begin("MyWindow");
670        Button("Play");        // Label = "Play",   ID = hash of ("MyWindow", "Play")
671        Button("Play##foo1");  // Label = "Play",   ID = hash of ("MyWindow", "Play##foo1")  // Different from above
672        Button("Play##foo2");  // Label = "Play",   ID = hash of ("MyWindow", "Play##foo2")  // Different from above
673        End();
674 
675    - If you want to completely hide the label, but still need an ID:
676 
677        Checkbox("##On", &b);  // Label = "",       ID = hash of (..., "##On")   // No visible label, just a checkbox!
678 
679    - Occasionally/rarely you might want change a label while preserving a constant ID. This allows
680      you to animate labels. For example you may want to include varying information in a window title bar,
681      but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID:
682 
683        Button("Hello###ID");  // Label = "Hello",  ID = hash of (..., "###ID")
684        Button("World###ID");  // Label = "World",  ID = hash of (..., "###ID")  // Same as above, even though the label looks different
685 
686        sprintf(buf, "My game (%f FPS)###MyGame", fps);
687        Begin(buf);            // Variable title,   ID = hash of "MyGame"
688 
689    - Solving ID conflict in a more general manner:
690      Use PushID() / PopID() to create scopes and manipulate the ID stack, as to avoid ID conflicts
691      within the same window. This is the most convenient way of distinguishing ID when iterating and
692      creating many UI elements programmatically.
693      You can push a pointer, a string or an integer value into the ID stack.
694      Remember that ID are formed from the concatenation of _everything_ pushed into the ID stack.
695      At each level of the stack we store the seed used for items at this level of the ID stack.
696 
697      Begin("Window");
698        for (int i = 0; i < 100; i++)
699        {
700          PushID(i);           // Push i to the id tack
701          Button("Click");     // Label = "Click",  ID = hash of ("Window", i, "Click")
702          PopID();
703        }
704        for (int i = 0; i < 100; i++)
705        {
706          MyObject* obj = Objects[i];
707          PushID(obj);
708          Button("Click");     // Label = "Click",  ID = hash of ("Window", obj pointer, "Click")
709          PopID();
710        }
711        for (int i = 0; i < 100; i++)
712        {
713          MyObject* obj = Objects[i];
714          PushID(obj->Name);
715          Button("Click");     // Label = "Click",  ID = hash of ("Window", obj->Name, "Click")
716          PopID();
717        }
718        End();
719 
720    - You can stack multiple prefixes into the ID stack:
721 
722        Button("Click");       // Label = "Click",  ID = hash of (..., "Click")
723        PushID("node");
724        Button("Click");       // Label = "Click",  ID = hash of (..., "node", "Click")
725          PushID(my_ptr);
726            Button("Click");   // Label = "Click",  ID = hash of (..., "node", my_ptr, "Click")
727          PopID();
728        PopID();
729 
730    - Tree nodes implicitly creates a scope for you by calling PushID().
731 
732        Button("Click");       // Label = "Click",  ID = hash of (..., "Click")
733        if (TreeNode("node"))  // <-- this function call will do a PushID() for you (unless instructed not to, with a special flag)
734        {
735          Button("Click");     // Label = "Click",  ID = hash of (..., "node", "Click")
736          TreePop();
737        }
738 
739    - When working with trees, ID are used to preserve the open/close state of each tree node.
740      Depending on your use cases you may want to use strings, indices or pointers as ID.
741       e.g. when following a single pointer that may change over time, using a static string as ID
742        will preserve your node open/closed state when the targeted object change.
743       e.g. when displaying a list of objects, using indices or pointers as ID will preserve the
744        node open/closed state differently. See what makes more sense in your situation!
745 
746  Q: How can I display an image? What is ImTextureID, how does it works?
747  >> See https://www.dearimgui.org/faq and https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
748 
749  Q: How can I use my own math types instead of ImVec2/ImVec4?
750  Q: How can I interact with standard C++ types (such as std::string and std::vector)?
751  Q: How can I display custom shapes? (using low-level ImDrawList API)
752  >> See https://www.dearimgui.org/faq
753 
754  Q&A: Fonts, Text
755  ================
756 
757  Q: How can I load a different font than the default?
758  Q: How can I easily use icons in my application?
759  Q: How can I load multiple fonts?
760  Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
761  >> See https://www.dearimgui.org/faq and docs/FONTS.txt
762 
763  Q&A: Concerns
764  =============
765 
766  Q: Who uses Dear ImGui?
767  Q: Can you create elaborate/serious tools with Dear ImGui?
768  Q: Can you reskin the look of Dear ImGui?
769  Q: Why using C++ (as opposed to C)?
770  >> See https://www.dearimgui.org/faq
771 
772  Q&A: Community
773  ==============
774 
775  Q: How can I help?
776  A: - Businesses: please reach out to "contact AT dearimgui.org" if you work in a place using Dear ImGui!
777       We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts.
778       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.
779     - Individuals: you can support continued development via PayPal donations. See README.
780     - If you are experienced with Dear ImGui and C++, look at the github issues, look at the Wiki, read docs/TODO.txt
781       and see how you want to help and can help!
782     - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
783       You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/2847). Visuals are ideal as they inspire other programmers.
784       But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions.
785     - 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).
786 
787 */
788 
789 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
790 #define _CRT_SECURE_NO_WARNINGS
791 #endif
792 
793 #include "imgui.h"
794 #ifndef IMGUI_DISABLE
795 
796 #ifndef IMGUI_DEFINE_MATH_OPERATORS
797 #define IMGUI_DEFINE_MATH_OPERATORS
798 #endif
799 #include "imgui_internal.h"
800 
801 #include <ctype.h>      // toupper
802 #include <stdio.h>      // vsnprintf, sscanf, printf
803 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
804 #include <stddef.h>     // intptr_t
805 #else
806 #include <stdint.h>     // intptr_t
807 #endif
808 
809 // Debug options
810 #define IMGUI_DEBUG_NAV_SCORING     0   // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL
811 #define IMGUI_DEBUG_NAV_RECTS       0   // Display the reference navigation rectangle for each window
812 #define IMGUI_DEBUG_INI_SETTINGS    0   // Save additional comments in .ini file (particularly helps for Docking, but makes saving slower)
813 
814 // Visual Studio warnings
815 #ifdef _MSC_VER
816 #pragma warning (disable: 4127)     // condition expression is constant
817 #pragma warning (disable: 4996)     // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
818 #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later
819 #pragma warning (disable: 5054)     // operator '|': deprecated between enumerations of different types
820 #endif
821 #endif
822 
823 // Clang/GCC warnings with -Weverything
824 #if defined(__clang__)
825 #pragma clang diagnostic ignored "-Wunknown-pragmas"        // warning : unknown warning group '-Wformat-pedantic *'        // not all warnings are known by all clang versions.. so ignoring warnings triggers new warnings on some configuration. great!
826 #pragma clang diagnostic ignored "-Wold-style-cast"         // warning : use of old-style cast                              // yes, they are more terse.
827 #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.
828 #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.
829 #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.
830 #pragma clang diagnostic ignored "-Wglobal-constructors"    // warning : declaration requires a global destructor           // similar to above, not sure what the exact difference is.
831 #pragma clang diagnostic ignored "-Wsign-conversion"        // warning : implicit conversion changes signedness             //
832 #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.
833 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast"       // warning : cast to 'void *' from smaller integer type 'int'
834 #if __has_warning("-Wzero-as-null-pointer-constant")
835 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"  // warning : zero as null pointer constant              // some standard header variations use #define NULL 0
836 #endif
837 #if __has_warning("-Wdouble-promotion")
838 #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.
839 #endif
840 #elif defined(__GNUC__)
841 // We disable -Wpragmas because GCC doesn't provide an has_warning equivalent and some forks/patches may not following the warning/version association.
842 #pragma GCC diagnostic ignored "-Wpragmas"                  // warning: unknown option after '#pragma GCC diagnostic' kind
843 #pragma GCC diagnostic ignored "-Wunused-function"          // warning: 'xxxx' defined but not used
844 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"      // warning: cast to pointer from integer of different size
845 #pragma GCC diagnostic ignored "-Wformat"                   // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
846 #pragma GCC diagnostic ignored "-Wdouble-promotion"         // warning: implicit conversion from 'float' to 'double' when passing argument to function
847 #pragma GCC diagnostic ignored "-Wconversion"               // warning: conversion to 'xxxx' from 'xxxx' may alter its value
848 #pragma GCC diagnostic ignored "-Wformat-nonliteral"        // warning: format not a string literal, format string not checked
849 #pragma GCC diagnostic ignored "-Wstrict-overflow"          // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
850 #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
851 #endif
852 
853 // 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.
854 static const float NAV_WINDOWING_HIGHLIGHT_DELAY            = 0.20f;    // Time before the highlight and screen dimming starts fading in
855 static const float NAV_WINDOWING_LIST_APPEAR_DELAY          = 0.15f;    // Time before the window list starts to appear
856 
857 // Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by back-end)
858 static const float WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS = 4.0f;     // Extend outside and inside windows. Affect FindHoveredWindow().
859 static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f;    // Reduce visual noise by only highlighting the border after a certain time.
860 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 certaint time, unless mouse moved.
861 
862 //-------------------------------------------------------------------------
863 // [SECTION] FORWARD DECLARATIONS
864 //-------------------------------------------------------------------------
865 
866 static void             SetCurrentWindow(ImGuiWindow* window);
867 static void             FindHoveredWindow();
868 static ImGuiWindow*     CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
869 static ImVec2           CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges);
870 
871 static void             AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
872 static void             AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
873 
874 static ImRect           GetViewportRect();
875 
876 // Settings
877 static void*            WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
878 static void             WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
879 static void             WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf);
880 
881 // Platform Dependents default implementation for IO functions
882 static const char*      GetClipboardTextFn_DefaultImpl(void* user_data);
883 static void             SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
884 static void             ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
885 
886 namespace ImGui
887 {
888 // Navigation
889 static void             NavUpdate();
890 static void             NavUpdateWindowing();
891 static void             NavUpdateWindowingOverlay();
892 static void             NavUpdateMoveResult();
893 static float            NavUpdatePageUpPageDown();
894 static inline void      NavUpdateAnyRequestFlag();
895 static bool             NavScoreItem(ImGuiNavMoveResult* result, ImRect cand);
896 static void             NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id);
897 static ImVec2           NavCalcPreferredRefPos();
898 static void             NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
899 static ImGuiWindow*     NavRestoreLastChildNavWindow(ImGuiWindow* window);
900 static int              FindWindowFocusIndex(ImGuiWindow* window);
901 
902 // Error Checking
903 static void             ErrorCheckEndFrame();
904 static void             ErrorCheckBeginEndCompareStacksSize(ImGuiWindow* window, bool write);
905 
906 // Misc
907 static void             UpdateMouseInputs();
908 static void             UpdateMouseWheel();
909 static bool             UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]);
910 static void             UpdateDebugToolItemPicker();
911 static void             RenderWindowOuterBorders(ImGuiWindow* window);
912 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);
913 static void             RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
914 
915 }
916 
917 //-----------------------------------------------------------------------------
918 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
919 //-----------------------------------------------------------------------------
920 
921 // Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
922 // ImGui::CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext().
923 // 1) Important: globals are not shared across DLL boundaries! If you use DLLs or any form of hot-reloading: you will need to call
924 //    SetCurrentContext() (with the pointer you got from CreateContext) from each unique static/DLL boundary, and after each hot-reloading.
925 //    In your debugger, add GImGui to your watch window and notice how its value changes depending on which location you are currently stepping into.
926 // 2) Important: Dear ImGui functions are not thread-safe because of this pointer.
927 //    If you want thread-safety to allow N threads to access N different contexts, you can:
928 //    - Change this variable to use thread local storage so each thread can refer to a different context, in imconfig.h:
929 //          struct ImGuiContext;
930 //          extern thread_local ImGuiContext* MyImGuiTLS;
931 //          #define GImGui MyImGuiTLS
932 //      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.
933 //    - Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
934 //    - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from different namespace.
935 #ifndef GImGui
936 ImGuiContext*   GImGui = NULL;
937 #endif
938 
939 // Memory Allocator functions. Use SetAllocatorFunctions() to change them.
940 // If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file.
941 // 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.
942 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
MallocWrapper(size_t size,void * user_data)943 static void*   MallocWrapper(size_t size, void* user_data)    { IM_UNUSED(user_data); return malloc(size); }
FreeWrapper(void * ptr,void * user_data)944 static void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); free(ptr); }
945 #else
MallocWrapper(size_t size,void * user_data)946 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)947 static void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }
948 #endif
949 
950 static void*  (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper;
951 static void   (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper;
952 static void*    GImAllocatorUserData = NULL;
953 
954 //-----------------------------------------------------------------------------
955 // [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
956 //-----------------------------------------------------------------------------
957 
ImGuiStyle()958 ImGuiStyle::ImGuiStyle()
959 {
960     Alpha                   = 1.0f;             // Global alpha applies to everything in ImGui
961     WindowPadding           = ImVec2(8,8);      // Padding within a window
962     WindowRounding          = 7.0f;             // Radius of window corners rounding. Set to 0.0f to have rectangular windows
963     WindowBorderSize        = 1.0f;             // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
964     WindowMinSize           = ImVec2(32,32);    // Minimum window size
965     WindowTitleAlign        = ImVec2(0.0f,0.5f);// Alignment for title bar text
966     WindowMenuButtonPosition= ImGuiDir_Left;    // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.
967     ChildRounding           = 0.0f;             // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
968     ChildBorderSize         = 1.0f;             // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
969     PopupRounding           = 0.0f;             // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
970     PopupBorderSize         = 1.0f;             // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
971     FramePadding            = ImVec2(4,3);      // Padding within a framed rectangle (used by most widgets)
972     FrameRounding           = 0.0f;             // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
973     FrameBorderSize         = 0.0f;             // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
974     ItemSpacing             = ImVec2(8,4);      // Horizontal and vertical spacing between widgets/lines
975     ItemInnerSpacing        = ImVec2(4,4);      // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
976     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!
977     IndentSpacing           = 21.0f;            // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
978     ColumnsMinSpacing       = 6.0f;             // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
979     ScrollbarSize           = 14.0f;            // Width of the vertical scrollbar, Height of the horizontal scrollbar
980     ScrollbarRounding       = 9.0f;             // Radius of grab corners rounding for scrollbar
981     GrabMinSize             = 10.0f;            // Minimum width/height of a grab box for slider/scrollbar
982     GrabRounding            = 0.0f;             // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
983     TabRounding             = 4.0f;             // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
984     TabBorderSize           = 0.0f;             // Thickness of border around tabs.
985     ColorButtonPosition     = ImGuiDir_Right;   // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
986     ButtonTextAlign         = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
987     SelectableTextAlign     = ImVec2(0.0f,0.0f);// Alignment of selectable text when button is larger than text.
988     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.
989     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.
990     MouseCursorScale        = 1.0f;             // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
991     AntiAliasedLines        = true;             // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
992     AntiAliasedFill         = true;             // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
993     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.
994     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.
995 
996     // Default theme
997     ImGui::StyleColorsDark(this);
998 }
999 
1000 // 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.
1001 // 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)1002 void ImGuiStyle::ScaleAllSizes(float scale_factor)
1003 {
1004     WindowPadding = ImFloor(WindowPadding * scale_factor);
1005     WindowRounding = ImFloor(WindowRounding * scale_factor);
1006     WindowMinSize = ImFloor(WindowMinSize * scale_factor);
1007     ChildRounding = ImFloor(ChildRounding * scale_factor);
1008     PopupRounding = ImFloor(PopupRounding * scale_factor);
1009     FramePadding = ImFloor(FramePadding * scale_factor);
1010     FrameRounding = ImFloor(FrameRounding * scale_factor);
1011     ItemSpacing = ImFloor(ItemSpacing * scale_factor);
1012     ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
1013     TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
1014     IndentSpacing = ImFloor(IndentSpacing * scale_factor);
1015     ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
1016     ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
1017     ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
1018     GrabMinSize = ImFloor(GrabMinSize * scale_factor);
1019     GrabRounding = ImFloor(GrabRounding * scale_factor);
1020     TabRounding = ImFloor(TabRounding * scale_factor);
1021     DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
1022     DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
1023     MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
1024 }
1025 
ImGuiIO()1026 ImGuiIO::ImGuiIO()
1027 {
1028     // Most fields are initialized with zero
1029     memset(this, 0, sizeof(*this));
1030     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.
1031 
1032     // Settings
1033     ConfigFlags = ImGuiConfigFlags_None;
1034     BackendFlags = ImGuiBackendFlags_None;
1035     DisplaySize = ImVec2(-1.0f, -1.0f);
1036     DeltaTime = 1.0f/60.0f;
1037     IniSavingRate = 5.0f;
1038     IniFilename = "imgui.ini";
1039     LogFilename = "imgui_log.txt";
1040     MouseDoubleClickTime = 0.30f;
1041     MouseDoubleClickMaxDist = 6.0f;
1042     for (int i = 0; i < ImGuiKey_COUNT; i++)
1043         KeyMap[i] = -1;
1044     KeyRepeatDelay = 0.275f;
1045     KeyRepeatRate = 0.050f;
1046     UserData = NULL;
1047 
1048     Fonts = NULL;
1049     FontGlobalScale = 1.0f;
1050     FontDefault = NULL;
1051     FontAllowUserScaling = false;
1052     DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1053 
1054     // Miscellaneous options
1055     MouseDrawCursor = false;
1056 #ifdef __APPLE__
1057     ConfigMacOSXBehaviors = true;  // Set Mac OS X style defaults based on __APPLE__ compile time flag
1058 #else
1059     ConfigMacOSXBehaviors = false;
1060 #endif
1061     ConfigInputTextCursorBlink = true;
1062     ConfigWindowsResizeFromEdges = true;
1063     ConfigWindowsMoveFromTitleBarOnly = false;
1064     ConfigWindowsMemoryCompactTimer = 60.0f;
1065 
1066     // Platform Functions
1067     BackendPlatformName = BackendRendererName = NULL;
1068     BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1069     GetClipboardTextFn = GetClipboardTextFn_DefaultImpl;   // Platform dependent default implementations
1070     SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
1071     ClipboardUserData = NULL;
1072     ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
1073     ImeWindowHandle = NULL;
1074 
1075 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1076     RenderDrawListsFn = NULL;
1077 #endif
1078 
1079     // Input (NB: we already have memset zero the entire structure!)
1080     MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1081     MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1082     MouseDragThreshold = 6.0f;
1083     for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1084     for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i]  = KeysDownDurationPrev[i] = -1.0f;
1085     for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
1086 }
1087 
1088 // Pass in translated ASCII characters for text input.
1089 // - with glfw you can get those from the callback set in glfwSetCharCallback()
1090 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(unsigned int c)1091 void ImGuiIO::AddInputCharacter(unsigned int c)
1092 {
1093     if (c > 0 && c <= IM_UNICODE_CODEPOINT_MAX)
1094         InputQueueCharacters.push_back((ImWchar)c);
1095 }
1096 
AddInputCharactersUTF8(const char * utf8_chars)1097 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1098 {
1099     while (*utf8_chars != 0)
1100     {
1101         unsigned int c = 0;
1102         utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
1103         if (c > 0 && c <= IM_UNICODE_CODEPOINT_MAX)
1104             InputQueueCharacters.push_back((ImWchar)c);
1105     }
1106 }
1107 
ClearInputCharacters()1108 void ImGuiIO::ClearInputCharacters()
1109 {
1110     InputQueueCharacters.resize(0);
1111 }
1112 
1113 //-----------------------------------------------------------------------------
1114 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
1115 //-----------------------------------------------------------------------------
1116 
ImBezierClosestPoint(const ImVec2 & p1,const ImVec2 & p2,const ImVec2 & p3,const ImVec2 & p4,const ImVec2 & p,int num_segments)1117 ImVec2 ImBezierClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments)
1118 {
1119     IM_ASSERT(num_segments > 0); // Use ImBezierClosestPointCasteljau()
1120     ImVec2 p_last = p1;
1121     ImVec2 p_closest;
1122     float p_closest_dist2 = FLT_MAX;
1123     float t_step = 1.0f / (float)num_segments;
1124     for (int i_step = 1; i_step <= num_segments; i_step++)
1125     {
1126         ImVec2 p_current = ImBezierCalc(p1, p2, p3, p4, t_step * i_step);
1127         ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1128         float dist2 = ImLengthSqr(p - p_line);
1129         if (dist2 < p_closest_dist2)
1130         {
1131             p_closest = p_line;
1132             p_closest_dist2 = dist2;
1133         }
1134         p_last = p_current;
1135     }
1136     return p_closest;
1137 }
1138 
1139 // Closely mimics PathBezierToCasteljau() in imgui_draw.cpp
BezierClosestPointCasteljauStep(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)1140 static void BezierClosestPointCasteljauStep(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)
1141 {
1142     float dx = x4 - x1;
1143     float dy = y4 - y1;
1144     float d2 = ((x2 - x4) * dy - (y2 - y4) * dx);
1145     float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
1146     d2 = (d2 >= 0) ? d2 : -d2;
1147     d3 = (d3 >= 0) ? d3 : -d3;
1148     if ((d2+d3) * (d2+d3) < tess_tol * (dx*dx + dy*dy))
1149     {
1150         ImVec2 p_current(x4, y4);
1151         ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1152         float dist2 = ImLengthSqr(p - p_line);
1153         if (dist2 < p_closest_dist2)
1154         {
1155             p_closest = p_line;
1156             p_closest_dist2 = dist2;
1157         }
1158         p_last = p_current;
1159     }
1160     else if (level < 10)
1161     {
1162         float x12 = (x1+x2)*0.5f,       y12 = (y1+y2)*0.5f;
1163         float x23 = (x2+x3)*0.5f,       y23 = (y2+y3)*0.5f;
1164         float x34 = (x3+x4)*0.5f,       y34 = (y3+y4)*0.5f;
1165         float x123 = (x12+x23)*0.5f,    y123 = (y12+y23)*0.5f;
1166         float x234 = (x23+x34)*0.5f,    y234 = (y23+y34)*0.5f;
1167         float x1234 = (x123+x234)*0.5f, y1234 = (y123+y234)*0.5f;
1168         BezierClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);
1169         BezierClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);
1170     }
1171 }
1172 
1173 // tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol
1174 // Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically.
ImBezierClosestPointCasteljau(const ImVec2 & p1,const ImVec2 & p2,const ImVec2 & p3,const ImVec2 & p4,const ImVec2 & p,float tess_tol)1175 ImVec2 ImBezierClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol)
1176 {
1177     IM_ASSERT(tess_tol > 0.0f);
1178     ImVec2 p_last = p1;
1179     ImVec2 p_closest;
1180     float p_closest_dist2 = FLT_MAX;
1181     BezierClosestPointCasteljauStep(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);
1182     return p_closest;
1183 }
1184 
ImLineClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & p)1185 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1186 {
1187     ImVec2 ap = p - a;
1188     ImVec2 ab_dir = b - a;
1189     float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1190     if (dot < 0.0f)
1191         return a;
1192     float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1193     if (dot > ab_len_sqr)
1194         return b;
1195     return a + ab_dir * dot / ab_len_sqr;
1196 }
1197 
ImTriangleContainsPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1198 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1199 {
1200     bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1201     bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1202     bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1203     return ((b1 == b2) && (b2 == b3));
1204 }
1205 
ImTriangleBarycentricCoords(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p,float & out_u,float & out_v,float & out_w)1206 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1207 {
1208     ImVec2 v0 = b - a;
1209     ImVec2 v1 = c - a;
1210     ImVec2 v2 = p - a;
1211     const float denom = v0.x * v1.y - v1.x * v0.y;
1212     out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1213     out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1214     out_u = 1.0f - out_v - out_w;
1215 }
1216 
ImTriangleClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1217 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1218 {
1219     ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1220     ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1221     ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1222     float dist2_ab = ImLengthSqr(p - proj_ab);
1223     float dist2_bc = ImLengthSqr(p - proj_bc);
1224     float dist2_ca = ImLengthSqr(p - proj_ca);
1225     float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1226     if (m == dist2_ab)
1227         return proj_ab;
1228     if (m == dist2_bc)
1229         return proj_bc;
1230     return proj_ca;
1231 }
1232 
1233 //-----------------------------------------------------------------------------
1234 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
1235 //-----------------------------------------------------------------------------
1236 
1237 // 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)1238 int ImStricmp(const char* str1, const char* str2)
1239 {
1240     int d;
1241     while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
1242     return d;
1243 }
1244 
ImStrnicmp(const char * str1,const char * str2,size_t count)1245 int ImStrnicmp(const char* str1, const char* str2, size_t count)
1246 {
1247     int d = 0;
1248     while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
1249     return d;
1250 }
1251 
ImStrncpy(char * dst,const char * src,size_t count)1252 void ImStrncpy(char* dst, const char* src, size_t count)
1253 {
1254     if (count < 1)
1255         return;
1256     if (count > 1)
1257         strncpy(dst, src, count - 1);
1258     dst[count - 1] = 0;
1259 }
1260 
ImStrdup(const char * str)1261 char* ImStrdup(const char* str)
1262 {
1263     size_t len = strlen(str);
1264     void* buf = IM_ALLOC(len + 1);
1265     return (char*)memcpy(buf, (const void*)str, len + 1);
1266 }
1267 
ImStrdupcpy(char * dst,size_t * p_dst_size,const char * src)1268 char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
1269 {
1270     size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1;
1271     size_t src_size = strlen(src) + 1;
1272     if (dst_buf_size < src_size)
1273     {
1274         IM_FREE(dst);
1275         dst = (char*)IM_ALLOC(src_size);
1276         if (p_dst_size)
1277             *p_dst_size = src_size;
1278     }
1279     return (char*)memcpy(dst, (const void*)src, src_size);
1280 }
1281 
ImStrchrRange(const char * str,const char * str_end,char c)1282 const char* ImStrchrRange(const char* str, const char* str_end, char c)
1283 {
1284     const char* p = (const char*)memchr(str, (int)c, str_end - str);
1285     return p;
1286 }
1287 
ImStrlenW(const ImWchar * str)1288 int ImStrlenW(const ImWchar* str)
1289 {
1290     //return (int)wcslen((const wchar_t*)str);  // FIXME-OPT: Could use this when wchar_t are 16-bit
1291     int n = 0;
1292     while (*str++) n++;
1293     return n;
1294 }
1295 
1296 // Find end-of-line. Return pointer will point to either first \n, either str_end.
ImStreolRange(const char * str,const char * str_end)1297 const char* ImStreolRange(const char* str, const char* str_end)
1298 {
1299     const char* p = (const char*)memchr(str, '\n', str_end - str);
1300     return p ? p : str_end;
1301 }
1302 
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)1303 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
1304 {
1305     while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1306         buf_mid_line--;
1307     return buf_mid_line;
1308 }
1309 
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)1310 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1311 {
1312     if (!needle_end)
1313         needle_end = needle + strlen(needle);
1314 
1315     const char un0 = (char)toupper(*needle);
1316     while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1317     {
1318         if (toupper(*haystack) == un0)
1319         {
1320             const char* b = needle + 1;
1321             for (const char* a = haystack + 1; b < needle_end; a++, b++)
1322                 if (toupper(*a) != toupper(*b))
1323                     break;
1324             if (b == needle_end)
1325                 return haystack;
1326         }
1327         haystack++;
1328     }
1329     return NULL;
1330 }
1331 
1332 // 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)1333 void ImStrTrimBlanks(char* buf)
1334 {
1335     char* p = buf;
1336     while (p[0] == ' ' || p[0] == '\t')     // Leading blanks
1337         p++;
1338     char* p_start = p;
1339     while (*p != 0)                         // Find end of string
1340         p++;
1341     while (p > p_start && (p[-1] == ' ' || p[-1] == '\t'))  // Trailing blanks
1342         p--;
1343     if (p_start != buf)                     // Copy memory if we had leading blanks
1344         memmove(buf, p_start, p - p_start);
1345     buf[p - p_start] = 0;                   // Zero terminate
1346 }
1347 
ImStrSkipBlank(const char * str)1348 const char* ImStrSkipBlank(const char* str)
1349 {
1350     while (str[0] == ' ' || str[0] == '\t')
1351         str++;
1352     return str;
1353 }
1354 
1355 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1356 // 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.
1357 // B) When buf==NULL vsnprintf() will return the output size.
1358 #ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1359 
1360 // We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h)
1361 // You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1362 // and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are
1363 // designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.)
1364 #ifdef IMGUI_USE_STB_SPRINTF
1365 #define STB_SPRINTF_IMPLEMENTATION
1366 #include "stb_sprintf.h"
1367 #endif
1368 
1369 #if defined(_MSC_VER) && !defined(vsnprintf)
1370 #define vsnprintf _vsnprintf
1371 #endif
1372 
ImFormatString(char * buf,size_t buf_size,const char * fmt,...)1373 int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1374 {
1375     va_list args;
1376     va_start(args, fmt);
1377 #ifdef IMGUI_USE_STB_SPRINTF
1378     int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1379 #else
1380     int w = vsnprintf(buf, buf_size, fmt, args);
1381 #endif
1382     va_end(args);
1383     if (buf == NULL)
1384         return w;
1385     if (w == -1 || w >= (int)buf_size)
1386         w = (int)buf_size - 1;
1387     buf[w] = 0;
1388     return w;
1389 }
1390 
ImFormatStringV(char * buf,size_t buf_size,const char * fmt,va_list args)1391 int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1392 {
1393 #ifdef IMGUI_USE_STB_SPRINTF
1394     int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1395 #else
1396     int w = vsnprintf(buf, buf_size, fmt, args);
1397 #endif
1398     if (buf == NULL)
1399         return w;
1400     if (w == -1 || w >= (int)buf_size)
1401         w = (int)buf_size - 1;
1402     buf[w] = 0;
1403     return w;
1404 }
1405 #endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1406 
1407 // CRC32 needs a 1KB lookup table (not cache friendly)
1408 // Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
1409 // - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
1410 static const ImU32 GCrc32LookupTable[256] =
1411 {
1412     0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
1413     0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
1414     0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
1415     0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
1416     0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
1417     0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
1418     0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
1419     0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
1420     0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
1421     0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
1422     0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
1423     0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
1424     0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
1425     0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
1426     0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
1427     0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
1428 };
1429 
1430 // Known size hash
1431 // It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
1432 // 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)1433 ImU32 ImHashData(const void* data_p, size_t data_size, ImU32 seed)
1434 {
1435     ImU32 crc = ~seed;
1436     const unsigned char* data = (const unsigned char*)data_p;
1437     const ImU32* crc32_lut = GCrc32LookupTable;
1438     while (data_size-- != 0)
1439         crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
1440     return ~crc;
1441 }
1442 
1443 // Zero-terminated string hash, with support for ### to reset back to seed value
1444 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1445 // Because this syntax is rarely used we are optimizing for the common case.
1446 // - If we reach ### in the string we discard the hash so far and reset to the seed.
1447 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
1448 // 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)1449 ImU32 ImHashStr(const char* data_p, size_t data_size, ImU32 seed)
1450 {
1451     seed = ~seed;
1452     ImU32 crc = seed;
1453     const unsigned char* data = (const unsigned char*)data_p;
1454     const ImU32* crc32_lut = GCrc32LookupTable;
1455     if (data_size != 0)
1456     {
1457         while (data_size-- != 0)
1458         {
1459             unsigned char c = *data++;
1460             if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')
1461                 crc = seed;
1462             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1463         }
1464     }
1465     else
1466     {
1467         while (unsigned char c = *data++)
1468         {
1469             if (c == '#' && data[0] == '#' && data[1] == '#')
1470                 crc = seed;
1471             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1472         }
1473     }
1474     return ~crc;
1475 }
1476 
1477 //-----------------------------------------------------------------------------
1478 // [SECTION] MISC HELPERS/UTILITIES (File functions)
1479 //-----------------------------------------------------------------------------
1480 
1481 // Default file functions
1482 #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
ImFileOpen(const char * filename,const char * mode)1483 ImFileHandle ImFileOpen(const char* filename, const char* mode)
1484 {
1485 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__)
1486     // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.
1487     const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1;
1488     const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1;
1489     ImVector<ImWchar> buf;
1490     buf.resize(filename_wsize + mode_wsize);
1491     ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL);
1492     ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL);
1493     return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]);
1494 #else
1495     return fopen(filename, mode);
1496 #endif
1497 }
1498 
1499 // 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)1500 bool    ImFileClose(ImFileHandle f)     { return fclose(f) == 0; }
ImFileGetSize(ImFileHandle f)1501 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)1502 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)1503 ImU64   ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f)    { return fwrite(data, (size_t)sz, (size_t)count, f); }
1504 #endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
1505 
1506 // Helper: Load file content into memory
1507 // Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree()
ImFileLoadToMemory(const char * filename,const char * mode,size_t * out_file_size,int padding_bytes)1508 void*   ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes)
1509 {
1510     IM_ASSERT(filename && mode);
1511     if (out_file_size)
1512         *out_file_size = 0;
1513 
1514     ImFileHandle f;
1515     if ((f = ImFileOpen(filename, mode)) == NULL)
1516         return NULL;
1517 
1518     size_t file_size = (size_t)ImFileGetSize(f);
1519     if (file_size == (size_t)-1)
1520     {
1521         ImFileClose(f);
1522         return NULL;
1523     }
1524 
1525     void* file_data = IM_ALLOC(file_size + padding_bytes);
1526     if (file_data == NULL)
1527     {
1528         ImFileClose(f);
1529         return NULL;
1530     }
1531     if (ImFileRead(file_data, 1, file_size, f) != file_size)
1532     {
1533         ImFileClose(f);
1534         IM_FREE(file_data);
1535         return NULL;
1536     }
1537     if (padding_bytes > 0)
1538         memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
1539 
1540     ImFileClose(f);
1541     if (out_file_size)
1542         *out_file_size = file_size;
1543 
1544     return file_data;
1545 }
1546 
1547 //-----------------------------------------------------------------------------
1548 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
1549 //-----------------------------------------------------------------------------
1550 
1551 // Convert UTF-8 to 32-bit character, process single character input.
1552 // Based on stb_from_utf8() from github.com/nothings/stb/
1553 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)1554 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1555 {
1556     unsigned int c = (unsigned int)-1;
1557     const unsigned char* str = (const unsigned char*)in_text;
1558     if (!(*str & 0x80))
1559     {
1560         c = (unsigned int)(*str++);
1561         *out_char = c;
1562         return 1;
1563     }
1564     if ((*str & 0xe0) == 0xc0)
1565     {
1566         *out_char = IM_UNICODE_CODEPOINT_INVALID; // will be invalid but not end of string
1567         if (in_text_end && in_text_end - (const char*)str < 2) return 1;
1568         if (*str < 0xc2) return 2;
1569         c = (unsigned int)((*str++ & 0x1f) << 6);
1570         if ((*str & 0xc0) != 0x80) return 2;
1571         c += (*str++ & 0x3f);
1572         *out_char = c;
1573         return 2;
1574     }
1575     if ((*str & 0xf0) == 0xe0)
1576     {
1577         *out_char = IM_UNICODE_CODEPOINT_INVALID; // will be invalid but not end of string
1578         if (in_text_end && in_text_end - (const char*)str < 3) return 1;
1579         if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3;
1580         if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below
1581         c = (unsigned int)((*str++ & 0x0f) << 12);
1582         if ((*str & 0xc0) != 0x80) return 3;
1583         c += (unsigned int)((*str++ & 0x3f) << 6);
1584         if ((*str & 0xc0) != 0x80) return 3;
1585         c += (*str++ & 0x3f);
1586         *out_char = c;
1587         return 3;
1588     }
1589     if ((*str & 0xf8) == 0xf0)
1590     {
1591         *out_char = IM_UNICODE_CODEPOINT_INVALID; // will be invalid but not end of string
1592         if (in_text_end && in_text_end - (const char*)str < 4) return 1;
1593         if (*str > 0xf4) return 4;
1594         if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4;
1595         if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below
1596         c = (unsigned int)((*str++ & 0x07) << 18);
1597         if ((*str & 0xc0) != 0x80) return 4;
1598         c += (unsigned int)((*str++ & 0x3f) << 12);
1599         if ((*str & 0xc0) != 0x80) return 4;
1600         c += (unsigned int)((*str++ & 0x3f) << 6);
1601         if ((*str & 0xc0) != 0x80) return 4;
1602         c += (*str++ & 0x3f);
1603         // utf-8 encodings of values used in surrogate pairs are invalid
1604         if ((c & 0xFFFFF800) == 0xD800) return 4;
1605         *out_char = c;
1606         return 4;
1607     }
1608     *out_char = 0;
1609     return 0;
1610 }
1611 
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1612 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1613 {
1614     ImWchar* buf_out = buf;
1615     ImWchar* buf_end = buf + buf_size;
1616     while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1617     {
1618         unsigned int c;
1619         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1620         if (c == 0)
1621             break;
1622         if (c <= IM_UNICODE_CODEPOINT_MAX)    // FIXME: Losing characters that don't fit in 2 bytes
1623             *buf_out++ = (ImWchar)c;
1624     }
1625     *buf_out = 0;
1626     if (in_text_remaining)
1627         *in_text_remaining = in_text;
1628     return (int)(buf_out - buf);
1629 }
1630 
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1631 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1632 {
1633     int char_count = 0;
1634     while ((!in_text_end || in_text < in_text_end) && *in_text)
1635     {
1636         unsigned int c;
1637         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1638         if (c == 0)
1639             break;
1640         if (c <= IM_UNICODE_CODEPOINT_MAX)
1641             char_count++;
1642     }
1643     return char_count;
1644 }
1645 
1646 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8(char * buf,int buf_size,unsigned int c)1647 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1648 {
1649     if (c < 0x80)
1650     {
1651         buf[0] = (char)c;
1652         return 1;
1653     }
1654     if (c < 0x800)
1655     {
1656         if (buf_size < 2) return 0;
1657         buf[0] = (char)(0xc0 + (c >> 6));
1658         buf[1] = (char)(0x80 + (c & 0x3f));
1659         return 2;
1660     }
1661     if (c >= 0xdc00 && c < 0xe000)
1662     {
1663         return 0;
1664     }
1665     if (c >= 0xd800 && c < 0xdc00)
1666     {
1667         if (buf_size < 4) return 0;
1668         buf[0] = (char)(0xf0 + (c >> 18));
1669         buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1670         buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1671         buf[3] = (char)(0x80 + ((c ) & 0x3f));
1672         return 4;
1673     }
1674     //else if (c < 0x10000)
1675     {
1676         if (buf_size < 3) return 0;
1677         buf[0] = (char)(0xe0 + (c >> 12));
1678         buf[1] = (char)(0x80 + ((c>> 6) & 0x3f));
1679         buf[2] = (char)(0x80 + ((c ) & 0x3f));
1680         return 3;
1681     }
1682 }
1683 
1684 // Not optimal but we very rarely use this function.
ImTextCountUtf8BytesFromChar(const char * in_text,const char * in_text_end)1685 int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
1686 {
1687     unsigned int dummy = 0;
1688     return ImTextCharFromUtf8(&dummy, in_text, in_text_end);
1689 }
1690 
ImTextCountUtf8BytesFromChar(unsigned int c)1691 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1692 {
1693     if (c < 0x80) return 1;
1694     if (c < 0x800) return 2;
1695     if (c >= 0xdc00 && c < 0xe000) return 0;
1696     if (c >= 0xd800 && c < 0xdc00) return 4;
1697     return 3;
1698 }
1699 
ImTextStrToUtf8(char * buf,int buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1700 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1701 {
1702     char* buf_out = buf;
1703     const char* buf_end = buf + buf_size;
1704     while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1705     {
1706         unsigned int c = (unsigned int)(*in_text++);
1707         if (c < 0x80)
1708             *buf_out++ = (char)c;
1709         else
1710             buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c);
1711     }
1712     *buf_out = 0;
1713     return (int)(buf_out - buf);
1714 }
1715 
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1716 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1717 {
1718     int bytes_count = 0;
1719     while ((!in_text_end || in_text < in_text_end) && *in_text)
1720     {
1721         unsigned int c = (unsigned int)(*in_text++);
1722         if (c < 0x80)
1723             bytes_count++;
1724         else
1725             bytes_count += ImTextCountUtf8BytesFromChar(c);
1726     }
1727     return bytes_count;
1728 }
1729 
1730 //-----------------------------------------------------------------------------
1731 // [SECTION] MISC HELPERS/UTILTIES (Color functions)
1732 // Note: The Convert functions are early design which are not consistent with other API.
1733 //-----------------------------------------------------------------------------
1734 
ColorConvertU32ToFloat4(ImU32 in)1735 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1736 {
1737     float s = 1.0f/255.0f;
1738     return ImVec4(
1739         ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1740         ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1741         ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1742         ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1743 }
1744 
ColorConvertFloat4ToU32(const ImVec4 & in)1745 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1746 {
1747     ImU32 out;
1748     out  = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1749     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1750     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1751     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1752     return out;
1753 }
1754 
1755 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1756 // 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)1757 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1758 {
1759     float K = 0.f;
1760     if (g < b)
1761     {
1762         ImSwap(g, b);
1763         K = -1.f;
1764     }
1765     if (r < g)
1766     {
1767         ImSwap(r, g);
1768         K = -2.f / 6.f - K;
1769     }
1770 
1771     const float chroma = r - (g < b ? g : b);
1772     out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
1773     out_s = chroma / (r + 1e-20f);
1774     out_v = r;
1775 }
1776 
1777 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1778 // 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)1779 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1780 {
1781     if (s == 0.0f)
1782     {
1783         // gray
1784         out_r = out_g = out_b = v;
1785         return;
1786     }
1787 
1788     h = ImFmod(h, 1.0f) / (60.0f/360.0f);
1789     int   i = (int)h;
1790     float f = h - (float)i;
1791     float p = v * (1.0f - s);
1792     float q = v * (1.0f - s * f);
1793     float t = v * (1.0f - s * (1.0f - f));
1794 
1795     switch (i)
1796     {
1797     case 0: out_r = v; out_g = t; out_b = p; break;
1798     case 1: out_r = q; out_g = v; out_b = p; break;
1799     case 2: out_r = p; out_g = v; out_b = t; break;
1800     case 3: out_r = p; out_g = q; out_b = v; break;
1801     case 4: out_r = t; out_g = p; out_b = v; break;
1802     case 5: default: out_r = v; out_g = p; out_b = q; break;
1803     }
1804 }
1805 
1806 //-----------------------------------------------------------------------------
1807 // [SECTION] ImGuiStorage
1808 // Helper: Key->value storage
1809 //-----------------------------------------------------------------------------
1810 
1811 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair> & data,ImGuiID key)1812 static ImGuiStorage::ImGuiStoragePair* LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair>& data, ImGuiID key)
1813 {
1814     ImGuiStorage::ImGuiStoragePair* first = data.Data;
1815     ImGuiStorage::ImGuiStoragePair* last = data.Data + data.Size;
1816     size_t count = (size_t)(last - first);
1817     while (count > 0)
1818     {
1819         size_t count2 = count >> 1;
1820         ImGuiStorage::ImGuiStoragePair* mid = first + count2;
1821         if (mid->key < key)
1822         {
1823             first = ++mid;
1824             count -= count2 + 1;
1825         }
1826         else
1827         {
1828             count = count2;
1829         }
1830     }
1831     return first;
1832 }
1833 
1834 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
BuildSortByKey()1835 void ImGuiStorage::BuildSortByKey()
1836 {
1837     struct StaticFunc
1838     {
1839         static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)
1840         {
1841             // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1842             if (((const ImGuiStoragePair*)lhs)->key > ((const ImGuiStoragePair*)rhs)->key) return +1;
1843             if (((const ImGuiStoragePair*)lhs)->key < ((const ImGuiStoragePair*)rhs)->key) return -1;
1844             return 0;
1845         }
1846     };
1847     if (Data.Size > 1)
1848         ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairCompareByID);
1849 }
1850 
GetInt(ImGuiID key,int default_val) const1851 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1852 {
1853     ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1854     if (it == Data.end() || it->key != key)
1855         return default_val;
1856     return it->val_i;
1857 }
1858 
GetBool(ImGuiID key,bool default_val) const1859 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1860 {
1861     return GetInt(key, default_val ? 1 : 0) != 0;
1862 }
1863 
GetFloat(ImGuiID key,float default_val) const1864 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1865 {
1866     ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1867     if (it == Data.end() || it->key != key)
1868         return default_val;
1869     return it->val_f;
1870 }
1871 
GetVoidPtr(ImGuiID key) const1872 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1873 {
1874     ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1875     if (it == Data.end() || it->key != key)
1876         return NULL;
1877     return it->val_p;
1878 }
1879 
1880 // 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)1881 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1882 {
1883     ImGuiStoragePair* it = LowerBound(Data, key);
1884     if (it == Data.end() || it->key != key)
1885         it = Data.insert(it, ImGuiStoragePair(key, default_val));
1886     return &it->val_i;
1887 }
1888 
GetBoolRef(ImGuiID key,bool default_val)1889 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1890 {
1891     return (bool*)GetIntRef(key, default_val ? 1 : 0);
1892 }
1893 
GetFloatRef(ImGuiID key,float default_val)1894 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1895 {
1896     ImGuiStoragePair* it = LowerBound(Data, key);
1897     if (it == Data.end() || it->key != key)
1898         it = Data.insert(it, ImGuiStoragePair(key, default_val));
1899     return &it->val_f;
1900 }
1901 
GetVoidPtrRef(ImGuiID key,void * default_val)1902 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1903 {
1904     ImGuiStoragePair* it = LowerBound(Data, key);
1905     if (it == Data.end() || it->key != key)
1906         it = Data.insert(it, ImGuiStoragePair(key, default_val));
1907     return &it->val_p;
1908 }
1909 
1910 // 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)1911 void ImGuiStorage::SetInt(ImGuiID key, int val)
1912 {
1913     ImGuiStoragePair* it = LowerBound(Data, key);
1914     if (it == Data.end() || it->key != key)
1915     {
1916         Data.insert(it, ImGuiStoragePair(key, val));
1917         return;
1918     }
1919     it->val_i = val;
1920 }
1921 
SetBool(ImGuiID key,bool val)1922 void ImGuiStorage::SetBool(ImGuiID key, bool val)
1923 {
1924     SetInt(key, val ? 1 : 0);
1925 }
1926 
SetFloat(ImGuiID key,float val)1927 void ImGuiStorage::SetFloat(ImGuiID key, float 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_f = val;
1936 }
1937 
SetVoidPtr(ImGuiID key,void * val)1938 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1939 {
1940     ImGuiStoragePair* it = LowerBound(Data, key);
1941     if (it == Data.end() || it->key != key)
1942     {
1943         Data.insert(it, ImGuiStoragePair(key, val));
1944         return;
1945     }
1946     it->val_p = val;
1947 }
1948 
SetAllInt(int v)1949 void ImGuiStorage::SetAllInt(int v)
1950 {
1951     for (int i = 0; i < Data.Size; i++)
1952         Data[i].val_i = v;
1953 }
1954 
1955 //-----------------------------------------------------------------------------
1956 // [SECTION] ImGuiTextFilter
1957 //-----------------------------------------------------------------------------
1958 
1959 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)1960 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
1961 {
1962     if (default_filter)
1963     {
1964         ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
1965         Build();
1966     }
1967     else
1968     {
1969         InputBuf[0] = 0;
1970         CountGrep = 0;
1971     }
1972 }
1973 
Draw(const char * label,float width)1974 bool ImGuiTextFilter::Draw(const char* label, float width)
1975 {
1976     if (width != 0.0f)
1977         ImGui::SetNextItemWidth(width);
1978     bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
1979     if (value_changed)
1980         Build();
1981     return value_changed;
1982 }
1983 
split(char separator,ImVector<ImGuiTextRange> * out) const1984 void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const
1985 {
1986     out->resize(0);
1987     const char* wb = b;
1988     const char* we = wb;
1989     while (we < e)
1990     {
1991         if (*we == separator)
1992         {
1993             out->push_back(ImGuiTextRange(wb, we));
1994             wb = we + 1;
1995         }
1996         we++;
1997     }
1998     if (wb != we)
1999         out->push_back(ImGuiTextRange(wb, we));
2000 }
2001 
Build()2002 void ImGuiTextFilter::Build()
2003 {
2004     Filters.resize(0);
2005     ImGuiTextRange input_range(InputBuf, InputBuf+strlen(InputBuf));
2006     input_range.split(',', &Filters);
2007 
2008     CountGrep = 0;
2009     for (int i = 0; i != Filters.Size; i++)
2010     {
2011         ImGuiTextRange& f = Filters[i];
2012         while (f.b < f.e && ImCharIsBlankA(f.b[0]))
2013             f.b++;
2014         while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
2015             f.e--;
2016         if (f.empty())
2017             continue;
2018         if (Filters[i].b[0] != '-')
2019             CountGrep += 1;
2020     }
2021 }
2022 
PassFilter(const char * text,const char * text_end) const2023 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
2024 {
2025     if (Filters.empty())
2026         return true;
2027 
2028     if (text == NULL)
2029         text = "";
2030 
2031     for (int i = 0; i != Filters.Size; i++)
2032     {
2033         const ImGuiTextRange& f = Filters[i];
2034         if (f.empty())
2035             continue;
2036         if (f.b[0] == '-')
2037         {
2038             // Subtract
2039             if (ImStristr(text, text_end, f.b + 1, f.e) != NULL)
2040                 return false;
2041         }
2042         else
2043         {
2044             // Grep
2045             if (ImStristr(text, text_end, f.b, f.e) != NULL)
2046                 return true;
2047         }
2048     }
2049 
2050     // Implicit * grep
2051     if (CountGrep == 0)
2052         return true;
2053 
2054     return false;
2055 }
2056 
2057 //-----------------------------------------------------------------------------
2058 // [SECTION] ImGuiTextBuffer
2059 //-----------------------------------------------------------------------------
2060 
2061 // On some platform vsnprintf() takes va_list by reference and modifies it.
2062 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
2063 #ifndef va_copy
2064 #if defined(__GNUC__) || defined(__clang__)
2065 #define va_copy(dest, src) __builtin_va_copy(dest, src)
2066 #else
2067 #define va_copy(dest, src) (dest = src)
2068 #endif
2069 #endif
2070 
2071 char ImGuiTextBuffer::EmptyString[1] = { 0 };
2072 
append(const char * str,const char * str_end)2073 void ImGuiTextBuffer::append(const char* str, const char* str_end)
2074 {
2075     int len = str_end ? (int)(str_end - str) : (int)strlen(str);
2076 
2077     // Add zero-terminator the first time
2078     const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2079     const int needed_sz = write_off + len;
2080     if (write_off + len >= Buf.Capacity)
2081     {
2082         int new_capacity = Buf.Capacity * 2;
2083         Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2084     }
2085 
2086     Buf.resize(needed_sz);
2087     memcpy(&Buf[write_off - 1], str, (size_t)len);
2088     Buf[write_off - 1 + len] = 0;
2089 }
2090 
appendf(const char * fmt,...)2091 void ImGuiTextBuffer::appendf(const char* fmt, ...)
2092 {
2093     va_list args;
2094     va_start(args, fmt);
2095     appendfv(fmt, args);
2096     va_end(args);
2097 }
2098 
2099 // Helper: Text buffer for logging/accumulating text
appendfv(const char * fmt,va_list args)2100 void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
2101 {
2102     va_list args_copy;
2103     va_copy(args_copy, args);
2104 
2105     int len = ImFormatStringV(NULL, 0, fmt, args);         // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
2106     if (len <= 0)
2107     {
2108         va_end(args_copy);
2109         return;
2110     }
2111 
2112     // Add zero-terminator the first time
2113     const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2114     const int needed_sz = write_off + len;
2115     if (write_off + len >= Buf.Capacity)
2116     {
2117         int new_capacity = Buf.Capacity * 2;
2118         Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2119     }
2120 
2121     Buf.resize(needed_sz);
2122     ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
2123     va_end(args_copy);
2124 }
2125 
2126 //-----------------------------------------------------------------------------
2127 // [SECTION] ImGuiListClipper
2128 // This is currently not as flexible/powerful as it should be and really confusing/spaghetti, mostly because we changed
2129 // the API mid-way through development and support two ways to using the clipper, needs some rework (see TODO)
2130 //-----------------------------------------------------------------------------
2131 
2132 // Helper to calculate coarse clipping of large list of evenly sized items.
2133 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
2134 // 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)2135 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
2136 {
2137     ImGuiContext& g = *GImGui;
2138     ImGuiWindow* window = g.CurrentWindow;
2139     if (g.LogEnabled)
2140     {
2141         // If logging is active, do not perform any clipping
2142         *out_items_display_start = 0;
2143         *out_items_display_end = items_count;
2144         return;
2145     }
2146     if (window->SkipItems)
2147     {
2148         *out_items_display_start = *out_items_display_end = 0;
2149         return;
2150     }
2151 
2152     // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect
2153     ImRect unclipped_rect = window->ClipRect;
2154     if (g.NavMoveRequest)
2155         unclipped_rect.Add(g.NavScoringRectScreen);
2156 
2157     const ImVec2 pos = window->DC.CursorPos;
2158     int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
2159     int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
2160 
2161     // When performing a navigation request, ensure we have one item extra in the direction we are moving to
2162     if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up)
2163         start--;
2164     if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down)
2165         end++;
2166 
2167     start = ImClamp(start, 0, items_count);
2168     end = ImClamp(end + 1, start, items_count);
2169     *out_items_display_start = start;
2170     *out_items_display_end = end;
2171 }
2172 
SetCursorPosYAndSetupDummyPrevLine(float pos_y,float line_height)2173 static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height)
2174 {
2175     // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
2176     // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
2177     // The clipper should probably have a 4th step to display the last item in a regular manner.
2178     ImGuiContext& g = *GImGui;
2179     ImGuiWindow* window = g.CurrentWindow;
2180     window->DC.CursorPos.y = pos_y;
2181     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y);
2182     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.
2183     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.
2184     if (ImGuiColumns* columns = window->DC.CurrentColumns)
2185         columns->LineMinY = window->DC.CursorPos.y;                         // Setting this so that cell Y position are set properly
2186 }
2187 
2188 // Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
2189 // Use case B: Begin() called from constructor with items_height>0
2190 // FIXME-LEGACY: Ideally we should remove the Begin/End functions but they are part of the legacy API we still support. This is why some of the code in Step() calling Begin() and reassign some fields, spaghetti style.
Begin(int count,float items_height)2191 void ImGuiListClipper::Begin(int count, float items_height)
2192 {
2193     ImGuiContext& g = *GImGui;
2194     ImGuiWindow* window = g.CurrentWindow;
2195 
2196     StartPosY = window->DC.CursorPos.y;
2197     ItemsHeight = items_height;
2198     ItemsCount = count;
2199     StepNo = 0;
2200     DisplayEnd = DisplayStart = -1;
2201     if (ItemsHeight > 0.0f)
2202     {
2203         ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display
2204         if (DisplayStart > 0)
2205             SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor
2206         StepNo = 2;
2207     }
2208 }
2209 
End()2210 void ImGuiListClipper::End()
2211 {
2212     if (ItemsCount < 0)
2213         return;
2214     // 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.
2215     if (ItemsCount < INT_MAX)
2216         SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor
2217     ItemsCount = -1;
2218     StepNo = 3;
2219 }
2220 
Step()2221 bool ImGuiListClipper::Step()
2222 {
2223     ImGuiContext& g = *GImGui;
2224     ImGuiWindow* window = g.CurrentWindow;
2225 
2226     if (ItemsCount == 0 || window->SkipItems)
2227     {
2228         ItemsCount = -1;
2229         return false;
2230     }
2231     if (StepNo == 0) // Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height.
2232     {
2233         DisplayStart = 0;
2234         DisplayEnd = 1;
2235         StartPosY = window->DC.CursorPos.y;
2236         StepNo = 1;
2237         return true;
2238     }
2239     if (StepNo == 1) // Step 1: the clipper infer height from first element, calculate the actual range of elements to display, and position the cursor before the first element.
2240     {
2241         if (ItemsCount == 1) { ItemsCount = -1; return false; }
2242         float items_height = window->DC.CursorPos.y - StartPosY;
2243         IM_ASSERT(items_height > 0.0f);   // If this triggers, it means Item 0 hasn't moved the cursor vertically
2244         Begin(ItemsCount - 1, items_height);
2245         DisplayStart++;
2246         DisplayEnd++;
2247         StepNo = 3;
2248         return true;
2249     }
2250     if (StepNo == 2) // Step 2: dummy step only required if an explicit items_height was passed to constructor or Begin() and user still call Step(). Does nothing and switch to Step 3.
2251     {
2252         IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0);
2253         StepNo = 3;
2254         return true;
2255     }
2256     if (StepNo == 3) // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), advance the cursor to the end of the list and then returns 'false' to end the loop.
2257         End();
2258     return false;
2259 }
2260 
2261 //-----------------------------------------------------------------------------
2262 // [SECTION] RENDER HELPERS
2263 // Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change.
2264 // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: state.
2265 //-----------------------------------------------------------------------------
2266 
GetColorU32(ImGuiCol idx,float alpha_mul)2267 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
2268 {
2269     ImGuiStyle& style = GImGui->Style;
2270     ImVec4 c = style.Colors[idx];
2271     c.w *= style.Alpha * alpha_mul;
2272     return ColorConvertFloat4ToU32(c);
2273 }
2274 
GetColorU32(const ImVec4 & col)2275 ImU32 ImGui::GetColorU32(const ImVec4& col)
2276 {
2277     ImGuiStyle& style = GImGui->Style;
2278     ImVec4 c = col;
2279     c.w *= style.Alpha;
2280     return ColorConvertFloat4ToU32(c);
2281 }
2282 
GetStyleColorVec4(ImGuiCol idx)2283 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
2284 {
2285     ImGuiStyle& style = GImGui->Style;
2286     return style.Colors[idx];
2287 }
2288 
GetColorU32(ImU32 col)2289 ImU32 ImGui::GetColorU32(ImU32 col)
2290 {
2291     ImGuiStyle& style = GImGui->Style;
2292     if (style.Alpha >= 1.0f)
2293         return col;
2294     ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
2295     a = (ImU32)(a * style.Alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
2296     return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
2297 }
2298 
FindRenderedTextEnd(const char * text,const char * text_end)2299 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2300 {
2301     const char* text_display_end = text;
2302     if (!text_end)
2303         text_end = (const char*)-1;
2304 
2305     while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2306         text_display_end++;
2307     return text_display_end;
2308 }
2309 
2310 // Internal ImGui functions to render text
2311 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)2312 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2313 {
2314     ImGuiContext& g = *GImGui;
2315     ImGuiWindow* window = g.CurrentWindow;
2316 
2317     // Hide anything after a '##' string
2318     const char* text_display_end;
2319     if (hide_text_after_hash)
2320     {
2321         text_display_end = FindRenderedTextEnd(text, text_end);
2322     }
2323     else
2324     {
2325         if (!text_end)
2326             text_end = text + strlen(text); // FIXME-OPT
2327         text_display_end = text_end;
2328     }
2329 
2330     if (text != text_display_end)
2331     {
2332         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2333         if (g.LogEnabled)
2334             LogRenderedText(&pos, text, text_display_end);
2335     }
2336 }
2337 
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)2338 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2339 {
2340     ImGuiContext& g = *GImGui;
2341     ImGuiWindow* window = g.CurrentWindow;
2342 
2343     if (!text_end)
2344         text_end = text + strlen(text); // FIXME-OPT
2345 
2346     if (text != text_end)
2347     {
2348         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2349         if (g.LogEnabled)
2350             LogRenderedText(&pos, text, text_end);
2351     }
2352 }
2353 
2354 // Default clip_rect uses (pos_min,pos_max)
2355 // 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)2356 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)
2357 {
2358     // Perform CPU side clipping for single clipped element to avoid using scissor state
2359     ImVec2 pos = pos_min;
2360     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
2361 
2362     const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
2363     const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
2364     bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2365     if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
2366         need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2367 
2368     // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
2369     if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
2370     if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
2371 
2372     // Render
2373     if (need_clipping)
2374     {
2375         ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2376         draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2377     }
2378     else
2379     {
2380         draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2381     }
2382 }
2383 
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)2384 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)
2385 {
2386     // Hide anything after a '##' string
2387     const char* text_display_end = FindRenderedTextEnd(text, text_end);
2388     const int text_len = (int)(text_display_end - text);
2389     if (text_len == 0)
2390         return;
2391 
2392     ImGuiContext& g = *GImGui;
2393     ImGuiWindow* window = g.CurrentWindow;
2394     RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
2395     if (g.LogEnabled)
2396         LogRenderedText(&pos_min, text, text_display_end);
2397 }
2398 
2399 
2400 // Another overly complex function until we reorganize everything into a nice all-in-one helper.
2401 // 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.
2402 // 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)2403 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)
2404 {
2405     ImGuiContext& g = *GImGui;
2406     if (text_end_full == NULL)
2407         text_end_full = FindRenderedTextEnd(text);
2408     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);
2409 
2410     //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));
2411     //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));
2412     //draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255));
2413     // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
2414     if (text_size.x > pos_max.x - pos_min.x)
2415     {
2416         // Hello wo...
2417         // |       |   |
2418         // min   max   ellipsis_max
2419         //          <-> this is generally some padding value
2420 
2421         const ImFont* font = draw_list->_Data->Font;
2422         const float font_size = draw_list->_Data->FontSize;
2423         const char* text_end_ellipsis = NULL;
2424 
2425         ImWchar ellipsis_char = font->EllipsisChar;
2426         int ellipsis_char_count = 1;
2427         if (ellipsis_char == (ImWchar)-1)
2428         {
2429             ellipsis_char = (ImWchar)'.';
2430             ellipsis_char_count = 3;
2431         }
2432         const ImFontGlyph* glyph = font->FindGlyph(ellipsis_char);
2433 
2434         float ellipsis_glyph_width = glyph->X1;                 // Width of the glyph with no padding on either side
2435         float ellipsis_total_width = ellipsis_glyph_width;      // Full width of entire ellipsis
2436 
2437         if (ellipsis_char_count > 1)
2438         {
2439             // Full ellipsis size without free spacing after it.
2440             const float spacing_between_dots = 1.0f * (draw_list->_Data->FontSize / font->FontSize);
2441             ellipsis_glyph_width = glyph->X1 - glyph->X0 + spacing_between_dots;
2442             ellipsis_total_width = ellipsis_glyph_width * (float)ellipsis_char_count - spacing_between_dots;
2443         }
2444 
2445         // We can now claim the space between pos_max.x and ellipsis_max.x
2446         const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_total_width) - pos_min.x, 1.0f);
2447         float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
2448         if (text == text_end_ellipsis && text_end_ellipsis < text_end_full)
2449         {
2450             // Always display at least 1 character if there's no room for character + ellipsis
2451             text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full);
2452             text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x;
2453         }
2454         while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1]))
2455         {
2456             // 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)
2457             text_end_ellipsis--;
2458             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
2459         }
2460 
2461         // Render text, render ellipsis
2462         RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
2463         float ellipsis_x = pos_min.x + text_size_clipped_x;
2464         if (ellipsis_x + ellipsis_total_width <= ellipsis_max_x)
2465             for (int i = 0; i < ellipsis_char_count; i++)
2466             {
2467                 font->RenderChar(draw_list, font_size, ImVec2(ellipsis_x, pos_min.y), GetColorU32(ImGuiCol_Text), ellipsis_char);
2468                 ellipsis_x += ellipsis_glyph_width;
2469             }
2470     }
2471     else
2472     {
2473         RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));
2474     }
2475 
2476     if (g.LogEnabled)
2477         LogRenderedText(&pos_min, text, text_end_full);
2478 }
2479 
2480 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)2481 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2482 {
2483     ImGuiContext& g = *GImGui;
2484     ImGuiWindow* window = g.CurrentWindow;
2485     window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2486     const float border_size = g.Style.FrameBorderSize;
2487     if (border && border_size > 0.0f)
2488     {
2489         window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2490         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2491     }
2492 }
2493 
RenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding)2494 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
2495 {
2496     ImGuiContext& g = *GImGui;
2497     ImGuiWindow* window = g.CurrentWindow;
2498     const float border_size = g.Style.FrameBorderSize;
2499     if (border_size > 0.0f)
2500     {
2501         window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2502         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2503     }
2504 }
2505 
2506 // Render an arrow aimed to be aligned with text (p_min is a position in the same space text would be positioned). To e.g. denote expanded/collapsed state
RenderArrow(ImDrawList * draw_list,ImVec2 pos,ImU32 col,ImGuiDir dir,float scale)2507 void ImGui::RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale)
2508 {
2509     const float h = draw_list->_Data->FontSize * 1.00f;
2510     float r = h * 0.40f * scale;
2511     ImVec2 center = pos + ImVec2(h * 0.50f, h * 0.50f * scale);
2512 
2513     ImVec2 a, b, c;
2514     switch (dir)
2515     {
2516     case ImGuiDir_Up:
2517     case ImGuiDir_Down:
2518         if (dir == ImGuiDir_Up) r = -r;
2519         a = ImVec2(+0.000f,+0.750f) * r;
2520         b = ImVec2(-0.866f,-0.750f) * r;
2521         c = ImVec2(+0.866f,-0.750f) * r;
2522         break;
2523     case ImGuiDir_Left:
2524     case ImGuiDir_Right:
2525         if (dir == ImGuiDir_Left) r = -r;
2526         a = ImVec2(+0.750f,+0.000f) * r;
2527         b = ImVec2(-0.750f,+0.866f) * r;
2528         c = ImVec2(-0.750f,-0.866f) * r;
2529         break;
2530     case ImGuiDir_None:
2531     case ImGuiDir_COUNT:
2532         IM_ASSERT(0);
2533         break;
2534     }
2535     draw_list->AddTriangleFilled(center + a, center + b, center + c, col);
2536 }
2537 
RenderBullet(ImDrawList * draw_list,ImVec2 pos,ImU32 col)2538 void ImGui::RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col)
2539 {
2540     draw_list->AddCircleFilled(pos, draw_list->_Data->FontSize * 0.20f, col, 8);
2541 }
2542 
RenderCheckMark(ImVec2 pos,ImU32 col,float sz)2543 void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz)
2544 {
2545     ImGuiContext& g = *GImGui;
2546     ImGuiWindow* window = g.CurrentWindow;
2547 
2548     float thickness = ImMax(sz / 5.0f, 1.0f);
2549     sz -= thickness*0.5f;
2550     pos += ImVec2(thickness*0.25f, thickness*0.25f);
2551 
2552     float third = sz / 3.0f;
2553     float bx = pos.x + third;
2554     float by = pos.y + sz - third*0.5f;
2555     window->DrawList->PathLineTo(ImVec2(bx - third, by - third));
2556     window->DrawList->PathLineTo(ImVec2(bx, by));
2557     window->DrawList->PathLineTo(ImVec2(bx + third*2, by - third*2));
2558     window->DrawList->PathStroke(col, false, thickness);
2559 }
2560 
RenderNavHighlight(const ImRect & bb,ImGuiID id,ImGuiNavHighlightFlags flags)2561 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
2562 {
2563     ImGuiContext& g = *GImGui;
2564     if (id != g.NavId)
2565         return;
2566     if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
2567         return;
2568     ImGuiWindow* window = g.CurrentWindow;
2569     if (window->DC.NavHideHighlightOneFrame)
2570         return;
2571 
2572     float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
2573     ImRect display_rect = bb;
2574     display_rect.ClipWith(window->ClipRect);
2575     if (flags & ImGuiNavHighlightFlags_TypeDefault)
2576     {
2577         const float THICKNESS = 2.0f;
2578         const float DISTANCE = 3.0f + THICKNESS * 0.5f;
2579         display_rect.Expand(ImVec2(DISTANCE,DISTANCE));
2580         bool fully_visible = window->ClipRect.Contains(display_rect);
2581         if (!fully_visible)
2582             window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
2583         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);
2584         if (!fully_visible)
2585             window->DrawList->PopClipRect();
2586     }
2587     if (flags & ImGuiNavHighlightFlags_TypeThin)
2588     {
2589         window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f);
2590     }
2591 }
2592 
2593 //-----------------------------------------------------------------------------
2594 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
2595 //-----------------------------------------------------------------------------
2596 
2597 // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
ImGuiWindow(ImGuiContext * context,const char * name)2598 ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
2599     : DrawListInst(&context->DrawListSharedData)
2600 {
2601     Name = ImStrdup(name);
2602     ID = ImHashStr(name);
2603     IDStack.push_back(ID);
2604     Flags = ImGuiWindowFlags_None;
2605     Pos = ImVec2(0.0f, 0.0f);
2606     Size = SizeFull = ImVec2(0.0f, 0.0f);
2607     ContentSize = ContentSizeExplicit = ImVec2(0.0f, 0.0f);
2608     WindowPadding = ImVec2(0.0f, 0.0f);
2609     WindowRounding = 0.0f;
2610     WindowBorderSize = 0.0f;
2611     NameBufLen = (int)strlen(name) + 1;
2612     MoveId = GetID("#MOVE");
2613     ChildId = 0;
2614     Scroll = ImVec2(0.0f, 0.0f);
2615     ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
2616     ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
2617     ScrollbarSizes = ImVec2(0.0f, 0.0f);
2618     ScrollbarX = ScrollbarY = false;
2619     Active = WasActive = false;
2620     WriteAccessed = false;
2621     Collapsed = false;
2622     WantCollapseToggle = false;
2623     SkipItems = false;
2624     Appearing = false;
2625     Hidden = false;
2626     IsFallbackWindow = false;
2627     HasCloseButton = false;
2628     ResizeBorderHeld = -1;
2629     BeginCount = 0;
2630     BeginOrderWithinParent = -1;
2631     BeginOrderWithinContext = -1;
2632     PopupId = 0;
2633     AutoFitFramesX = AutoFitFramesY = -1;
2634     AutoFitChildAxises = 0x00;
2635     AutoFitOnlyGrows = false;
2636     AutoPosLastDirection = ImGuiDir_None;
2637     HiddenFramesCanSkipItems = HiddenFramesCannotSkipItems = 0;
2638     SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
2639     SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
2640 
2641     InnerRect = ImRect(0.0f, 0.0f, 0.0f, 0.0f); // Clear so the InnerRect.GetSize() code in Begin() doesn't lead to overflow even if the result isn't used.
2642 
2643     LastFrameActive = -1;
2644     LastTimeActive = -1.0f;
2645     ItemWidthDefault = 0.0f;
2646     FontWindowScale = 1.0f;
2647     SettingsOffset = -1;
2648 
2649     DrawList = &DrawListInst;
2650     DrawList->_OwnerName = Name;
2651     ParentWindow = NULL;
2652     RootWindow = NULL;
2653     RootWindowForTitleBarHighlight = NULL;
2654     RootWindowForNav = NULL;
2655 
2656     NavLastIds[0] = NavLastIds[1] = 0;
2657     NavRectRel[0] = NavRectRel[1] = ImRect();
2658     NavLastChildNavWindow = NULL;
2659 
2660     MemoryCompacted = false;
2661     MemoryDrawListIdxCapacity = MemoryDrawListVtxCapacity = 0;
2662 }
2663 
~ImGuiWindow()2664 ImGuiWindow::~ImGuiWindow()
2665 {
2666     IM_ASSERT(DrawList == &DrawListInst);
2667     IM_DELETE(Name);
2668     for (int i = 0; i != ColumnsStorage.Size; i++)
2669         ColumnsStorage[i].~ImGuiColumns();
2670 }
2671 
GetID(const char * str,const char * str_end)2672 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
2673 {
2674     ImGuiID seed = IDStack.back();
2675     ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2676     ImGui::KeepAliveID(id);
2677     return id;
2678 }
2679 
GetID(const void * ptr)2680 ImGuiID ImGuiWindow::GetID(const void* ptr)
2681 {
2682     ImGuiID seed = IDStack.back();
2683     ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
2684     ImGui::KeepAliveID(id);
2685     return id;
2686 }
2687 
GetID(int n)2688 ImGuiID ImGuiWindow::GetID(int n)
2689 {
2690     ImGuiID seed = IDStack.back();
2691     ImGuiID id = ImHashData(&n, sizeof(n), seed);
2692     ImGui::KeepAliveID(id);
2693     return id;
2694 }
2695 
GetIDNoKeepAlive(const char * str,const char * str_end)2696 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
2697 {
2698     ImGuiID seed = IDStack.back();
2699     return ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2700 }
2701 
GetIDNoKeepAlive(const void * ptr)2702 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
2703 {
2704     ImGuiID seed = IDStack.back();
2705     return ImHashData(&ptr, sizeof(void*), seed);
2706 }
2707 
GetIDNoKeepAlive(int n)2708 ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n)
2709 {
2710     ImGuiID seed = IDStack.back();
2711     return ImHashData(&n, sizeof(n), seed);
2712 }
2713 
2714 // This is only used in rare/specific situations to manufacture an ID out of nowhere.
GetIDFromRectangle(const ImRect & r_abs)2715 ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
2716 {
2717     ImGuiID seed = IDStack.back();
2718     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) };
2719     ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
2720     ImGui::KeepAliveID(id);
2721     return id;
2722 }
2723 
SetCurrentWindow(ImGuiWindow * window)2724 static void SetCurrentWindow(ImGuiWindow* window)
2725 {
2726     ImGuiContext& g = *GImGui;
2727     g.CurrentWindow = window;
2728     if (window)
2729         g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
2730 }
2731 
2732 // Free up/compact internal window buffers, we can use this when a window becomes unused.
2733 // This is currently unused by the library, but you may call this yourself for easy GC.
2734 // Not freed:
2735 // - ImGuiWindow, ImGuiWindowSettings, Name
2736 // - StateStorage, ColumnsStorage (may hold useful data)
2737 // This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.
GcCompactTransientWindowBuffers(ImGuiWindow * window)2738 void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window)
2739 {
2740     window->MemoryCompacted = true;
2741     window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;
2742     window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;
2743     window->IDStack.clear();
2744     window->DrawList->ClearFreeMemory();
2745     window->DC.ChildWindows.clear();
2746     window->DC.ItemFlagsStack.clear();
2747     window->DC.ItemWidthStack.clear();
2748     window->DC.TextWrapPosStack.clear();
2749     window->DC.GroupStack.clear();
2750 }
2751 
GcAwakeTransientWindowBuffers(ImGuiWindow * window)2752 void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window)
2753 {
2754     // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.
2755     // The other buffers tends to amortize much faster.
2756     window->MemoryCompacted = false;
2757     window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity);
2758     window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity);
2759     window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0;
2760 }
2761 
SetActiveID(ImGuiID id,ImGuiWindow * window)2762 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
2763 {
2764     ImGuiContext& g = *GImGui;
2765     g.ActiveIdIsJustActivated = (g.ActiveId != id);
2766     if (g.ActiveIdIsJustActivated)
2767     {
2768         g.ActiveIdTimer = 0.0f;
2769         g.ActiveIdHasBeenPressedBefore = false;
2770         g.ActiveIdHasBeenEditedBefore = false;
2771         if (id != 0)
2772         {
2773             g.LastActiveId = id;
2774             g.LastActiveIdTimer = 0.0f;
2775         }
2776     }
2777     g.ActiveId = id;
2778     g.ActiveIdAllowOverlap = false;
2779     g.ActiveIdWindow = window;
2780     g.ActiveIdHasBeenEditedThisFrame = false;
2781     if (id)
2782     {
2783         g.ActiveIdIsAlive = id;
2784         g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
2785     }
2786 
2787     // Clear declaration of inputs claimed by the widget
2788     // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
2789     g.ActiveIdUsingNavDirMask = 0x00;
2790     g.ActiveIdUsingNavInputMask = 0x00;
2791     g.ActiveIdUsingKeyInputMask = 0x00;
2792 }
2793 
ClearActiveID()2794 void ImGui::ClearActiveID()
2795 {
2796     SetActiveID(0, NULL);
2797 }
2798 
SetHoveredID(ImGuiID id)2799 void ImGui::SetHoveredID(ImGuiID id)
2800 {
2801     ImGuiContext& g = *GImGui;
2802     g.HoveredId = id;
2803     g.HoveredIdAllowOverlap = false;
2804     if (id != 0 && g.HoveredIdPreviousFrame != id)
2805         g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
2806 }
2807 
GetHoveredID()2808 ImGuiID ImGui::GetHoveredID()
2809 {
2810     ImGuiContext& g = *GImGui;
2811     return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
2812 }
2813 
KeepAliveID(ImGuiID id)2814 void ImGui::KeepAliveID(ImGuiID id)
2815 {
2816     ImGuiContext& g = *GImGui;
2817     if (g.ActiveId == id)
2818         g.ActiveIdIsAlive = id;
2819     if (g.ActiveIdPreviousFrame == id)
2820         g.ActiveIdPreviousFrameIsAlive = true;
2821 }
2822 
MarkItemEdited(ImGuiID id)2823 void ImGui::MarkItemEdited(ImGuiID id)
2824 {
2825     // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
2826     // 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.
2827     ImGuiContext& g = *GImGui;
2828     IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive);
2829     IM_UNUSED(id); // Avoid unused variable warnings when asserts are compiled out.
2830     //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
2831     g.ActiveIdHasBeenEditedThisFrame = true;
2832     g.ActiveIdHasBeenEditedBefore = true;
2833     g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
2834 }
2835 
IsWindowContentHoverable(ImGuiWindow * window,ImGuiHoveredFlags flags)2836 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
2837 {
2838     // An active popup disable hovering on other windows (apart from its own children)
2839     // FIXME-OPT: This could be cached/stored within the window.
2840     ImGuiContext& g = *GImGui;
2841     if (g.NavWindow)
2842         if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
2843             if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
2844             {
2845                 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
2846                 // NB: The order of those two tests is important because Modal windows are also Popups.
2847                 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
2848                     return false;
2849                 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
2850                     return false;
2851             }
2852     return true;
2853 }
2854 
2855 // Advance cursor given item size for layout.
ItemSize(const ImVec2 & size,float text_baseline_y)2856 void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
2857 {
2858     ImGuiContext& g = *GImGui;
2859     ImGuiWindow* window = g.CurrentWindow;
2860     if (window->SkipItems)
2861         return;
2862 
2863     // We increase the height in this function to accommodate for baseline offset.
2864     // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
2865     // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
2866     const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
2867     const float line_height = ImMax(window->DC.CurrLineSize.y, size.y + offset_to_match_baseline_y);
2868 
2869     // Always align ourselves on pixel boundaries
2870     //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]
2871     window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
2872     window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y;
2873     window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);    // Next line
2874     window->DC.CursorPos.y = IM_FLOOR(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y);        // Next line
2875     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
2876     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
2877     //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
2878 
2879     window->DC.PrevLineSize.y = line_height;
2880     window->DC.CurrLineSize.y = 0.0f;
2881     window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
2882     window->DC.CurrLineTextBaseOffset = 0.0f;
2883 
2884     // Horizontal layout mode
2885     if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
2886         SameLine();
2887 }
2888 
ItemSize(const ImRect & bb,float text_baseline_y)2889 void ImGui::ItemSize(const ImRect& bb, float text_baseline_y)
2890 {
2891     ItemSize(bb.GetSize(), text_baseline_y);
2892 }
2893 
2894 // Declare item bounding box for clipping and interaction.
2895 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
2896 // declare their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd().
ItemAdd(const ImRect & bb,ImGuiID id,const ImRect * nav_bb_arg)2897 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg)
2898 {
2899     ImGuiContext& g = *GImGui;
2900     ImGuiWindow* window = g.CurrentWindow;
2901 
2902     if (id != 0)
2903     {
2904         // Navigation processing runs prior to clipping early-out
2905         //  (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
2906         //  (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
2907         //      unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
2908         //      thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
2909         //      We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
2910         //      to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
2911         // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
2912         // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
2913         window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask;
2914         if (g.NavId == id || g.NavAnyRequest)
2915             if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
2916                 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
2917                     NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
2918 
2919         // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd()
2920 #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
2921         if (id == g.DebugItemPickerBreakId)
2922         {
2923             IM_DEBUG_BREAK();
2924             g.DebugItemPickerBreakId = 0;
2925         }
2926 #endif
2927     }
2928 
2929     window->DC.LastItemId = id;
2930     window->DC.LastItemRect = bb;
2931     window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None;
2932     g.NextItemData.Flags = ImGuiNextItemDataFlags_None;
2933 
2934 #ifdef IMGUI_ENABLE_TEST_ENGINE
2935     if (id != 0)
2936         IMGUI_TEST_ENGINE_ITEM_ADD(nav_bb_arg ? *nav_bb_arg : bb, id);
2937 #endif
2938 
2939     // Clipping test
2940     const bool is_clipped = IsClippedEx(bb, id, false);
2941     if (is_clipped)
2942         return false;
2943     //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
2944 
2945     // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
2946     if (IsMouseHoveringRect(bb.Min, bb.Max))
2947         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect;
2948     return true;
2949 }
2950 
2951 // This is roughly matching the behavior of internal-facing ItemHoverable()
2952 // - 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()
2953 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
IsItemHovered(ImGuiHoveredFlags flags)2954 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
2955 {
2956     ImGuiContext& g = *GImGui;
2957     ImGuiWindow* window = g.CurrentWindow;
2958     if (g.NavDisableMouseHover && !g.NavDisableHighlight)
2959         return IsItemFocused();
2960 
2961     // Test for bounding box overlap, as updated as ItemAdd()
2962     if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
2963         return false;
2964     IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0);   // Flags not supported by this function
2965 
2966     // Test if we are hovering the right window (our window could be behind another window)
2967     // [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.
2968     // 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.
2969     //if (g.HoveredWindow != window)
2970     //    return false;
2971     if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
2972         return false;
2973 
2974     // Test if another item is active (e.g. being dragged)
2975     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
2976         if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
2977             return false;
2978 
2979     // Test if interactions on this window are blocked by an active popup or modal.
2980     // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
2981     if (!IsWindowContentHoverable(window, flags))
2982         return false;
2983 
2984     // Test if the item is disabled
2985     if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
2986         return false;
2987 
2988     // Special handling for the dummy item after Begin() which represent the title bar or tab.
2989     // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
2990     if (window->DC.LastItemId == window->MoveId && window->WriteAccessed)
2991         return false;
2992     return true;
2993 }
2994 
2995 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
ItemHoverable(const ImRect & bb,ImGuiID id)2996 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
2997 {
2998     ImGuiContext& g = *GImGui;
2999     if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
3000         return false;
3001 
3002     ImGuiWindow* window = g.CurrentWindow;
3003     if (g.HoveredWindow != window)
3004         return false;
3005     if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
3006         return false;
3007     if (!IsMouseHoveringRect(bb.Min, bb.Max))
3008         return false;
3009     if (g.NavDisableMouseHover || !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
3010         return false;
3011     if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
3012         return false;
3013 
3014     SetHoveredID(id);
3015 
3016     // [DEBUG] Item Picker tool!
3017     // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making
3018     // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered
3019     // items if we perform the test in ItemAdd(), but that would incur a small runtime cost.
3020     // #define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX in imconfig.h if you want this check to also be performed in ItemAdd().
3021     if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
3022         GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
3023     if (g.DebugItemPickerBreakId == id)
3024         IM_DEBUG_BREAK();
3025 
3026     return true;
3027 }
3028 
IsClippedEx(const ImRect & bb,ImGuiID id,bool clip_even_when_logged)3029 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
3030 {
3031     ImGuiContext& g = *GImGui;
3032     ImGuiWindow* window = g.CurrentWindow;
3033     if (!bb.Overlaps(window->ClipRect))
3034         if (id == 0 || id != g.ActiveId)
3035             if (clip_even_when_logged || !g.LogEnabled)
3036                 return true;
3037     return false;
3038 }
3039 
3040 // Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out.
FocusableItemRegister(ImGuiWindow * window,ImGuiID id)3041 bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id)
3042 {
3043     ImGuiContext& g = *GImGui;
3044 
3045     // Increment counters
3046     const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0;
3047     window->DC.FocusCounterRegular++;
3048     if (is_tab_stop)
3049         window->DC.FocusCounterTabStop++;
3050 
3051     // Process TAB/Shift-TAB to tab *OUT* of the currently focused item.
3052     // (Note that we can always TAB out of a widget that doesn't allow tabbing in)
3053     if (g.ActiveId == id && g.FocusTabPressed && !IsActiveIdUsingKey(ImGuiKey_Tab) && g.FocusRequestNextWindow == NULL)
3054     {
3055         g.FocusRequestNextWindow = window;
3056         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.
3057     }
3058 
3059     // Handle focus requests
3060     if (g.FocusRequestCurrWindow == window)
3061     {
3062         if (window->DC.FocusCounterRegular == g.FocusRequestCurrCounterRegular)
3063             return true;
3064         if (is_tab_stop && window->DC.FocusCounterTabStop == g.FocusRequestCurrCounterTabStop)
3065         {
3066             g.NavJustTabbedId = id;
3067             return true;
3068         }
3069 
3070         // If another item is about to be focused, we clear our own active id
3071         if (g.ActiveId == id)
3072             ClearActiveID();
3073     }
3074 
3075     return false;
3076 }
3077 
FocusableItemUnregister(ImGuiWindow * window)3078 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
3079 {
3080     window->DC.FocusCounterRegular--;
3081     window->DC.FocusCounterTabStop--;
3082 }
3083 
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)3084 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
3085 {
3086     if (wrap_pos_x < 0.0f)
3087         return 0.0f;
3088 
3089     ImGuiWindow* window = GImGui->CurrentWindow;
3090     if (wrap_pos_x == 0.0f)
3091         wrap_pos_x = window->WorkRect.Max.x;
3092     else if (wrap_pos_x > 0.0f)
3093         wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
3094 
3095     return ImMax(wrap_pos_x - pos.x, 1.0f);
3096 }
3097 
3098 // IM_ALLOC() == ImGui::MemAlloc()
MemAlloc(size_t size)3099 void* ImGui::MemAlloc(size_t size)
3100 {
3101     if (ImGuiContext* ctx = GImGui)
3102         ctx->IO.MetricsActiveAllocations++;
3103     return GImAllocatorAllocFunc(size, GImAllocatorUserData);
3104 }
3105 
3106 // IM_FREE() == ImGui::MemFree()
MemFree(void * ptr)3107 void ImGui::MemFree(void* ptr)
3108 {
3109     if (ptr)
3110         if (ImGuiContext* ctx = GImGui)
3111             ctx->IO.MetricsActiveAllocations--;
3112     return GImAllocatorFreeFunc(ptr, GImAllocatorUserData);
3113 }
3114 
GetClipboardText()3115 const char* ImGui::GetClipboardText()
3116 {
3117     ImGuiContext& g = *GImGui;
3118     return g.IO.GetClipboardTextFn ? g.IO.GetClipboardTextFn(g.IO.ClipboardUserData) : "";
3119 }
3120 
SetClipboardText(const char * text)3121 void ImGui::SetClipboardText(const char* text)
3122 {
3123     ImGuiContext& g = *GImGui;
3124     if (g.IO.SetClipboardTextFn)
3125         g.IO.SetClipboardTextFn(g.IO.ClipboardUserData, text);
3126 }
3127 
GetVersion()3128 const char* ImGui::GetVersion()
3129 {
3130     return IMGUI_VERSION;
3131 }
3132 
3133 // Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself
3134 // 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()3135 ImGuiContext* ImGui::GetCurrentContext()
3136 {
3137     return GImGui;
3138 }
3139 
SetCurrentContext(ImGuiContext * ctx)3140 void ImGui::SetCurrentContext(ImGuiContext* ctx)
3141 {
3142 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
3143     IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
3144 #else
3145     GImGui = ctx;
3146 #endif
3147 }
3148 
3149 // Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui.
3150 // Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
3151 // If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. your user code
3152 // may see different structures than what imgui.cpp sees, which is problematic.
3153 // 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)3154 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)
3155 {
3156     bool error = false;
3157     if (strcmp(version, IMGUI_VERSION)!=0) { error = true; IM_ASSERT(strcmp(version,IMGUI_VERSION)==0 && "Mismatched version string!");  }
3158     if (sz_io    != sizeof(ImGuiIO))       { error = true; IM_ASSERT(sz_io    == sizeof(ImGuiIO)      && "Mismatched struct layout!"); }
3159     if (sz_style != sizeof(ImGuiStyle))    { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle)   && "Mismatched struct layout!"); }
3160     if (sz_vec2  != sizeof(ImVec2))        { error = true; IM_ASSERT(sz_vec2  == sizeof(ImVec2)       && "Mismatched struct layout!"); }
3161     if (sz_vec4  != sizeof(ImVec4))        { error = true; IM_ASSERT(sz_vec4  == sizeof(ImVec4)       && "Mismatched struct layout!"); }
3162     if (sz_vert  != sizeof(ImDrawVert))    { error = true; IM_ASSERT(sz_vert  == sizeof(ImDrawVert)   && "Mismatched struct layout!"); }
3163     if (sz_idx   != sizeof(ImDrawIdx))     { error = true; IM_ASSERT(sz_idx   == sizeof(ImDrawIdx)    && "Mismatched struct layout!"); }
3164     return !error;
3165 }
3166 
SetAllocatorFunctions(void * (* alloc_func)(size_t sz,void * user_data),void (* free_func)(void * ptr,void * user_data),void * user_data)3167 void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data)
3168 {
3169     GImAllocatorAllocFunc = alloc_func;
3170     GImAllocatorFreeFunc = free_func;
3171     GImAllocatorUserData = user_data;
3172 }
3173 
CreateContext(ImFontAtlas * shared_font_atlas)3174 ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
3175 {
3176     ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
3177     if (GImGui == NULL)
3178         SetCurrentContext(ctx);
3179     Initialize(ctx);
3180     return ctx;
3181 }
3182 
DestroyContext(ImGuiContext * ctx)3183 void ImGui::DestroyContext(ImGuiContext* ctx)
3184 {
3185     if (ctx == NULL)
3186         ctx = GImGui;
3187     Shutdown(ctx);
3188     if (GImGui == ctx)
3189         SetCurrentContext(NULL);
3190     IM_DELETE(ctx);
3191 }
3192 
GetIO()3193 ImGuiIO& ImGui::GetIO()
3194 {
3195     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3196     return GImGui->IO;
3197 }
3198 
GetStyle()3199 ImGuiStyle& ImGui::GetStyle()
3200 {
3201     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3202     return GImGui->Style;
3203 }
3204 
3205 // Same value as passed to the old io.RenderDrawListsFn function. Valid after Render() and until the next call to NewFrame()
GetDrawData()3206 ImDrawData* ImGui::GetDrawData()
3207 {
3208     ImGuiContext& g = *GImGui;
3209     return g.DrawData.Valid ? &g.DrawData : NULL;
3210 }
3211 
GetTime()3212 double ImGui::GetTime()
3213 {
3214     return GImGui->Time;
3215 }
3216 
GetFrameCount()3217 int ImGui::GetFrameCount()
3218 {
3219     return GImGui->FrameCount;
3220 }
3221 
GetBackgroundDrawList()3222 ImDrawList* ImGui::GetBackgroundDrawList()
3223 {
3224     return &GImGui->BackgroundDrawList;
3225 }
3226 
GetForegroundDrawList()3227 ImDrawList* ImGui::GetForegroundDrawList()
3228 {
3229     return &GImGui->ForegroundDrawList;
3230 }
3231 
GetDrawListSharedData()3232 ImDrawListSharedData* ImGui::GetDrawListSharedData()
3233 {
3234     return &GImGui->DrawListSharedData;
3235 }
3236 
StartMouseMovingWindow(ImGuiWindow * window)3237 void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
3238 {
3239     // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
3240     // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
3241     // This is because we want ActiveId to be set even when the window is not permitted to move.
3242     ImGuiContext& g = *GImGui;
3243     FocusWindow(window);
3244     SetActiveID(window->MoveId, window);
3245     g.NavDisableHighlight = true;
3246     g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos;
3247 
3248     bool can_move_window = true;
3249     if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
3250         can_move_window = false;
3251     if (can_move_window)
3252         g.MovingWindow = window;
3253 }
3254 
3255 // Handle mouse moving window
3256 // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
UpdateMouseMovingWindowNewFrame()3257 void ImGui::UpdateMouseMovingWindowNewFrame()
3258 {
3259     ImGuiContext& g = *GImGui;
3260     if (g.MovingWindow != NULL)
3261     {
3262         // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
3263         // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
3264         KeepAliveID(g.ActiveId);
3265         IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
3266         ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
3267         if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
3268         {
3269             ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
3270             if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
3271             {
3272                 MarkIniSettingsDirty(moving_window);
3273                 SetWindowPos(moving_window, pos, ImGuiCond_Always);
3274             }
3275             FocusWindow(g.MovingWindow);
3276         }
3277         else
3278         {
3279             ClearActiveID();
3280             g.MovingWindow = NULL;
3281         }
3282     }
3283     else
3284     {
3285         // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
3286         if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
3287         {
3288             KeepAliveID(g.ActiveId);
3289             if (!g.IO.MouseDown[0])
3290                 ClearActiveID();
3291         }
3292     }
3293 }
3294 
3295 // Initiate moving window when clicking on empty space or title bar.
3296 // Handle left-click and right-click focus.
UpdateMouseMovingWindowEndFrame()3297 void ImGui::UpdateMouseMovingWindowEndFrame()
3298 {
3299     ImGuiContext& g = *GImGui;
3300     if (g.ActiveId != 0 || g.HoveredId != 0)
3301         return;
3302 
3303     // Unless we just made a window/popup appear
3304     if (g.NavWindow && g.NavWindow->Appearing)
3305         return;
3306 
3307     // Click to focus window and start moving (after we're done with all our widgets)
3308     if (g.IO.MouseClicked[0])
3309     {
3310         if (g.HoveredRootWindow != NULL)
3311         {
3312             StartMouseMovingWindow(g.HoveredWindow);
3313             if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoTitleBar))
3314                 if (!g.HoveredRootWindow->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
3315                     g.MovingWindow = NULL;
3316         }
3317         else if (g.NavWindow != NULL && GetTopMostPopupModal() == NULL)
3318         {
3319             // Clicking on void disable focus
3320             FocusWindow(NULL);
3321         }
3322     }
3323 
3324     // With right mouse button we close popups without changing focus based on where the mouse is aimed
3325     // Instead, focus will be restored to the window under the bottom-most closed popup.
3326     // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
3327     if (g.IO.MouseClicked[1])
3328     {
3329         // Find the top-most window between HoveredWindow and the top-most Modal Window.
3330         // This is where we can trim the popup stack.
3331         ImGuiWindow* modal = GetTopMostPopupModal();
3332         bool hovered_window_above_modal = false;
3333         if (modal == NULL)
3334             hovered_window_above_modal = true;
3335         for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--)
3336         {
3337             ImGuiWindow* window = g.Windows[i];
3338             if (window == modal)
3339                 break;
3340             if (window == g.HoveredWindow)
3341                 hovered_window_above_modal = true;
3342         }
3343         ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true);
3344     }
3345 }
3346 
IsWindowActiveAndVisible(ImGuiWindow * window)3347 static bool IsWindowActiveAndVisible(ImGuiWindow* window)
3348 {
3349     return (window->Active) && (!window->Hidden);
3350 }
3351 
UpdateMouseInputs()3352 static void ImGui::UpdateMouseInputs()
3353 {
3354     ImGuiContext& g = *GImGui;
3355 
3356     // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
3357     if (IsMousePosValid(&g.IO.MousePos))
3358         g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos);
3359 
3360     // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
3361     if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
3362         g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
3363     else
3364         g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
3365     if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
3366         g.NavDisableMouseHover = false;
3367 
3368     g.IO.MousePosPrev = g.IO.MousePos;
3369     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3370     {
3371         g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
3372         g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
3373         g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
3374         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;
3375         g.IO.MouseDoubleClicked[i] = false;
3376         if (g.IO.MouseClicked[i])
3377         {
3378             if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime)
3379             {
3380                 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3381                 if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
3382                     g.IO.MouseDoubleClicked[i] = true;
3383                 g.IO.MouseClickedTime[i] = -FLT_MAX;    // so the third click isn't turned into a double-click
3384             }
3385             else
3386             {
3387                 g.IO.MouseClickedTime[i] = g.Time;
3388             }
3389             g.IO.MouseClickedPos[i] = g.IO.MousePos;
3390             g.IO.MouseDownWasDoubleClick[i] = g.IO.MouseDoubleClicked[i];
3391             g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
3392             g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
3393         }
3394         else if (g.IO.MouseDown[i])
3395         {
3396             // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
3397             ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3398             g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
3399             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);
3400             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);
3401         }
3402         if (!g.IO.MouseDown[i] && !g.IO.MouseReleased[i])
3403             g.IO.MouseDownWasDoubleClick[i] = false;
3404         if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
3405             g.NavDisableMouseHover = false;
3406     }
3407 }
3408 
StartLockWheelingWindow(ImGuiWindow * window)3409 static void StartLockWheelingWindow(ImGuiWindow* window)
3410 {
3411     ImGuiContext& g = *GImGui;
3412     if (g.WheelingWindow == window)
3413         return;
3414     g.WheelingWindow = window;
3415     g.WheelingWindowRefMousePos = g.IO.MousePos;
3416     g.WheelingWindowTimer = WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER;
3417 }
3418 
UpdateMouseWheel()3419 void ImGui::UpdateMouseWheel()
3420 {
3421     ImGuiContext& g = *GImGui;
3422 
3423     // Reset the locked window if we move the mouse or after the timer elapses
3424     if (g.WheelingWindow != NULL)
3425     {
3426         g.WheelingWindowTimer -= g.IO.DeltaTime;
3427         if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
3428             g.WheelingWindowTimer = 0.0f;
3429         if (g.WheelingWindowTimer <= 0.0f)
3430         {
3431             g.WheelingWindow = NULL;
3432             g.WheelingWindowTimer = 0.0f;
3433         }
3434     }
3435 
3436     if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f)
3437         return;
3438 
3439     ImGuiWindow* window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
3440     if (!window || window->Collapsed)
3441         return;
3442 
3443     // Zoom / Scale window
3444     // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
3445     if (g.IO.MouseWheel != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
3446     {
3447         StartLockWheelingWindow(window);
3448         const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
3449         const float scale = new_font_scale / window->FontWindowScale;
3450         window->FontWindowScale = new_font_scale;
3451         if (!(window->Flags & ImGuiWindowFlags_ChildWindow))
3452         {
3453             const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
3454             SetWindowPos(window, window->Pos + offset, 0);
3455             window->Size = ImFloor(window->Size * scale);
3456             window->SizeFull = ImFloor(window->SizeFull * scale);
3457         }
3458         return;
3459     }
3460 
3461     // Mouse wheel scrolling
3462     // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent
3463 
3464     // Vertical Mouse Wheel scrolling
3465     const float wheel_y = (g.IO.MouseWheel != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f;
3466     if (wheel_y != 0.0f && !g.IO.KeyCtrl)
3467     {
3468         StartLockWheelingWindow(window);
3469         while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3470             window = window->ParentWindow;
3471         if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3472         {
3473             float max_step = window->InnerRect.GetHeight() * 0.67f;
3474             float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step));
3475             SetScrollY(window, window->Scroll.y - wheel_y * scroll_step);
3476         }
3477     }
3478 
3479     // Horizontal Mouse Wheel scrolling, or Vertical Mouse Wheel w/ Shift held
3480     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;
3481     if (wheel_x != 0.0f && !g.IO.KeyCtrl)
3482     {
3483         StartLockWheelingWindow(window);
3484         while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.x == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3485             window = window->ParentWindow;
3486         if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3487         {
3488             float max_step = window->InnerRect.GetWidth() * 0.67f;
3489             float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step));
3490             SetScrollX(window, window->Scroll.x - wheel_x * scroll_step);
3491         }
3492     }
3493 }
3494 
3495 // 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()3496 void ImGui::UpdateHoveredWindowAndCaptureFlags()
3497 {
3498     ImGuiContext& g = *GImGui;
3499 
3500     // Find the window hovered by mouse:
3501     // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3502     // - 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.
3503     // - 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.
3504     FindHoveredWindow();
3505 
3506     // Modal windows prevents cursor from hovering behind them.
3507     ImGuiWindow* modal_window = GetTopMostPopupModal();
3508     if (modal_window)
3509         if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window))
3510             g.HoveredRootWindow = g.HoveredWindow = NULL;
3511 
3512     // Disabled mouse?
3513     if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
3514         g.HoveredWindow = g.HoveredRootWindow = NULL;
3515 
3516     // 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.
3517     int mouse_earliest_button_down = -1;
3518     bool mouse_any_down = false;
3519     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3520     {
3521         if (g.IO.MouseClicked[i])
3522             g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty());
3523         mouse_any_down |= g.IO.MouseDown[i];
3524         if (g.IO.MouseDown[i])
3525             if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
3526                 mouse_earliest_button_down = i;
3527     }
3528     const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
3529 
3530     // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
3531     // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
3532     const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
3533     if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
3534         g.HoveredWindow = g.HoveredRootWindow = NULL;
3535 
3536     // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app)
3537     if (g.WantCaptureMouseNextFrame != -1)
3538         g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
3539     else
3540         g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty());
3541 
3542     // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app)
3543     if (g.WantCaptureKeyboardNextFrame != -1)
3544         g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
3545     else
3546         g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
3547     if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
3548         g.IO.WantCaptureKeyboard = true;
3549 
3550     // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
3551     g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
3552 }
3553 
NewFrameSanityChecks()3554 static void NewFrameSanityChecks()
3555 {
3556     ImGuiContext& g = *GImGui;
3557 
3558     // Check user data
3559     // (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)
3560     IM_ASSERT(g.Initialized);
3561     IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0)              && "Need a positive DeltaTime!");
3562     IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount)  && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
3563     IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f  && "Invalid DisplaySize value!");
3564     IM_ASSERT(g.IO.Fonts->Fonts.Size > 0                                && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3565     IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded()                          && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3566     IM_ASSERT(g.Style.CurveTessellationTol > 0.0f                       && "Invalid style setting!");
3567     IM_ASSERT(g.Style.CircleSegmentMaxError > 0.0f                      && "Invalid style setting!");
3568     IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f            && "Invalid style setting. Alpha cannot be negative (allows us to avoid a few clamps in color computations)!");
3569     IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting.");
3570     IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
3571     for (int n = 0; n < ImGuiKey_COUNT; n++)
3572         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)");
3573 
3574     // Perform simple check: required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only recently added in 1.60 WIP)
3575     if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
3576         IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
3577 
3578     // Perform simple check: the beta io.ConfigWindowsResizeFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
3579     if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
3580         g.IO.ConfigWindowsResizeFromEdges = false;
3581 }
3582 
NewFrame()3583 void ImGui::NewFrame()
3584 {
3585     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3586     ImGuiContext& g = *GImGui;
3587 
3588 #ifdef IMGUI_ENABLE_TEST_ENGINE
3589     ImGuiTestEngineHook_PreNewFrame(&g);
3590 #endif
3591 
3592     // Check and assert for various common IO and Configuration mistakes
3593     NewFrameSanityChecks();
3594 
3595     // Load settings on first frame (if not explicitly loaded manually before)
3596     if (!g.SettingsLoaded)
3597     {
3598         IM_ASSERT(g.SettingsWindows.empty());
3599         if (g.IO.IniFilename)
3600             LoadIniSettingsFromDisk(g.IO.IniFilename);
3601         g.SettingsLoaded = true;
3602     }
3603 
3604     // Save settings (with a delay after the last modification, so we don't spam disk too much)
3605     if (g.SettingsDirtyTimer > 0.0f)
3606     {
3607         g.SettingsDirtyTimer -= g.IO.DeltaTime;
3608         if (g.SettingsDirtyTimer <= 0.0f)
3609         {
3610             if (g.IO.IniFilename != NULL)
3611                 SaveIniSettingsToDisk(g.IO.IniFilename);
3612             else
3613                 g.IO.WantSaveIniSettings = true;  // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
3614             g.SettingsDirtyTimer = 0.0f;
3615         }
3616     }
3617 
3618     g.Time += g.IO.DeltaTime;
3619     g.WithinFrameScope = true;
3620     g.FrameCount += 1;
3621     g.TooltipOverrideCount = 0;
3622     g.WindowsActiveCount = 0;
3623 
3624     // Setup current font and draw list shared data
3625     g.IO.Fonts->Locked = true;
3626     SetCurrentFont(GetDefaultFont());
3627     IM_ASSERT(g.Font->IsLoaded());
3628     g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
3629     g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
3630     g.DrawListSharedData.SetCircleSegmentMaxError(g.Style.CircleSegmentMaxError);
3631     g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
3632     if (g.Style.AntiAliasedLines)
3633         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
3634     if (g.Style.AntiAliasedFill)
3635         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
3636     if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
3637         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
3638 
3639     g.BackgroundDrawList.Clear();
3640     g.BackgroundDrawList.PushTextureID(g.IO.Fonts->TexID);
3641     g.BackgroundDrawList.PushClipRectFullScreen();
3642 
3643     g.ForegroundDrawList.Clear();
3644     g.ForegroundDrawList.PushTextureID(g.IO.Fonts->TexID);
3645     g.ForegroundDrawList.PushClipRectFullScreen();
3646 
3647     // Mark rendering data as invalid to prevent user who may have a handle on it to use it.
3648     g.DrawData.Clear();
3649 
3650     // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
3651     if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
3652         KeepAliveID(g.DragDropPayload.SourceId);
3653 
3654     // Clear reference to active widget if the widget isn't alive anymore
3655     if (!g.HoveredIdPreviousFrame)
3656         g.HoveredIdTimer = 0.0f;
3657     if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
3658         g.HoveredIdNotActiveTimer = 0.0f;
3659     if (g.HoveredId)
3660         g.HoveredIdTimer += g.IO.DeltaTime;
3661     if (g.HoveredId && g.ActiveId != g.HoveredId)
3662         g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
3663     g.HoveredIdPreviousFrame = g.HoveredId;
3664     g.HoveredId = 0;
3665     g.HoveredIdAllowOverlap = false;
3666     if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
3667         ClearActiveID();
3668     if (g.ActiveId)
3669         g.ActiveIdTimer += g.IO.DeltaTime;
3670     g.LastActiveIdTimer += g.IO.DeltaTime;
3671     g.ActiveIdPreviousFrame = g.ActiveId;
3672     g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
3673     g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore;
3674     g.ActiveIdIsAlive = 0;
3675     g.ActiveIdHasBeenEditedThisFrame = false;
3676     g.ActiveIdPreviousFrameIsAlive = false;
3677     g.ActiveIdIsJustActivated = false;
3678     if (g.TempInputTextId != 0 && g.ActiveId != g.TempInputTextId)
3679         g.TempInputTextId = 0;
3680     if (g.ActiveId == 0)
3681     {
3682         g.ActiveIdUsingNavDirMask = g.ActiveIdUsingNavInputMask = 0;
3683         g.ActiveIdUsingKeyInputMask = 0;
3684     }
3685 
3686     // Drag and drop
3687     g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
3688     g.DragDropAcceptIdCurr = 0;
3689     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
3690     g.DragDropWithinSourceOrTarget = false;
3691 
3692     // Update keyboard input state
3693     memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
3694     for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
3695         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;
3696 
3697     // Update gamepad/keyboard directional navigation
3698     NavUpdate();
3699 
3700     // Update mouse input state
3701     UpdateMouseInputs();
3702 
3703     // Calculate frame-rate for the user, as a purely luxurious feature
3704     g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
3705     g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
3706     g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
3707     g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX;
3708 
3709     // Find hovered window
3710     // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
3711     UpdateHoveredWindowAndCaptureFlags();
3712 
3713     // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
3714     UpdateMouseMovingWindowNewFrame();
3715 
3716     // Background darkening/whitening
3717     if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
3718         g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
3719     else
3720         g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
3721 
3722     g.MouseCursor = ImGuiMouseCursor_Arrow;
3723     g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
3724     g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
3725 
3726     // Mouse wheel scrolling, scale
3727     UpdateMouseWheel();
3728 
3729     // Pressing TAB activate widget focus
3730     g.FocusTabPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab));
3731     if (g.ActiveId == 0 && g.FocusTabPressed)
3732     {
3733         // Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also
3734         // manipulate the Next fields even, even though they will be turned into Curr fields by the code below.
3735         g.FocusRequestNextWindow = g.NavWindow;
3736         g.FocusRequestNextCounterRegular = INT_MAX;
3737         if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
3738             g.FocusRequestNextCounterTabStop = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1);
3739         else
3740             g.FocusRequestNextCounterTabStop = g.IO.KeyShift ? -1 : 0;
3741     }
3742 
3743     // Turn queued focus request into current one
3744     g.FocusRequestCurrWindow = NULL;
3745     g.FocusRequestCurrCounterRegular = g.FocusRequestCurrCounterTabStop = INT_MAX;
3746     if (g.FocusRequestNextWindow != NULL)
3747     {
3748         ImGuiWindow* window = g.FocusRequestNextWindow;
3749         g.FocusRequestCurrWindow = window;
3750         if (g.FocusRequestNextCounterRegular != INT_MAX && window->DC.FocusCounterRegular != -1)
3751             g.FocusRequestCurrCounterRegular = ImModPositive(g.FocusRequestNextCounterRegular, window->DC.FocusCounterRegular + 1);
3752         if (g.FocusRequestNextCounterTabStop != INT_MAX && window->DC.FocusCounterTabStop != -1)
3753             g.FocusRequestCurrCounterTabStop = ImModPositive(g.FocusRequestNextCounterTabStop, window->DC.FocusCounterTabStop + 1);
3754         g.FocusRequestNextWindow = NULL;
3755         g.FocusRequestNextCounterRegular = g.FocusRequestNextCounterTabStop = INT_MAX;
3756     }
3757 
3758     g.NavIdTabCounter = INT_MAX;
3759 
3760     // Mark all windows as not visible and compact unused memory.
3761     IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size);
3762     const float memory_compact_start_time = (g.IO.ConfigWindowsMemoryCompactTimer >= 0.0f) ? (float)g.Time - g.IO.ConfigWindowsMemoryCompactTimer : FLT_MAX;
3763     for (int i = 0; i != g.Windows.Size; i++)
3764     {
3765         ImGuiWindow* window = g.Windows[i];
3766         window->WasActive = window->Active;
3767         window->BeginCount = 0;
3768         window->Active = false;
3769         window->WriteAccessed = false;
3770 
3771         // Garbage collect transient buffers of recently unused windows
3772         if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
3773             GcCompactTransientWindowBuffers(window);
3774     }
3775 
3776     // Closing the focused window restore focus to the first active root window in descending z-order
3777     if (g.NavWindow && !g.NavWindow->WasActive)
3778         FocusTopMostWindowUnderOne(NULL, NULL);
3779 
3780     // No window should be open at the beginning of the frame.
3781     // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
3782     g.CurrentWindowStack.resize(0);
3783     g.BeginPopupStack.resize(0);
3784     ClosePopupsOverWindow(g.NavWindow, false);
3785 
3786     // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
3787     UpdateDebugToolItemPicker();
3788 
3789     // Create implicit/fallback window - which we will only render it if the user has added something to it.
3790     // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
3791     // This fallback is particularly important as it avoid ImGui:: calls from crashing.
3792     g.WithinFrameScopeWithImplicitWindow = true;
3793     SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver);
3794     Begin("Debug##Default");
3795     IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true);
3796 
3797 #ifdef IMGUI_ENABLE_TEST_ENGINE
3798     ImGuiTestEngineHook_PostNewFrame(&g);
3799 #endif
3800 }
3801 
3802 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
UpdateDebugToolItemPicker()3803 void ImGui::UpdateDebugToolItemPicker()
3804 {
3805     ImGuiContext& g = *GImGui;
3806     g.DebugItemPickerBreakId = 0;
3807     if (g.DebugItemPickerActive)
3808     {
3809         const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
3810         ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
3811         if (ImGui::IsKeyPressedMap(ImGuiKey_Escape))
3812             g.DebugItemPickerActive = false;
3813         if (ImGui::IsMouseClicked(0) && hovered_id)
3814         {
3815             g.DebugItemPickerBreakId = hovered_id;
3816             g.DebugItemPickerActive = false;
3817         }
3818         ImGui::SetNextWindowBgAlpha(0.60f);
3819         ImGui::BeginTooltip();
3820         ImGui::Text("HoveredId: 0x%08X", hovered_id);
3821         ImGui::Text("Press ESC to abort picking.");
3822         ImGui::TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!");
3823         ImGui::EndTooltip();
3824     }
3825 }
3826 
Initialize(ImGuiContext * context)3827 void ImGui::Initialize(ImGuiContext* context)
3828 {
3829     ImGuiContext& g = *context;
3830     IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
3831 
3832     // Add .ini handle for ImGuiWindow type
3833     {
3834         ImGuiSettingsHandler ini_handler;
3835         ini_handler.TypeName = "Window";
3836         ini_handler.TypeHash = ImHashStr("Window");
3837         ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen;
3838         ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;
3839         ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll;
3840         g.SettingsHandlers.push_back(ini_handler);
3841     }
3842 
3843 #ifdef IMGUI_HAS_TABLE
3844     // Add .ini handle for ImGuiTable type
3845     {
3846         ImGuiSettingsHandler ini_handler;
3847         ini_handler.TypeName = "Table";
3848         ini_handler.TypeHash = ImHashStr("Table");
3849         ini_handler.ReadOpenFn = TableSettingsHandler_ReadOpen;
3850         ini_handler.ReadLineFn = TableSettingsHandler_ReadLine;
3851         ini_handler.WriteAllFn = TableSettingsHandler_WriteAll;
3852         g.SettingsHandlers.push_back(ini_handler);
3853     }
3854 #endif // #ifdef IMGUI_HAS_TABLE
3855 
3856 #ifdef IMGUI_HAS_DOCK
3857 #endif // #ifdef IMGUI_HAS_DOCK
3858 
3859     g.Initialized = true;
3860 }
3861 
3862 // This function is merely here to free heap allocations.
Shutdown(ImGuiContext * context)3863 void ImGui::Shutdown(ImGuiContext* context)
3864 {
3865     // 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)
3866     ImGuiContext& g = *context;
3867     if (g.IO.Fonts && g.FontAtlasOwnedByContext)
3868     {
3869         g.IO.Fonts->Locked = false;
3870         IM_DELETE(g.IO.Fonts);
3871     }
3872     g.IO.Fonts = NULL;
3873 
3874     // Cleanup of other data are conditional on actually having initialized Dear ImGui.
3875     if (!g.Initialized)
3876         return;
3877 
3878     // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
3879     if (g.SettingsLoaded && g.IO.IniFilename != NULL)
3880     {
3881         ImGuiContext* backup_context = GImGui;
3882         SetCurrentContext(context);
3883         SaveIniSettingsToDisk(g.IO.IniFilename);
3884         SetCurrentContext(backup_context);
3885     }
3886 
3887     // Clear everything else
3888     for (int i = 0; i < g.Windows.Size; i++)
3889         IM_DELETE(g.Windows[i]);
3890     g.Windows.clear();
3891     g.WindowsFocusOrder.clear();
3892     g.WindowsTempSortBuffer.clear();
3893     g.CurrentWindow = NULL;
3894     g.CurrentWindowStack.clear();
3895     g.WindowsById.Clear();
3896     g.NavWindow = NULL;
3897     g.HoveredWindow = g.HoveredRootWindow = NULL;
3898     g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
3899     g.MovingWindow = NULL;
3900     g.ColorModifiers.clear();
3901     g.StyleModifiers.clear();
3902     g.FontStack.clear();
3903     g.OpenPopupStack.clear();
3904     g.BeginPopupStack.clear();
3905     g.DrawDataBuilder.ClearFreeMemory();
3906     g.BackgroundDrawList.ClearFreeMemory();
3907     g.ForegroundDrawList.ClearFreeMemory();
3908 
3909     g.TabBars.Clear();
3910     g.CurrentTabBarStack.clear();
3911     g.ShrinkWidthBuffer.clear();
3912 
3913     g.PrivateClipboard.clear();
3914     g.InputTextState.ClearFreeMemory();
3915 
3916     g.SettingsWindows.clear();
3917     g.SettingsHandlers.clear();
3918 
3919     if (g.LogFile)
3920     {
3921 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
3922         if (g.LogFile != stdout)
3923 #endif
3924             ImFileClose(g.LogFile);
3925         g.LogFile = NULL;
3926     }
3927     g.LogBuffer.clear();
3928 
3929     g.Initialized = false;
3930 }
3931 
3932 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)3933 static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
3934 {
3935     const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
3936     const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
3937     if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
3938         return d;
3939     if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
3940         return d;
3941     return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
3942 }
3943 
AddWindowToSortBuffer(ImVector<ImGuiWindow * > * out_sorted_windows,ImGuiWindow * window)3944 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
3945 {
3946     out_sorted_windows->push_back(window);
3947     if (window->Active)
3948     {
3949         int count = window->DC.ChildWindows.Size;
3950         if (count > 1)
3951             ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
3952         for (int i = 0; i < count; i++)
3953         {
3954             ImGuiWindow* child = window->DC.ChildWindows[i];
3955             if (child->Active)
3956                 AddWindowToSortBuffer(out_sorted_windows, child);
3957         }
3958     }
3959 }
3960 
AddDrawListToDrawData(ImVector<ImDrawList * > * out_list,ImDrawList * draw_list)3961 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
3962 {
3963     if (draw_list->CmdBuffer.empty())
3964         return;
3965 
3966     // Remove trailing command if unused
3967     ImDrawCmd& last_cmd = draw_list->CmdBuffer.back();
3968     if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL)
3969     {
3970         draw_list->CmdBuffer.pop_back();
3971         if (draw_list->CmdBuffer.empty())
3972             return;
3973     }
3974 
3975     // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
3976     // May trigger for you if you are using PrimXXX functions incorrectly.
3977     IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
3978     IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
3979     if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset))
3980         IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
3981 
3982     // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
3983     // If this assert triggers because you are drawing lots of stuff manually:
3984     // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds.
3985     //   Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics window to inspect draw list contents.
3986     // - If you want large meshes with more than 64K vertices, you can either:
3987     //   (A) Handle the ImDrawCmd::VtxOffset value in your renderer back-end, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'.
3988     //       Most example back-ends already support this from 1.71. Pre-1.71 back-ends won't.
3989     //       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.
3990     //   (B) Or handle 32-bit indices in your renderer back-end, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h.
3991     //       Most example back-ends already support this. For example, the OpenGL example code detect index size at compile-time:
3992     //         glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
3993     //       Your own engine or render API may use different parameters or function calls to specify index sizes.
3994     //       2 and 4 bytes indices are generally supported by most graphics API.
3995     // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching
3996     //   the 64K limit to split your draw commands in multiple draw lists.
3997     if (sizeof(ImDrawIdx) == 2)
3998         IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
3999 
4000     out_list->push_back(draw_list);
4001 }
4002 
AddWindowToDrawData(ImVector<ImDrawList * > * out_render_list,ImGuiWindow * window)4003 static void AddWindowToDrawData(ImVector<ImDrawList*>* out_render_list, ImGuiWindow* window)
4004 {
4005     ImGuiContext& g = *GImGui;
4006     g.IO.MetricsRenderWindows++;
4007     AddDrawListToDrawData(out_render_list, window->DrawList);
4008     for (int i = 0; i < window->DC.ChildWindows.Size; i++)
4009     {
4010         ImGuiWindow* child = window->DC.ChildWindows[i];
4011         if (IsWindowActiveAndVisible(child)) // clipped children may have been marked not active
4012             AddWindowToDrawData(out_render_list, child);
4013     }
4014 }
4015 
4016 // Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)
AddRootWindowToDrawData(ImGuiWindow * window)4017 static void AddRootWindowToDrawData(ImGuiWindow* window)
4018 {
4019     ImGuiContext& g = *GImGui;
4020     int layer = (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0;
4021     AddWindowToDrawData(&g.DrawDataBuilder.Layers[layer], window);
4022 }
4023 
FlattenIntoSingleLayer()4024 void ImDrawDataBuilder::FlattenIntoSingleLayer()
4025 {
4026     int n = Layers[0].Size;
4027     int size = n;
4028     for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
4029         size += Layers[i].Size;
4030     Layers[0].resize(size);
4031     for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
4032     {
4033         ImVector<ImDrawList*>& layer = Layers[layer_n];
4034         if (layer.empty())
4035             continue;
4036         memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
4037         n += layer.Size;
4038         layer.resize(0);
4039     }
4040 }
4041 
SetupDrawData(ImVector<ImDrawList * > * draw_lists,ImDrawData * draw_data)4042 static void SetupDrawData(ImVector<ImDrawList*>* draw_lists, ImDrawData* draw_data)
4043 {
4044     ImGuiIO& io = ImGui::GetIO();
4045     draw_data->Valid = true;
4046     draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
4047     draw_data->CmdListsCount = draw_lists->Size;
4048     draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
4049     draw_data->DisplayPos = ImVec2(0.0f, 0.0f);
4050     draw_data->DisplaySize = io.DisplaySize;
4051     draw_data->FramebufferScale = io.DisplayFramebufferScale;
4052     for (int n = 0; n < draw_lists->Size; n++)
4053     {
4054         draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size;
4055         draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size;
4056     }
4057 }
4058 
4059 // When using this function it is sane to ensure that float are perfectly rounded to integer values, to that e.g. (int)(max.x-min.x) in user's render produce correct result.
PushClipRect(const ImVec2 & clip_rect_min,const ImVec2 & clip_rect_max,bool intersect_with_current_clip_rect)4060 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
4061 {
4062     ImGuiWindow* window = GetCurrentWindow();
4063     window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
4064     window->ClipRect = window->DrawList->_ClipRectStack.back();
4065 }
4066 
PopClipRect()4067 void ImGui::PopClipRect()
4068 {
4069     ImGuiWindow* window = GetCurrentWindow();
4070     window->DrawList->PopClipRect();
4071     window->ClipRect = window->DrawList->_ClipRectStack.back();
4072 }
4073 
4074 // 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()4075 void ImGui::EndFrame()
4076 {
4077     ImGuiContext& g = *GImGui;
4078     IM_ASSERT(g.Initialized);
4079     if (g.FrameCountEnded == g.FrameCount)          // Don't process EndFrame() multiple times.
4080         return;
4081     IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?");
4082 
4083     // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
4084     if (g.IO.ImeSetInputScreenPosFn && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f))
4085     {
4086         g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y);
4087         g.PlatformImeLastPos = g.PlatformImePos;
4088     }
4089 
4090     ErrorCheckEndFrame();
4091 
4092     // Hide implicit/fallback "Debug" window if it hasn't been used
4093     g.WithinFrameScopeWithImplicitWindow = false;
4094     if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
4095         g.CurrentWindow->Active = false;
4096     End();
4097 
4098     // Show CTRL+TAB list window
4099     if (g.NavWindowingTarget != NULL)
4100         NavUpdateWindowingOverlay();
4101 
4102     // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
4103     if (g.DragDropActive)
4104     {
4105         bool is_delivered = g.DragDropPayload.Delivery;
4106         bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton));
4107         if (is_delivered || is_elapsed)
4108             ClearDragDrop();
4109     }
4110 
4111     // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.
4112     if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount)
4113     {
4114         g.DragDropWithinSourceOrTarget = true;
4115         SetTooltip("...");
4116         g.DragDropWithinSourceOrTarget = false;
4117     }
4118 
4119     // End frame
4120     g.WithinFrameScope = false;
4121     g.FrameCountEnded = g.FrameCount;
4122 
4123     // Initiate moving window + handle left-click and right-click focus
4124     UpdateMouseMovingWindowEndFrame();
4125 
4126     // Sort the window list so that all child windows are after their parent
4127     // We cannot do that on FocusWindow() because childs may not exist yet
4128     g.WindowsTempSortBuffer.resize(0);
4129     g.WindowsTempSortBuffer.reserve(g.Windows.Size);
4130     for (int i = 0; i != g.Windows.Size; i++)
4131     {
4132         ImGuiWindow* window = g.Windows[i];
4133         if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow))       // if a child is active its parent will add it
4134             continue;
4135         AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window);
4136     }
4137 
4138     // 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.
4139     IM_ASSERT(g.Windows.Size == g.WindowsTempSortBuffer.Size);
4140     g.Windows.swap(g.WindowsTempSortBuffer);
4141     g.IO.MetricsActiveWindows = g.WindowsActiveCount;
4142 
4143     // Unlock font atlas
4144     g.IO.Fonts->Locked = false;
4145 
4146     // Clear Input data for next frame
4147     g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
4148     g.IO.InputQueueCharacters.resize(0);
4149     memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
4150 }
4151 
Render()4152 void ImGui::Render()
4153 {
4154     ImGuiContext& g = *GImGui;
4155     IM_ASSERT(g.Initialized);
4156 
4157     if (g.FrameCountEnded != g.FrameCount)
4158         EndFrame();
4159     g.FrameCountRendered = g.FrameCount;
4160     g.IO.MetricsRenderWindows = 0;
4161     g.DrawDataBuilder.Clear();
4162 
4163     // Add background ImDrawList
4164     if (!g.BackgroundDrawList.VtxBuffer.empty())
4165         AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.BackgroundDrawList);
4166 
4167     // Add ImDrawList to render
4168     ImGuiWindow* windows_to_render_top_most[2];
4169     windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
4170     windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingList : NULL);
4171     for (int n = 0; n != g.Windows.Size; n++)
4172     {
4173         ImGuiWindow* window = g.Windows[n];
4174         if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])
4175             AddRootWindowToDrawData(window);
4176     }
4177     for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++)
4178         if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window
4179             AddRootWindowToDrawData(windows_to_render_top_most[n]);
4180     g.DrawDataBuilder.FlattenIntoSingleLayer();
4181 
4182     // Draw software mouse cursor if requested
4183     if (g.IO.MouseDrawCursor)
4184         RenderMouseCursor(&g.ForegroundDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
4185 
4186     // Add foreground ImDrawList
4187     if (!g.ForegroundDrawList.VtxBuffer.empty())
4188         AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.ForegroundDrawList);
4189 
4190     // Setup ImDrawData structure for end-user
4191     SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData);
4192     g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount;
4193     g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount;
4194 
4195     // (Legacy) Call the Render callback function. The current prefer way is to let the user retrieve GetDrawData() and call the render function themselves.
4196 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
4197     if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)
4198         g.IO.RenderDrawListsFn(&g.DrawData);
4199 #endif
4200 }
4201 
4202 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
4203 // 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)4204 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
4205 {
4206     ImGuiContext& g = *GImGui;
4207 
4208     const char* text_display_end;
4209     if (hide_text_after_double_hash)
4210         text_display_end = FindRenderedTextEnd(text, text_end);      // Hide anything after a '##' string
4211     else
4212         text_display_end = text_end;
4213 
4214     ImFont* font = g.Font;
4215     const float font_size = g.FontSize;
4216     if (text == text_display_end)
4217         return ImVec2(0.0f, font_size);
4218     ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
4219 
4220     // Round
4221     text_size.x = IM_FLOOR(text_size.x + 0.95f);
4222 
4223     return text_size;
4224 }
4225 
4226 // Find window given position, search front-to-back
4227 // FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programatically
4228 // with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
4229 // called, aka before the next Begin(). Moving window isn't affected.
FindHoveredWindow()4230 static void FindHoveredWindow()
4231 {
4232     ImGuiContext& g = *GImGui;
4233 
4234     ImGuiWindow* hovered_window = NULL;
4235     if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
4236         hovered_window = g.MovingWindow;
4237 
4238     ImVec2 padding_regular = g.Style.TouchExtraPadding;
4239     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;
4240     for (int i = g.Windows.Size - 1; i >= 0; i--)
4241     {
4242         ImGuiWindow* window = g.Windows[i];
4243         if (!window->Active || window->Hidden)
4244             continue;
4245         if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
4246             continue;
4247 
4248         // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
4249         ImRect bb(window->OuterRectClipped);
4250         if (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize))
4251             bb.Expand(padding_regular);
4252         else
4253             bb.Expand(padding_for_resize_from_edges);
4254         if (!bb.Contains(g.IO.MousePos))
4255             continue;
4256 
4257         // Those seemingly unnecessary extra tests are because the code here is a little different in viewport/docking branches.
4258         if (hovered_window == NULL)
4259             hovered_window = window;
4260         if (hovered_window)
4261             break;
4262     }
4263 
4264     g.HoveredWindow = hovered_window;
4265     g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
4266 
4267 }
4268 
4269 // Test if mouse cursor is hovering given rectangle
4270 // NB- Rectangle is clipped by our current clip setting
4271 // 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)4272 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
4273 {
4274     ImGuiContext& g = *GImGui;
4275 
4276     // Clip
4277     ImRect rect_clipped(r_min, r_max);
4278     if (clip)
4279         rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
4280 
4281     // Expand for touch input
4282     const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
4283     if (!rect_for_touch.Contains(g.IO.MousePos))
4284         return false;
4285     return true;
4286 }
4287 
GetKeyIndex(ImGuiKey imgui_key)4288 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
4289 {
4290     IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
4291     ImGuiContext& g = *GImGui;
4292     return g.IO.KeyMap[imgui_key];
4293 }
4294 
4295 // Note that dear imgui doesn't know the semantic of each entry of io.KeysDown[]!
4296 // Use your own indices/enums according to how your back-end/engine stored them into io.KeysDown[]!
IsKeyDown(int user_key_index)4297 bool ImGui::IsKeyDown(int user_key_index)
4298 {
4299     if (user_key_index < 0)
4300         return false;
4301     ImGuiContext& g = *GImGui;
4302     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4303     return g.IO.KeysDown[user_key_index];
4304 }
4305 
4306 // t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
4307 // t1 = current time (e.g.: g.Time)
4308 // An event is triggered at:
4309 //  t = 0.0f     t = repeat_delay,    t = repeat_delay + repeat_rate*N
CalcTypematicRepeatAmount(float t0,float t1,float repeat_delay,float repeat_rate)4310 int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)
4311 {
4312     if (t1 == 0.0f)
4313         return 1;
4314     if (t0 >= t1)
4315         return 0;
4316     if (repeat_rate <= 0.0f)
4317         return (t0 < repeat_delay) && (t1 >= repeat_delay);
4318     const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);
4319     const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);
4320     const int count = count_t1 - count_t0;
4321     return count;
4322 }
4323 
GetKeyPressedAmount(int key_index,float repeat_delay,float repeat_rate)4324 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
4325 {
4326     ImGuiContext& g = *GImGui;
4327     if (key_index < 0)
4328         return 0;
4329     IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4330     const float t = g.IO.KeysDownDuration[key_index];
4331     return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
4332 }
4333 
IsKeyPressed(int user_key_index,bool repeat)4334 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
4335 {
4336     ImGuiContext& g = *GImGui;
4337     if (user_key_index < 0)
4338         return false;
4339     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4340     const float t = g.IO.KeysDownDuration[user_key_index];
4341     if (t == 0.0f)
4342         return true;
4343     if (repeat && t > g.IO.KeyRepeatDelay)
4344         return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
4345     return false;
4346 }
4347 
IsKeyReleased(int user_key_index)4348 bool ImGui::IsKeyReleased(int user_key_index)
4349 {
4350     ImGuiContext& g = *GImGui;
4351     if (user_key_index < 0) return false;
4352     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4353     return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
4354 }
4355 
IsMouseDown(ImGuiMouseButton button)4356 bool ImGui::IsMouseDown(ImGuiMouseButton button)
4357 {
4358     ImGuiContext& g = *GImGui;
4359     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4360     return g.IO.MouseDown[button];
4361 }
4362 
IsMouseClicked(ImGuiMouseButton button,bool repeat)4363 bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)
4364 {
4365     ImGuiContext& g = *GImGui;
4366     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4367     const float t = g.IO.MouseDownDuration[button];
4368     if (t == 0.0f)
4369         return true;
4370 
4371     if (repeat && t > g.IO.KeyRepeatDelay)
4372     {
4373         // 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.
4374         int amount = CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.50f);
4375         if (amount > 0)
4376             return true;
4377     }
4378     return false;
4379 }
4380 
IsMouseReleased(ImGuiMouseButton button)4381 bool ImGui::IsMouseReleased(ImGuiMouseButton button)
4382 {
4383     ImGuiContext& g = *GImGui;
4384     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4385     return g.IO.MouseReleased[button];
4386 }
4387 
IsMouseDoubleClicked(ImGuiMouseButton button)4388 bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
4389 {
4390     ImGuiContext& g = *GImGui;
4391     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4392     return g.IO.MouseDoubleClicked[button];
4393 }
4394 
4395 // [Internal] This doesn't test if the button is pressed
IsMouseDragPastThreshold(ImGuiMouseButton button,float lock_threshold)4396 bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold)
4397 {
4398     ImGuiContext& g = *GImGui;
4399     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4400     if (lock_threshold < 0.0f)
4401         lock_threshold = g.IO.MouseDragThreshold;
4402     return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
4403 }
4404 
IsMouseDragging(ImGuiMouseButton button,float lock_threshold)4405 bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold)
4406 {
4407     ImGuiContext& g = *GImGui;
4408     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4409     if (!g.IO.MouseDown[button])
4410         return false;
4411     return IsMouseDragPastThreshold(button, lock_threshold);
4412 }
4413 
GetMousePos()4414 ImVec2 ImGui::GetMousePos()
4415 {
4416     ImGuiContext& g = *GImGui;
4417     return g.IO.MousePos;
4418 }
4419 
4420 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()4421 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
4422 {
4423     ImGuiContext& g = *GImGui;
4424     if (g.BeginPopupStack.Size > 0)
4425         return g.OpenPopupStack[g.BeginPopupStack.Size-1].OpenMousePos;
4426     return g.IO.MousePos;
4427 }
4428 
4429 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
IsMousePosValid(const ImVec2 * mouse_pos)4430 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
4431 {
4432     // The assert is only to silence a false-positive in XCode Static Analysis.
4433     // 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).
4434     IM_ASSERT(GImGui != NULL);
4435     const float MOUSE_INVALID = -256000.0f;
4436     ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
4437     return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
4438 }
4439 
IsAnyMouseDown()4440 bool ImGui::IsAnyMouseDown()
4441 {
4442     ImGuiContext& g = *GImGui;
4443     for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
4444         if (g.IO.MouseDown[n])
4445             return true;
4446     return false;
4447 }
4448 
4449 // Return the delta from the initial clicking position while the mouse button is clicked or was just released.
4450 // This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
4451 // NB: This is only valid if IsMousePosValid(). Back-ends in theory should always keep mouse position valid when dragging even outside the client window.
GetMouseDragDelta(ImGuiMouseButton button,float lock_threshold)4452 ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold)
4453 {
4454     ImGuiContext& g = *GImGui;
4455     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4456     if (lock_threshold < 0.0f)
4457         lock_threshold = g.IO.MouseDragThreshold;
4458     if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])
4459         if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
4460             if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))
4461                 return g.IO.MousePos - g.IO.MouseClickedPos[button];
4462     return ImVec2(0.0f, 0.0f);
4463 }
4464 
ResetMouseDragDelta(ImGuiMouseButton button)4465 void ImGui::ResetMouseDragDelta(ImGuiMouseButton button)
4466 {
4467     ImGuiContext& g = *GImGui;
4468     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4469     // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
4470     g.IO.MouseClickedPos[button] = g.IO.MousePos;
4471 }
4472 
GetMouseCursor()4473 ImGuiMouseCursor ImGui::GetMouseCursor()
4474 {
4475     return GImGui->MouseCursor;
4476 }
4477 
SetMouseCursor(ImGuiMouseCursor cursor_type)4478 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
4479 {
4480     GImGui->MouseCursor = cursor_type;
4481 }
4482 
CaptureKeyboardFromApp(bool capture)4483 void ImGui::CaptureKeyboardFromApp(bool capture)
4484 {
4485     GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
4486 }
4487 
CaptureMouseFromApp(bool capture)4488 void ImGui::CaptureMouseFromApp(bool capture)
4489 {
4490     GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
4491 }
4492 
IsItemActive()4493 bool ImGui::IsItemActive()
4494 {
4495     ImGuiContext& g = *GImGui;
4496     if (g.ActiveId)
4497     {
4498         ImGuiWindow* window = g.CurrentWindow;
4499         return g.ActiveId == window->DC.LastItemId;
4500     }
4501     return false;
4502 }
4503 
IsItemActivated()4504 bool ImGui::IsItemActivated()
4505 {
4506     ImGuiContext& g = *GImGui;
4507     if (g.ActiveId)
4508     {
4509         ImGuiWindow* window = g.CurrentWindow;
4510         if (g.ActiveId == window->DC.LastItemId && g.ActiveIdPreviousFrame != window->DC.LastItemId)
4511             return true;
4512     }
4513     return false;
4514 }
4515 
IsItemDeactivated()4516 bool ImGui::IsItemDeactivated()
4517 {
4518     ImGuiContext& g = *GImGui;
4519     ImGuiWindow* window = g.CurrentWindow;
4520     if (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDeactivated)
4521         return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Deactivated) != 0;
4522     return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId);
4523 }
4524 
IsItemDeactivatedAfterEdit()4525 bool ImGui::IsItemDeactivatedAfterEdit()
4526 {
4527     ImGuiContext& g = *GImGui;
4528     return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore));
4529 }
4530 
IsItemFocused()4531 bool ImGui::IsItemFocused()
4532 {
4533     ImGuiContext& g = *GImGui;
4534     ImGuiWindow* window = g.CurrentWindow;
4535 
4536     if (g.NavId == 0 || g.NavDisableHighlight || g.NavId != window->DC.LastItemId)
4537         return false;
4538     return true;
4539 }
4540 
IsItemClicked(ImGuiMouseButton mouse_button)4541 bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button)
4542 {
4543     return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
4544 }
4545 
IsItemToggledOpen()4546 bool ImGui::IsItemToggledOpen()
4547 {
4548     ImGuiContext& g = *GImGui;
4549     return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false;
4550 }
4551 
IsItemToggledSelection()4552 bool ImGui::IsItemToggledSelection()
4553 {
4554     ImGuiContext& g = *GImGui;
4555     return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;
4556 }
4557 
IsAnyItemHovered()4558 bool ImGui::IsAnyItemHovered()
4559 {
4560     ImGuiContext& g = *GImGui;
4561     return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
4562 }
4563 
IsAnyItemActive()4564 bool ImGui::IsAnyItemActive()
4565 {
4566     ImGuiContext& g = *GImGui;
4567     return g.ActiveId != 0;
4568 }
4569 
IsAnyItemFocused()4570 bool ImGui::IsAnyItemFocused()
4571 {
4572     ImGuiContext& g = *GImGui;
4573     return g.NavId != 0 && !g.NavDisableHighlight;
4574 }
4575 
IsItemVisible()4576 bool ImGui::IsItemVisible()
4577 {
4578     ImGuiWindow* window = GetCurrentWindowRead();
4579     return window->ClipRect.Overlaps(window->DC.LastItemRect);
4580 }
4581 
IsItemEdited()4582 bool ImGui::IsItemEdited()
4583 {
4584     ImGuiWindow* window = GetCurrentWindowRead();
4585     return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0;
4586 }
4587 
4588 // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
SetItemAllowOverlap()4589 void ImGui::SetItemAllowOverlap()
4590 {
4591     ImGuiContext& g = *GImGui;
4592     if (g.HoveredId == g.CurrentWindow->DC.LastItemId)
4593         g.HoveredIdAllowOverlap = true;
4594     if (g.ActiveId == g.CurrentWindow->DC.LastItemId)
4595         g.ActiveIdAllowOverlap = true;
4596 }
4597 
GetItemRectMin()4598 ImVec2 ImGui::GetItemRectMin()
4599 {
4600     ImGuiWindow* window = GetCurrentWindowRead();
4601     return window->DC.LastItemRect.Min;
4602 }
4603 
GetItemRectMax()4604 ImVec2 ImGui::GetItemRectMax()
4605 {
4606     ImGuiWindow* window = GetCurrentWindowRead();
4607     return window->DC.LastItemRect.Max;
4608 }
4609 
GetItemRectSize()4610 ImVec2 ImGui::GetItemRectSize()
4611 {
4612     ImGuiWindow* window = GetCurrentWindowRead();
4613     return window->DC.LastItemRect.GetSize();
4614 }
4615 
GetViewportRect()4616 static ImRect GetViewportRect()
4617 {
4618     ImGuiContext& g = *GImGui;
4619     return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
4620 }
4621 
BeginChildEx(const char * name,ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags flags)4622 bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags)
4623 {
4624     ImGuiContext& g = *GImGui;
4625     ImGuiWindow* parent_window = g.CurrentWindow;
4626 
4627     flags |= ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
4628     flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove);  // Inherit the NoMove flag
4629 
4630     // Size
4631     const ImVec2 content_avail = GetContentRegionAvail();
4632     ImVec2 size = ImFloor(size_arg);
4633     const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
4634     if (size.x <= 0.0f)
4635         size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
4636     if (size.y <= 0.0f)
4637         size.y = ImMax(content_avail.y + size.y, 4.0f);
4638     SetNextWindowSize(size);
4639 
4640     // 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.
4641     char title[256];
4642     if (name)
4643         ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s_%08X", parent_window->Name, name, id);
4644     else
4645         ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id);
4646 
4647     const float backup_border_size = g.Style.ChildBorderSize;
4648     if (!border)
4649         g.Style.ChildBorderSize = 0.0f;
4650     bool ret = Begin(title, NULL, flags);
4651     g.Style.ChildBorderSize = backup_border_size;
4652 
4653     ImGuiWindow* child_window = g.CurrentWindow;
4654     child_window->ChildId = id;
4655     child_window->AutoFitChildAxises = (ImS8)auto_fit_axises;
4656 
4657     // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
4658     // While this is not really documented/defined, it seems that the expected thing to do.
4659     if (child_window->BeginCount == 1)
4660         parent_window->DC.CursorPos = child_window->Pos;
4661 
4662     // Process navigation-in immediately so NavInit can run on first frame
4663     if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll))
4664     {
4665         FocusWindow(child_window);
4666         NavInitWindow(child_window, false);
4667         SetActiveID(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item
4668         g.ActiveIdSource = ImGuiInputSource_Nav;
4669     }
4670     return ret;
4671 }
4672 
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4673 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4674 {
4675     ImGuiWindow* window = GetCurrentWindow();
4676     return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
4677 }
4678 
BeginChild(ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4679 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4680 {
4681     IM_ASSERT(id != 0);
4682     return BeginChildEx(NULL, id, size_arg, border, extra_flags);
4683 }
4684 
EndChild()4685 void ImGui::EndChild()
4686 {
4687     ImGuiContext& g = *GImGui;
4688     ImGuiWindow* window = g.CurrentWindow;
4689 
4690     IM_ASSERT(g.WithinEndChild == false);
4691     IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow);   // Mismatched BeginChild()/EndChild() calls
4692 
4693     g.WithinEndChild = true;
4694     if (window->BeginCount > 1)
4695     {
4696         End();
4697     }
4698     else
4699     {
4700         ImVec2 sz = window->Size;
4701         if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
4702             sz.x = ImMax(4.0f, sz.x);
4703         if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
4704             sz.y = ImMax(4.0f, sz.y);
4705         End();
4706 
4707         ImGuiWindow* parent_window = g.CurrentWindow;
4708         ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
4709         ItemSize(sz);
4710         if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
4711         {
4712             ItemAdd(bb, window->ChildId);
4713             RenderNavHighlight(bb, window->ChildId);
4714 
4715             // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
4716             if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow)
4717                 RenderNavHighlight(ImRect(bb.Min - ImVec2(2,2), bb.Max + ImVec2(2,2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
4718         }
4719         else
4720         {
4721             // Not navigable into
4722             ItemAdd(bb, 0);
4723         }
4724     }
4725     g.WithinEndChild = false;
4726 }
4727 
4728 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)4729 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
4730 {
4731     ImGuiContext& g = *GImGui;
4732     const ImGuiStyle& style = g.Style;
4733     PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
4734     PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
4735     PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
4736     PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
4737     bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
4738     PopStyleVar(3);
4739     PopStyleColor();
4740     return ret;
4741 }
4742 
EndChildFrame()4743 void ImGui::EndChildFrame()
4744 {
4745     EndChild();
4746 }
4747 
SetWindowConditionAllowFlags(ImGuiWindow * window,ImGuiCond flags,bool enabled)4748 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
4749 {
4750     window->SetWindowPosAllowFlags       = enabled ? (window->SetWindowPosAllowFlags       | flags) : (window->SetWindowPosAllowFlags       & ~flags);
4751     window->SetWindowSizeAllowFlags      = enabled ? (window->SetWindowSizeAllowFlags      | flags) : (window->SetWindowSizeAllowFlags      & ~flags);
4752     window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
4753 }
4754 
FindWindowByID(ImGuiID id)4755 ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
4756 {
4757     ImGuiContext& g = *GImGui;
4758     return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
4759 }
4760 
FindWindowByName(const char * name)4761 ImGuiWindow* ImGui::FindWindowByName(const char* name)
4762 {
4763     ImGuiID id = ImHashStr(name);
4764     return FindWindowByID(id);
4765 }
4766 
CreateNewWindow(const char * name,ImVec2 size,ImGuiWindowFlags flags)4767 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
4768 {
4769     ImGuiContext& g = *GImGui;
4770     //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags);
4771 
4772     // Create window the first time
4773     ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
4774     window->Flags = flags;
4775     g.WindowsById.SetVoidPtr(window->ID, window);
4776 
4777     // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
4778     window->Pos = ImVec2(60, 60);
4779 
4780     // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
4781     if (!(flags & ImGuiWindowFlags_NoSavedSettings))
4782         if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
4783         {
4784             // Retrieve settings from .ini file
4785             window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
4786             SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
4787             window->Pos = ImVec2(settings->Pos.x, settings->Pos.y);
4788             window->Collapsed = settings->Collapsed;
4789             if (settings->Size.x > 0 && settings->Size.y > 0)
4790                 size = ImVec2(settings->Size.x, settings->Size.y);
4791         }
4792     window->Size = window->SizeFull = ImFloor(size);
4793     window->DC.CursorStartPos = window->DC.CursorMaxPos = window->Pos; // So first call to CalcContentSize() doesn't return crazy values
4794 
4795     if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
4796     {
4797         window->AutoFitFramesX = window->AutoFitFramesY = 2;
4798         window->AutoFitOnlyGrows = false;
4799     }
4800     else
4801     {
4802         if (window->Size.x <= 0.0f)
4803             window->AutoFitFramesX = 2;
4804         if (window->Size.y <= 0.0f)
4805             window->AutoFitFramesY = 2;
4806         window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
4807     }
4808 
4809     g.WindowsFocusOrder.push_back(window);
4810     if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
4811         g.Windows.push_front(window); // Quite slow but rare and only once
4812     else
4813         g.Windows.push_back(window);
4814     return window;
4815 }
4816 
CalcWindowSizeAfterConstraint(ImGuiWindow * window,ImVec2 new_size)4817 static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size)
4818 {
4819     ImGuiContext& g = *GImGui;
4820     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)
4821     {
4822         // Using -1,-1 on either X/Y axis to preserve the current size.
4823         ImRect cr = g.NextWindowData.SizeConstraintRect;
4824         new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
4825         new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
4826         if (g.NextWindowData.SizeCallback)
4827         {
4828             ImGuiSizeCallbackData data;
4829             data.UserData = g.NextWindowData.SizeCallbackUserData;
4830             data.Pos = window->Pos;
4831             data.CurrentSize = window->SizeFull;
4832             data.DesiredSize = new_size;
4833             g.NextWindowData.SizeCallback(&data);
4834             new_size = data.DesiredSize;
4835         }
4836         new_size.x = IM_FLOOR(new_size.x);
4837         new_size.y = IM_FLOOR(new_size.y);
4838     }
4839 
4840     // Minimum size
4841     if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
4842     {
4843         ImGuiWindow* window_for_height = window;
4844         new_size = ImMax(new_size, g.Style.WindowMinSize);
4845         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
4846     }
4847     return new_size;
4848 }
4849 
CalcWindowContentSize(ImGuiWindow * window)4850 static ImVec2 CalcWindowContentSize(ImGuiWindow* window)
4851 {
4852     if (window->Collapsed)
4853         if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
4854             return window->ContentSize;
4855     if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0)
4856         return window->ContentSize;
4857 
4858     ImVec2 sz;
4859     sz.x = IM_FLOOR((window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);
4860     sz.y = IM_FLOOR((window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);
4861     return sz;
4862 }
4863 
CalcWindowAutoFitSize(ImGuiWindow * window,const ImVec2 & size_contents)4864 static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents)
4865 {
4866     ImGuiContext& g = *GImGui;
4867     ImGuiStyle& style = g.Style;
4868     ImVec2 size_decorations = ImVec2(0.0f, window->TitleBarHeight() + window->MenuBarHeight());
4869     ImVec2 size_pad = window->WindowPadding * 2.0f;
4870     ImVec2 size_desired = size_contents + size_pad + size_decorations;
4871     if (window->Flags & ImGuiWindowFlags_Tooltip)
4872     {
4873         // Tooltip always resize
4874         return size_desired;
4875     }
4876     else
4877     {
4878         // Maximum window size is determined by the viewport size or monitor size
4879         const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0;
4880         const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0;
4881         ImVec2 size_min = style.WindowMinSize;
4882         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)
4883             size_min = ImMin(size_min, ImVec2(4.0f, 4.0f));
4884         ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, g.IO.DisplaySize - style.DisplaySafeAreaPadding * 2.0f));
4885 
4886         // When the window cannot fit all contents (either because of constraints, either because screen is too small),
4887         // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
4888         ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);
4889         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);
4890         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);
4891         if (will_have_scrollbar_x)
4892             size_auto_fit.y += style.ScrollbarSize;
4893         if (will_have_scrollbar_y)
4894             size_auto_fit.x += style.ScrollbarSize;
4895         return size_auto_fit;
4896     }
4897 }
4898 
CalcWindowExpectedSize(ImGuiWindow * window)4899 ImVec2 ImGui::CalcWindowExpectedSize(ImGuiWindow* window)
4900 {
4901     ImVec2 size_contents = CalcWindowContentSize(window);
4902     ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents);
4903     ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit);
4904     return size_final;
4905 }
4906 
GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)4907 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
4908 {
4909     if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
4910         return ImGuiCol_PopupBg;
4911     if (flags & ImGuiWindowFlags_ChildWindow)
4912         return ImGuiCol_ChildBg;
4913     return ImGuiCol_WindowBg;
4914 }
4915 
CalcResizePosSizeFromAnyCorner(ImGuiWindow * window,const ImVec2 & corner_target,const ImVec2 & corner_norm,ImVec2 * out_pos,ImVec2 * out_size)4916 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
4917 {
4918     ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm);                // Expected window upper-left
4919     ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
4920     ImVec2 size_expected = pos_max - pos_min;
4921     ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected);
4922     *out_pos = pos_min;
4923     if (corner_norm.x == 0.0f)
4924         out_pos->x -= (size_constrained.x - size_expected.x);
4925     if (corner_norm.y == 0.0f)
4926         out_pos->y -= (size_constrained.y - size_expected.y);
4927     *out_size = size_constrained;
4928 }
4929 
4930 struct ImGuiResizeGripDef
4931 {
4932     ImVec2  CornerPosN;
4933     ImVec2  InnerDir;
4934     int     AngleMin12, AngleMax12;
4935 };
4936 
4937 static const ImGuiResizeGripDef resize_grip_def[4] =
4938 {
4939     { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower-right
4940     { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower-left
4941     { ImVec2(0,0), ImVec2(+1,+1), 6, 9 }, // Upper-left (Unused)
4942     { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper-right (Unused)
4943 };
4944 
GetResizeBorderRect(ImGuiWindow * window,int border_n,float perp_padding,float thickness)4945 static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
4946 {
4947     ImRect rect = window->Rect();
4948     if (thickness == 0.0f) rect.Max -= ImVec2(1,1);
4949     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
4950     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
4951     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
4952     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
4953     IM_ASSERT(0);
4954     return ImRect();
4955 }
4956 
4957 // 0..3: corners (Lower-right, Lower-left, Unused, Unused)
4958 // 4..7: borders (Top, Right, Bottom, Left)
GetWindowResizeID(ImGuiWindow * window,int n)4959 ImGuiID ImGui::GetWindowResizeID(ImGuiWindow* window, int n)
4960 {
4961     IM_ASSERT(n >= 0 && n <= 7);
4962     ImGuiID id = window->ID;
4963     id = ImHashStr("#RESIZE", 0, id);
4964     id = ImHashData(&n, sizeof(int), id);
4965     return id;
4966 }
4967 
4968 // Handle resize for: Resize Grips, Borders, Gamepad
4969 // Return true when using auto-fit (double click on resize grip)
UpdateManualResize(ImGuiWindow * window,const ImVec2 & size_auto_fit,int * border_held,int resize_grip_count,ImU32 resize_grip_col[4])4970 static bool ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4])
4971 {
4972     ImGuiContext& g = *GImGui;
4973     ImGuiWindowFlags flags = window->Flags;
4974 
4975     if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
4976         return false;
4977     if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window.
4978         return false;
4979 
4980     bool ret_auto_fit = false;
4981     const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0;
4982     const float grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
4983     const float grip_hover_inner_size = IM_FLOOR(grip_draw_size * 0.75f);
4984     const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS : 0.0f;
4985 
4986     ImVec2 pos_target(FLT_MAX, FLT_MAX);
4987     ImVec2 size_target(FLT_MAX, FLT_MAX);
4988 
4989     // Resize grips and borders are on layer 1
4990     window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
4991     window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu);
4992 
4993     // Manual resize grips
4994     PushID("#RESIZE");
4995     for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
4996     {
4997         const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
4998         const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
4999 
5000         // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
5001         ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size);
5002         if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
5003         if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
5004         bool hovered, held;
5005         ButtonBehavior(resize_rect, window->GetID(resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
5006         //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
5007         if (hovered || held)
5008             g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
5009 
5010         if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
5011         {
5012             // Manual auto-fit when double-clicking
5013             size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5014             ret_auto_fit = true;
5015             ClearActiveID();
5016         }
5017         else if (held)
5018         {
5019             // Resize from any of the four corners
5020             // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
5021             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
5022             CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPosN, &pos_target, &size_target);
5023         }
5024         if (resize_grip_n == 0 || held || hovered)
5025             resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
5026     }
5027     for (int border_n = 0; border_n < resize_border_count; border_n++)
5028     {
5029         bool hovered, held;
5030         ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS);
5031         ButtonBehavior(border_rect, window->GetID(border_n + 4), &hovered, &held, ImGuiButtonFlags_FlattenChildren);
5032         //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
5033         if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held)
5034         {
5035             g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
5036             if (held)
5037                 *border_held = border_n;
5038         }
5039         if (held)
5040         {
5041             ImVec2 border_target = window->Pos;
5042             ImVec2 border_posn;
5043             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
5044             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
5045             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
5046             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
5047             CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target);
5048         }
5049     }
5050     PopID();
5051 
5052     // Restore nav layer
5053     window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5054     window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
5055 
5056     // Navigation resize (keyboard/gamepad)
5057     if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
5058     {
5059         ImVec2 nav_resize_delta;
5060         if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift)
5061             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
5062         if (g.NavInputSource == ImGuiInputSource_NavGamepad)
5063             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down);
5064         if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f)
5065         {
5066             const float NAV_RESIZE_SPEED = 600.0f;
5067             nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
5068             g.NavWindowingToggleLayer = false;
5069             g.NavDisableMouseHover = true;
5070             resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
5071             // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
5072             size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + nav_resize_delta);
5073         }
5074     }
5075 
5076     // Apply back modified position/size to window
5077     if (size_target.x != FLT_MAX)
5078     {
5079         window->SizeFull = size_target;
5080         MarkIniSettingsDirty(window);
5081     }
5082     if (pos_target.x != FLT_MAX)
5083     {
5084         window->Pos = ImFloor(pos_target);
5085         MarkIniSettingsDirty(window);
5086     }
5087 
5088     window->Size = window->SizeFull;
5089     return ret_auto_fit;
5090 }
5091 
ClampWindowRect(ImGuiWindow * window,const ImRect & rect,const ImVec2 & padding)5092 static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& rect, const ImVec2& padding)
5093 {
5094     ImGuiContext& g = *GImGui;
5095     ImVec2 size_for_clamping = (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) ? ImVec2(window->Size.x, window->TitleBarHeight()) : window->Size;
5096     window->Pos = ImMin(rect.Max - padding, ImMax(window->Pos + size_for_clamping, rect.Min + padding) - size_for_clamping);
5097 }
5098 
RenderWindowOuterBorders(ImGuiWindow * window)5099 static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
5100 {
5101     ImGuiContext& g = *GImGui;
5102     float rounding = window->WindowRounding;
5103     float border_size = window->WindowBorderSize;
5104     if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground))
5105         window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
5106 
5107     int border_held = window->ResizeBorderHeld;
5108     if (border_held != -1)
5109     {
5110         struct ImGuiResizeBorderDef
5111         {
5112             ImVec2 InnerDir;
5113             ImVec2 CornerPosN1, CornerPosN2;
5114             float  OuterAngle;
5115         };
5116         static const ImGuiResizeBorderDef resize_border_def[4] =
5117         {
5118             { ImVec2(0,+1), ImVec2(0,0), ImVec2(1,0), IM_PI*1.50f }, // Top
5119             { ImVec2(-1,0), ImVec2(1,0), ImVec2(1,1), IM_PI*0.00f }, // Right
5120             { ImVec2(0,-1), ImVec2(1,1), ImVec2(0,1), IM_PI*0.50f }, // Bottom
5121             { ImVec2(+1,0), ImVec2(0,1), ImVec2(0,0), IM_PI*1.00f }  // Left
5122         };
5123         const ImGuiResizeBorderDef& def = resize_border_def[border_held];
5124         ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f);
5125         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);
5126         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);
5127         window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), false, ImMax(2.0f, border_size)); // Thicker than usual
5128     }
5129     if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
5130     {
5131         float y = window->Pos.y + window->TitleBarHeight() - 1;
5132         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);
5133     }
5134 }
5135 
5136 // Draw background and borders
5137 // 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)5138 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)
5139 {
5140     ImGuiContext& g = *GImGui;
5141     ImGuiStyle& style = g.Style;
5142     ImGuiWindowFlags flags = window->Flags;
5143 
5144     // Ensure that ScrollBar doesn't read last frame's SkipItems
5145     window->SkipItems = false;
5146 
5147     // Draw window + handle manual resize
5148     // 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.
5149     const float window_rounding = window->WindowRounding;
5150     const float window_border_size = window->WindowBorderSize;
5151     if (window->Collapsed)
5152     {
5153         // Title bar only
5154         float backup_border_size = style.FrameBorderSize;
5155         g.Style.FrameBorderSize = window->WindowBorderSize;
5156         ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
5157         RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
5158         g.Style.FrameBorderSize = backup_border_size;
5159     }
5160     else
5161     {
5162         // Window background
5163         if (!(flags & ImGuiWindowFlags_NoBackground))
5164         {
5165             ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
5166             bool override_alpha = false;
5167             float alpha = 1.0f;
5168             if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha)
5169             {
5170                 alpha = g.NextWindowData.BgAlphaVal;
5171                 override_alpha = true;
5172             }
5173             if (override_alpha)
5174                 bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
5175             window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot);
5176         }
5177 
5178         // Title bar
5179         if (!(flags & ImGuiWindowFlags_NoTitleBar))
5180         {
5181             ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
5182             window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top);
5183         }
5184 
5185         // Menu bar
5186         if (flags & ImGuiWindowFlags_MenuBar)
5187         {
5188             ImRect menu_bar_rect = window->MenuBarRect();
5189             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.
5190             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);
5191             if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
5192                 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
5193         }
5194 
5195         // Scrollbars
5196         if (window->ScrollbarX)
5197             Scrollbar(ImGuiAxis_X);
5198         if (window->ScrollbarY)
5199             Scrollbar(ImGuiAxis_Y);
5200 
5201         // Render resize grips (after their input handling so we don't have a frame of latency)
5202         if (!(flags & ImGuiWindowFlags_NoResize))
5203         {
5204             for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5205             {
5206                 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5207                 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
5208                 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)));
5209                 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)));
5210                 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);
5211                 window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]);
5212             }
5213         }
5214 
5215         // Borders
5216         RenderWindowOuterBorders(window);
5217     }
5218 }
5219 
5220 // Render title text, collapse button, close button
RenderWindowTitleBarContents(ImGuiWindow * window,const ImRect & title_bar_rect,const char * name,bool * p_open)5221 void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)
5222 {
5223     ImGuiContext& g = *GImGui;
5224     ImGuiStyle& style = g.Style;
5225     ImGuiWindowFlags flags = window->Flags;
5226 
5227     const bool has_close_button = (p_open != NULL);
5228     const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None);
5229 
5230     // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)
5231     const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
5232     window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
5233     window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5234     window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu);
5235 
5236     // Layout buttons
5237     // FIXME: Would be nice to generalize the subtleties expressed here into reusable code.
5238     float pad_l = style.FramePadding.x;
5239     float pad_r = style.FramePadding.x;
5240     float button_sz = g.FontSize;
5241     ImVec2 close_button_pos;
5242     ImVec2 collapse_button_pos;
5243     if (has_close_button)
5244     {
5245         pad_r += button_sz;
5246         close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5247     }
5248     if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)
5249     {
5250         pad_r += button_sz;
5251         collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5252     }
5253     if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)
5254     {
5255         collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l - style.FramePadding.x, title_bar_rect.Min.y);
5256         pad_l += button_sz;
5257     }
5258 
5259     // Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
5260     if (has_collapse_button)
5261         if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos))
5262             window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function
5263 
5264     // Close button
5265     if (has_close_button)
5266         if (CloseButton(window->GetID("#CLOSE"), close_button_pos))
5267             *p_open = false;
5268 
5269     window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5270     window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
5271     window->DC.ItemFlags = item_flags_backup;
5272 
5273     // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
5274     // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
5275     const char* UNSAVED_DOCUMENT_MARKER = "*";
5276     const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f;
5277     const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
5278 
5279     // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
5280     // while uncentered title text will still reach edges correct.
5281     if (pad_l > style.FramePadding.x)
5282         pad_l += g.Style.ItemInnerSpacing.x;
5283     if (pad_r > style.FramePadding.x)
5284         pad_r += g.Style.ItemInnerSpacing.x;
5285     if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f)
5286     {
5287         float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center
5288         float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x);
5289         pad_l = ImMax(pad_l, pad_extend * centerness);
5290         pad_r = ImMax(pad_r, pad_extend * centerness);
5291     }
5292 
5293     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);
5294     ImRect clip_r(layout_r.Min.x, layout_r.Min.y, layout_r.Max.x + g.Style.ItemInnerSpacing.x, layout_r.Max.y);
5295     //if (g.IO.KeyCtrl) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
5296     RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r);
5297     if (flags & ImGuiWindowFlags_UnsavedDocument)
5298     {
5299         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);
5300         ImVec2 off = ImVec2(0.0f, IM_FLOOR(-g.FontSize * 0.25f));
5301         RenderTextClipped(marker_pos + off, layout_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_r);
5302     }
5303 }
5304 
UpdateWindowParentAndRootLinks(ImGuiWindow * window,ImGuiWindowFlags flags,ImGuiWindow * parent_window)5305 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
5306 {
5307     window->ParentWindow = parent_window;
5308     window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
5309     if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
5310         window->RootWindow = parent_window->RootWindow;
5311     if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
5312         window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
5313     while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
5314     {
5315         IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);
5316         window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
5317     }
5318 }
5319 
5320 // Push a new Dear ImGui window to add widgets to.
5321 // - 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.
5322 // - Begin/End can be called multiple times during the frame with the same window name to append content.
5323 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
5324 //   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.
5325 // - 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.
5326 // - 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)5327 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
5328 {
5329     ImGuiContext& g = *GImGui;
5330     const ImGuiStyle& style = g.Style;
5331     IM_ASSERT(name != NULL && name[0] != '\0');     // Window name required
5332     IM_ASSERT(g.WithinFrameScope);                  // Forgot to call ImGui::NewFrame()
5333     IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
5334 
5335     // Find or create
5336     ImGuiWindow* window = FindWindowByName(name);
5337     const bool window_just_created = (window == NULL);
5338     if (window_just_created)
5339     {
5340         ImVec2 size_on_first_use = (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) ? g.NextWindowData.SizeVal : ImVec2(0.0f, 0.0f); // Any condition flag will do since we are creating a new window here.
5341         window = CreateNewWindow(name, size_on_first_use, flags);
5342     }
5343 
5344     // Automatically disable manual moving/resizing when NoInputs is set
5345     if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
5346         flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
5347 
5348     if (flags & ImGuiWindowFlags_NavFlattened)
5349         IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
5350 
5351     const int current_frame = g.FrameCount;
5352     const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
5353     window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow);
5354 
5355     // Update the Appearing flag
5356     bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1);   // Not using !WasActive because the implicit "Debug" window would always toggle off->on
5357     const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);
5358     if (flags & ImGuiWindowFlags_Popup)
5359     {
5360         ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5361         window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
5362         window_just_activated_by_user |= (window != popup_ref.Window);
5363     }
5364     window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
5365     if (window->Appearing)
5366         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
5367 
5368     // Update Flags, LastFrameActive, BeginOrderXXX fields
5369     if (first_begin_of_the_frame)
5370     {
5371         window->Flags = (ImGuiWindowFlags)flags;
5372         window->LastFrameActive = current_frame;
5373         window->LastTimeActive = (float)g.Time;
5374         window->BeginOrderWithinParent = 0;
5375         window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
5376     }
5377     else
5378     {
5379         flags = window->Flags;
5380     }
5381 
5382     // 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
5383     ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
5384     ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
5385     IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
5386 
5387     // We allow window memory to be compacted so recreate the base stack when needed.
5388     if (window->IDStack.Size == 0)
5389         window->IDStack.push_back(window->ID);
5390 
5391     // Add to stack
5392     // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
5393     g.CurrentWindowStack.push_back(window);
5394     g.CurrentWindow = NULL;
5395     ErrorCheckBeginEndCompareStacksSize(window, true);
5396     if (flags & ImGuiWindowFlags_Popup)
5397     {
5398         ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5399         popup_ref.Window = window;
5400         g.BeginPopupStack.push_back(popup_ref);
5401         window->PopupId = popup_ref.PopupId;
5402     }
5403 
5404     if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow))
5405         window->NavLastIds[0] = 0;
5406 
5407     // Update ->RootWindow and others pointers (before any possible call to FocusWindow)
5408     if (first_begin_of_the_frame)
5409         UpdateWindowParentAndRootLinks(window, flags, parent_window);
5410 
5411     // Process SetNextWindow***() calls
5412     bool window_pos_set_by_api = false;
5413     bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
5414     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos)
5415     {
5416         window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
5417         if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
5418         {
5419             // May be processed on the next frame if this is our first frame and we are measuring size
5420             // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
5421             window->SetWindowPosVal = g.NextWindowData.PosVal;
5422             window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
5423             window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5424         }
5425         else
5426         {
5427             SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
5428         }
5429     }
5430     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)
5431     {
5432         window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
5433         window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
5434         SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
5435     }
5436     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize)
5437         window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;
5438     else if (first_begin_of_the_frame)
5439         window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);
5440     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed)
5441         SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
5442     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus)
5443         FocusWindow(window);
5444     if (window->Appearing)
5445         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
5446 
5447     // When reusing window again multiple times a frame, just append content (don't need to setup again)
5448     if (first_begin_of_the_frame)
5449     {
5450         // Initialize
5451         const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
5452         window->Active = true;
5453         window->HasCloseButton = (p_open != NULL);
5454         window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX);
5455         window->IDStack.resize(1);
5456 
5457         // Restore buffer capacity when woken from a compacted state, to avoid
5458         if (window->MemoryCompacted)
5459             GcAwakeTransientWindowBuffers(window);
5460 
5461         // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
5462         // 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.
5463         bool window_title_visible_elsewhere = false;
5464         if (g.NavWindowingList != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0)   // Window titles visible when using CTRL+TAB
5465             window_title_visible_elsewhere = true;
5466         if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
5467         {
5468             size_t buf_len = (size_t)window->NameBufLen;
5469             window->Name = ImStrdupcpy(window->Name, &buf_len, name);
5470             window->NameBufLen = (int)buf_len;
5471         }
5472 
5473         // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
5474 
5475         // Update contents size from last frame for auto-fitting (or use explicit size)
5476         window->ContentSize = CalcWindowContentSize(window);
5477         if (window->HiddenFramesCanSkipItems > 0)
5478             window->HiddenFramesCanSkipItems--;
5479         if (window->HiddenFramesCannotSkipItems > 0)
5480             window->HiddenFramesCannotSkipItems--;
5481 
5482         // Hide new windows for one frame until they calculate their size
5483         if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
5484             window->HiddenFramesCannotSkipItems = 1;
5485 
5486         // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
5487         // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
5488         if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
5489         {
5490             window->HiddenFramesCannotSkipItems = 1;
5491             if (flags & ImGuiWindowFlags_AlwaysAutoResize)
5492             {
5493                 if (!window_size_x_set_by_api)
5494                     window->Size.x = window->SizeFull.x = 0.f;
5495                 if (!window_size_y_set_by_api)
5496                     window->Size.y = window->SizeFull.y = 0.f;
5497                 window->ContentSize = ImVec2(0.f, 0.f);
5498             }
5499         }
5500 
5501         // SELECT VIEWPORT
5502         // FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style)
5503         SetCurrentWindow(window);
5504 
5505         // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)
5506 
5507         if (flags & ImGuiWindowFlags_ChildWindow)
5508             window->WindowBorderSize = style.ChildBorderSize;
5509         else
5510             window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
5511         window->WindowPadding = style.WindowPadding;
5512         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
5513             window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
5514 
5515         // Collapse window by double-clicking on title bar
5516         // 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
5517         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
5518         {
5519             // 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.
5520             ImRect title_bar_rect = window->TitleBarRect();
5521             if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
5522                 window->WantCollapseToggle = true;
5523             if (window->WantCollapseToggle)
5524             {
5525                 window->Collapsed = !window->Collapsed;
5526                 MarkIniSettingsDirty(window);
5527                 FocusWindow(window);
5528             }
5529         }
5530         else
5531         {
5532             window->Collapsed = false;
5533         }
5534         window->WantCollapseToggle = false;
5535 
5536         // SIZE
5537 
5538         // Calculate auto-fit size, handle automatic resize
5539         const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSize);
5540         bool use_current_size_for_scrollbar_x = window_just_created;
5541         bool use_current_size_for_scrollbar_y = window_just_created;
5542         if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
5543         {
5544             // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
5545             if (!window_size_x_set_by_api)
5546             {
5547                 window->SizeFull.x = size_auto_fit.x;
5548                 use_current_size_for_scrollbar_x = true;
5549             }
5550             if (!window_size_y_set_by_api)
5551             {
5552                 window->SizeFull.y = size_auto_fit.y;
5553                 use_current_size_for_scrollbar_y = true;
5554             }
5555         }
5556         else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
5557         {
5558             // Auto-fit may only grow window during the first few frames
5559             // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
5560             if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
5561             {
5562                 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
5563                 use_current_size_for_scrollbar_x = true;
5564             }
5565             if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
5566             {
5567                 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
5568                 use_current_size_for_scrollbar_y = true;
5569             }
5570             if (!window->Collapsed)
5571                 MarkIniSettingsDirty(window);
5572         }
5573 
5574         // Apply minimum/maximum window size constraints and final size
5575         window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull);
5576         window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
5577 
5578         // Decoration size
5579         const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
5580 
5581         // POSITION
5582 
5583         // Popup latch its initial position, will position itself when it appears next frame
5584         if (window_just_activated_by_user)
5585         {
5586             window->AutoPosLastDirection = ImGuiDir_None;
5587             if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
5588                 window->Pos = g.BeginPopupStack.back().OpenPopupPos;
5589         }
5590 
5591         // Position child window
5592         if (flags & ImGuiWindowFlags_ChildWindow)
5593         {
5594             IM_ASSERT(parent_window && parent_window->Active);
5595             window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
5596             parent_window->DC.ChildWindows.push_back(window);
5597             if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
5598                 window->Pos = parent_window->DC.CursorPos;
5599         }
5600 
5601         const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0);
5602         if (window_pos_with_pivot)
5603             SetWindowPos(window, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering)
5604         else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
5605             window->Pos = FindBestWindowPosForPopup(window);
5606         else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
5607             window->Pos = FindBestWindowPosForPopup(window);
5608         else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
5609             window->Pos = FindBestWindowPosForPopup(window);
5610 
5611         // Clamp position/size so window stays visible within its viewport or monitor
5612         // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
5613         ImRect viewport_rect(GetViewportRect());
5614         if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5615         {
5616             ImVec2 clamp_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
5617             if (viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f)
5618             {
5619                 ClampWindowRect(window, viewport_rect, clamp_padding);
5620             }
5621         }
5622         window->Pos = ImFloor(window->Pos);
5623 
5624         // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
5625         window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
5626 
5627         // Apply window focus (new and reactivated windows are moved to front)
5628         bool want_focus = false;
5629         if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
5630         {
5631             if (flags & ImGuiWindowFlags_Popup)
5632                 want_focus = true;
5633             else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0)
5634                 want_focus = true;
5635         }
5636 
5637         // Handle manual resize: Resize Grips, Borders, Gamepad
5638         int border_held = -1;
5639         ImU32 resize_grip_col[4] = {};
5640         const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it.
5641         const float resize_grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
5642         if (!window->Collapsed)
5643             if (UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]))
5644                 use_current_size_for_scrollbar_x = use_current_size_for_scrollbar_y = true;
5645         window->ResizeBorderHeld = (signed char)border_held;
5646 
5647         // SCROLLBAR VISIBILITY
5648 
5649         // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).
5650         if (!window->Collapsed)
5651         {
5652             // When reading the current size we need to read it after size constraints have been applied.
5653             // When we use InnerRect here we are intentionally reading last frame size, same for ScrollbarSizes values before we set them again.
5654             ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - decoration_up_height);
5655             ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + window->ScrollbarSizes;
5656             ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;
5657             float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;
5658             float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;
5659             //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?
5660             window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
5661             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));
5662             if (window->ScrollbarX && !window->ScrollbarY)
5663                 window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar);
5664             window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
5665         }
5666 
5667         // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)
5668         // Update various regions. Variables they depends on should be set above in this function.
5669         // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.
5670 
5671         // Outer rectangle
5672         // Not affected by window border size. Used by:
5673         // - FindHoveredWindow() (w/ extra padding when border resize is enabled)
5674         // - Begin() initial clipping rect for drawing window background and borders.
5675         // - Begin() clipping whole child
5676         const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;
5677         const ImRect outer_rect = window->Rect();
5678         const ImRect title_bar_rect = window->TitleBarRect();
5679         window->OuterRectClipped = outer_rect;
5680         window->OuterRectClipped.ClipWith(host_rect);
5681 
5682         // Inner rectangle
5683         // Not affected by window border size. Used by:
5684         // - InnerClipRect
5685         // - ScrollToBringRectIntoView()
5686         // - NavUpdatePageUpPageDown()
5687         // - Scrollbar()
5688         window->InnerRect.Min.x = window->Pos.x;
5689         window->InnerRect.Min.y = window->Pos.y + decoration_up_height;
5690         window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x;
5691         window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y;
5692 
5693         // Inner clipping rectangle.
5694         // Will extend a little bit outside the normal work region.
5695         // This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
5696         // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
5697         // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
5698         // Affected by window/frame border size. Used by:
5699         // - Begin() initial clip rect
5700         float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
5701         window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
5702         window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size);
5703         window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
5704         window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize);
5705         window->InnerClipRect.ClipWithFull(host_rect);
5706 
5707         // Default item width. Make it proportional to window size if window manually resizes
5708         if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
5709             window->ItemWidthDefault = ImFloor(window->Size.x * 0.65f);
5710         else
5711             window->ItemWidthDefault = ImFloor(g.FontSize * 16.0f);
5712 
5713         // SCROLLING
5714 
5715         // Lock down maximum scrolling
5716         // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate
5717         // for right/bottom aligned items without creating a scrollbar.
5718         window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth());
5719         window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());
5720 
5721         // Apply scrolling
5722         window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window, true);
5723         window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
5724 
5725         // DRAWING
5726 
5727         // Setup draw list and outer clipping rectangle
5728         window->DrawList->Clear();
5729         window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
5730         PushClipRect(host_rect.Min, host_rect.Max, false);
5731 
5732         // Draw modal window background (darkens what is behind them, all viewports)
5733         const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0;
5734         const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow);
5735         if (dim_bg_for_modal || dim_bg_for_window_list)
5736         {
5737             const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
5738             window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col);
5739         }
5740 
5741         // Draw navigation selection/windowing rectangle background
5742         if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim)
5743         {
5744             ImRect bb = window->Rect();
5745             bb.Expand(g.FontSize);
5746             if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway
5747                 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
5748         }
5749 
5750         // Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call.
5751         // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
5752         // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child.
5753         // We also disabled this when we have dimming overlay behind this specific one child.
5754         // 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.
5755         {
5756             bool render_decorations_in_parent = false;
5757             if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
5758                 if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0)
5759                     render_decorations_in_parent = true;
5760             if (render_decorations_in_parent)
5761                 window->DrawList = parent_window->DrawList;
5762 
5763             // Handle title bar, scrollbar, resize grips and resize borders
5764             const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
5765             const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
5766             RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, resize_grip_count, resize_grip_col, resize_grip_draw_size);
5767 
5768             if (render_decorations_in_parent)
5769                 window->DrawList = &window->DrawListInst;
5770         }
5771 
5772         // Draw navigation selection/windowing rectangle border
5773         if (g.NavWindowingTargetAnim == window)
5774         {
5775             float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
5776             ImRect bb = window->Rect();
5777             bb.Expand(g.FontSize);
5778             if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward
5779             {
5780                 bb.Expand(-g.FontSize - 1.0f);
5781                 rounding = window->WindowRounding;
5782             }
5783             window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f);
5784         }
5785 
5786         // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
5787 
5788         // Work rectangle.
5789         // Affected by window padding and border size. Used by:
5790         // - Columns() for right-most edge
5791         // - TreeNode(), CollapsingHeader() for right-most edge
5792         // - BeginTabBar() for right-most edge
5793         const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);
5794         const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);
5795         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));
5796         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));
5797         window->WorkRect.Min.x = ImFloor(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));
5798         window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
5799         window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
5800         window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
5801 
5802         // [LEGACY] Content Region
5803         // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
5804         // Used by:
5805         // - Mouse wheel scrolling + many other things
5806         window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
5807         window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + decoration_up_height;
5808         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));
5809         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));
5810 
5811         // Setup drawing context
5812         // (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.)
5813         window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x;
5814         window->DC.GroupOffset.x = 0.0f;
5815         window->DC.ColumnsOffset.x = 0.0f;
5816         window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, decoration_up_height + window->WindowPadding.y - window->Scroll.y);
5817         window->DC.CursorPos = window->DC.CursorStartPos;
5818         window->DC.CursorPosPrevLine = window->DC.CursorPos;
5819         window->DC.CursorMaxPos = window->DC.CursorStartPos;
5820         window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
5821         window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
5822 
5823         window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5824         window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
5825         window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext;
5826         window->DC.NavLayerActiveMaskNext = 0x00;
5827         window->DC.NavFocusScopeIdCurrent = 0;
5828         window->DC.NavHideHighlightOneFrame = false;
5829         window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f);
5830 
5831         window->DC.MenuBarAppending = false;
5832         window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
5833         window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
5834         window->DC.MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user);
5835         window->DC.TreeDepth = 0;
5836         window->DC.TreeJumpToParentOnPopMask = 0x00;
5837         window->DC.ChildWindows.resize(0);
5838         window->DC.StateStorage = &window->StateStorage;
5839         window->DC.CurrentColumns = NULL;
5840         window->DC.LayoutType = ImGuiLayoutType_Vertical;
5841         window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
5842         window->DC.FocusCounterRegular = window->DC.FocusCounterTabStop = -1;
5843 
5844         window->DC.ItemFlags = parent_window ? parent_window->DC.ItemFlags : ImGuiItemFlags_Default_;
5845         window->DC.ItemWidth = window->ItemWidthDefault;
5846         window->DC.TextWrapPos = -1.0f; // disabled
5847         window->DC.ItemFlagsStack.resize(0);
5848         window->DC.ItemWidthStack.resize(0);
5849         window->DC.TextWrapPosStack.resize(0);
5850         window->DC.GroupStack.resize(0);
5851 
5852         if ((flags & ImGuiWindowFlags_ChildWindow) && (window->DC.ItemFlags != parent_window->DC.ItemFlags))
5853         {
5854             window->DC.ItemFlags = parent_window->DC.ItemFlags;
5855             window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
5856         }
5857 
5858         if (window->AutoFitFramesX > 0)
5859             window->AutoFitFramesX--;
5860         if (window->AutoFitFramesY > 0)
5861             window->AutoFitFramesY--;
5862 
5863         // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
5864         if (want_focus)
5865         {
5866             FocusWindow(window);
5867             NavInitWindow(window, false);
5868         }
5869 
5870         // Title bar
5871         if (!(flags & ImGuiWindowFlags_NoTitleBar))
5872             RenderWindowTitleBarContents(window, title_bar_rect, name, p_open);
5873 
5874         // Pressing CTRL+C while holding on a window copy its content to the clipboard
5875         // 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.
5876         // Maybe we can support CTRL+C on every element?
5877         /*
5878         if (g.ActiveId == move_id)
5879             if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
5880                 LogToClipboard();
5881         */
5882 
5883         // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
5884         // This is useful to allow creating context menus on title bar only, etc.
5885         window->DC.LastItemId = window->MoveId;
5886         window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;
5887         window->DC.LastItemRect = title_bar_rect;
5888 #ifdef IMGUI_ENABLE_TEST_ENGINE
5889         if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
5890             IMGUI_TEST_ENGINE_ITEM_ADD(window->DC.LastItemRect, window->DC.LastItemId);
5891 #endif
5892     }
5893     else
5894     {
5895         // Append
5896         SetCurrentWindow(window);
5897     }
5898 
5899     PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
5900 
5901     // 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)
5902     if (first_begin_of_the_frame)
5903         window->WriteAccessed = false;
5904 
5905     window->BeginCount++;
5906     g.NextWindowData.ClearFlags();
5907 
5908     if (flags & ImGuiWindowFlags_ChildWindow)
5909     {
5910         // Child window can be out of sight and have "negative" clip windows.
5911         // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
5912         IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
5913         if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5914             if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
5915                 window->HiddenFramesCanSkipItems = 1;
5916 
5917         // Hide along with parent or if parent is collapsed
5918         if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))
5919             window->HiddenFramesCanSkipItems = 1;
5920         if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0))
5921             window->HiddenFramesCannotSkipItems = 1;
5922     }
5923 
5924     // 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)
5925     if (style.Alpha <= 0.0f)
5926         window->HiddenFramesCanSkipItems = 1;
5927 
5928     // Update the Hidden flag
5929     window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0);
5930 
5931     // Update the SkipItems flag, used to early out of all items functions (no layout required)
5932     bool skip_items = false;
5933     if (window->Collapsed || !window->Active || window->Hidden)
5934         if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0)
5935             skip_items = true;
5936     window->SkipItems = skip_items;
5937 
5938     return !skip_items;
5939 }
5940 
End()5941 void ImGui::End()
5942 {
5943     ImGuiContext& g = *GImGui;
5944     ImGuiWindow* window = g.CurrentWindow;
5945 
5946     // Error checking: verify that user hasn't called End() too many times!
5947     if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow)
5948     {
5949         IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!");
5950         return;
5951     }
5952     IM_ASSERT(g.CurrentWindowStack.Size > 0);
5953 
5954     // Error checking: verify that user doesn't directly call End() on a child window.
5955     if (window->Flags & ImGuiWindowFlags_ChildWindow)
5956         IM_ASSERT_USER_ERROR(g.WithinEndChild, "Must call EndChild() and not End()!");
5957 
5958     // Close anything that is open
5959     if (window->DC.CurrentColumns)
5960         EndColumns();
5961     PopClipRect();   // Inner window clip rectangle
5962 
5963     // Stop logging
5964     if (!(window->Flags & ImGuiWindowFlags_ChildWindow))    // FIXME: add more options for scope of logging
5965         LogFinish();
5966 
5967     // Pop from window stack
5968     g.CurrentWindowStack.pop_back();
5969     if (window->Flags & ImGuiWindowFlags_Popup)
5970         g.BeginPopupStack.pop_back();
5971     ErrorCheckBeginEndCompareStacksSize(window, false);
5972     SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
5973 }
5974 
BringWindowToFocusFront(ImGuiWindow * window)5975 void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
5976 {
5977     ImGuiContext& g = *GImGui;
5978     if (g.WindowsFocusOrder.back() == window)
5979         return;
5980     for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the top-most window
5981         if (g.WindowsFocusOrder[i] == window)
5982         {
5983             memmove(&g.WindowsFocusOrder[i], &g.WindowsFocusOrder[i + 1], (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*));
5984             g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window;
5985             break;
5986         }
5987 }
5988 
BringWindowToDisplayFront(ImGuiWindow * window)5989 void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
5990 {
5991     ImGuiContext& g = *GImGui;
5992     ImGuiWindow* current_front_window = g.Windows.back();
5993     if (current_front_window == window || current_front_window->RootWindow == window)
5994         return;
5995     for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window
5996         if (g.Windows[i] == window)
5997         {
5998             memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
5999             g.Windows[g.Windows.Size - 1] = window;
6000             break;
6001         }
6002 }
6003 
BringWindowToDisplayBack(ImGuiWindow * window)6004 void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
6005 {
6006     ImGuiContext& g = *GImGui;
6007     if (g.Windows[0] == window)
6008         return;
6009     for (int i = 0; i < g.Windows.Size; i++)
6010         if (g.Windows[i] == window)
6011         {
6012             memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
6013             g.Windows[0] = window;
6014             break;
6015         }
6016 }
6017 
6018 // Moving window to front of display and set focus (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)6019 void ImGui::FocusWindow(ImGuiWindow* window)
6020 {
6021     ImGuiContext& g = *GImGui;
6022 
6023     if (g.NavWindow != window)
6024     {
6025         g.NavWindow = window;
6026         if (window && g.NavDisableMouseHover)
6027             g.NavMousePosDirty = true;
6028         g.NavInitRequest = false;
6029         g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
6030         g.NavFocusScopeId = 0;
6031         g.NavIdIsAlive = false;
6032         g.NavLayer = ImGuiNavLayer_Main;
6033         //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL);
6034     }
6035 
6036     // Close popups if any
6037     ClosePopupsOverWindow(window, false);
6038 
6039     // Passing NULL allow to disable keyboard focus
6040     if (!window)
6041         return;
6042 
6043     // Move the root window to the top of the pile
6044     IM_ASSERT(window->RootWindow != NULL);
6045     ImGuiWindow* focus_front_window = window->RootWindow; // NB: In docking branch this is window->RootWindowDockStop
6046     ImGuiWindow* display_front_window = window->RootWindow;
6047 
6048     // Steal focus on active widgets
6049     if (focus_front_window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement may be unnecessary? Need further testing before removing it..
6050         if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window)
6051             ClearActiveID();
6052 
6053     // Bring to front
6054     BringWindowToFocusFront(focus_front_window);
6055     if (((window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
6056         BringWindowToDisplayFront(display_front_window);
6057 }
6058 
FocusTopMostWindowUnderOne(ImGuiWindow * under_this_window,ImGuiWindow * ignore_window)6059 void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window)
6060 {
6061     ImGuiContext& g = *GImGui;
6062 
6063     int start_idx = g.WindowsFocusOrder.Size - 1;
6064     if (under_this_window != NULL)
6065     {
6066         int under_this_window_idx = FindWindowFocusIndex(under_this_window);
6067         if (under_this_window_idx != -1)
6068             start_idx = under_this_window_idx - 1;
6069     }
6070     for (int i = start_idx; i >= 0; i--)
6071     {
6072         // 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.
6073         ImGuiWindow* window = g.WindowsFocusOrder[i];
6074         if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow))
6075             if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
6076             {
6077                 ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window);
6078                 FocusWindow(focus_window);
6079                 return;
6080             }
6081     }
6082     FocusWindow(NULL);
6083 }
6084 
SetNextItemWidth(float item_width)6085 void ImGui::SetNextItemWidth(float item_width)
6086 {
6087     ImGuiContext& g = *GImGui;
6088     g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasWidth;
6089     g.NextItemData.Width = item_width;
6090 }
6091 
PushItemWidth(float item_width)6092 void ImGui::PushItemWidth(float item_width)
6093 {
6094     ImGuiContext& g = *GImGui;
6095     ImGuiWindow* window = g.CurrentWindow;
6096     window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
6097     window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
6098     g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
6099 }
6100 
PushMultiItemsWidths(int components,float w_full)6101 void ImGui::PushMultiItemsWidths(int components, float w_full)
6102 {
6103     ImGuiContext& g = *GImGui;
6104     ImGuiWindow* window = g.CurrentWindow;
6105     const ImGuiStyle& style = g.Style;
6106     const float w_item_one  = ImMax(1.0f, IM_FLOOR((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
6107     const float w_item_last = ImMax(1.0f, IM_FLOOR(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
6108     window->DC.ItemWidthStack.push_back(w_item_last);
6109     for (int i = 0; i < components-1; i++)
6110         window->DC.ItemWidthStack.push_back(w_item_one);
6111     window->DC.ItemWidth = window->DC.ItemWidthStack.back();
6112     g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
6113 }
6114 
PopItemWidth()6115 void ImGui::PopItemWidth()
6116 {
6117     ImGuiWindow* window = GetCurrentWindow();
6118     window->DC.ItemWidthStack.pop_back();
6119     window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
6120 }
6121 
6122 // Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().
6123 // The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()
CalcItemWidth()6124 float ImGui::CalcItemWidth()
6125 {
6126     ImGuiContext& g = *GImGui;
6127     ImGuiWindow* window = g.CurrentWindow;
6128     float w;
6129     if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth)
6130         w = g.NextItemData.Width;
6131     else
6132         w = window->DC.ItemWidth;
6133     if (w < 0.0f)
6134     {
6135         float region_max_x = GetContentRegionMaxAbs().x;
6136         w = ImMax(1.0f, region_max_x - window->DC.CursorPos.x + w);
6137     }
6138     w = IM_FLOOR(w);
6139     return w;
6140 }
6141 
6142 // [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().
6143 // Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.
6144 // Note that only CalcItemWidth() is publicly exposed.
6145 // 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)6146 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)
6147 {
6148     ImGuiWindow* window = GImGui->CurrentWindow;
6149 
6150     ImVec2 region_max;
6151     if (size.x < 0.0f || size.y < 0.0f)
6152         region_max = GetContentRegionMaxAbs();
6153 
6154     if (size.x == 0.0f)
6155         size.x = default_w;
6156     else if (size.x < 0.0f)
6157         size.x = ImMax(4.0f, region_max.x - window->DC.CursorPos.x + size.x);
6158 
6159     if (size.y == 0.0f)
6160         size.y = default_h;
6161     else if (size.y < 0.0f)
6162         size.y = ImMax(4.0f, region_max.y - window->DC.CursorPos.y + size.y);
6163 
6164     return size;
6165 }
6166 
SetCurrentFont(ImFont * font)6167 void ImGui::SetCurrentFont(ImFont* font)
6168 {
6169     ImGuiContext& g = *GImGui;
6170     IM_ASSERT(font && font->IsLoaded());    // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
6171     IM_ASSERT(font->Scale > 0.0f);
6172     g.Font = font;
6173     g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale);
6174     g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
6175 
6176     ImFontAtlas* atlas = g.Font->ContainerAtlas;
6177     g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
6178     g.DrawListSharedData.Font = g.Font;
6179     g.DrawListSharedData.FontSize = g.FontSize;
6180 }
6181 
PushFont(ImFont * font)6182 void ImGui::PushFont(ImFont* font)
6183 {
6184     ImGuiContext& g = *GImGui;
6185     if (!font)
6186         font = GetDefaultFont();
6187     SetCurrentFont(font);
6188     g.FontStack.push_back(font);
6189     g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
6190 }
6191 
PopFont()6192 void  ImGui::PopFont()
6193 {
6194     ImGuiContext& g = *GImGui;
6195     g.CurrentWindow->DrawList->PopTextureID();
6196     g.FontStack.pop_back();
6197     SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
6198 }
6199 
PushItemFlag(ImGuiItemFlags option,bool enabled)6200 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
6201 {
6202     ImGuiWindow* window = GetCurrentWindow();
6203     if (enabled)
6204         window->DC.ItemFlags |= option;
6205     else
6206         window->DC.ItemFlags &= ~option;
6207     window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
6208 }
6209 
PopItemFlag()6210 void ImGui::PopItemFlag()
6211 {
6212     ImGuiWindow* window = GetCurrentWindow();
6213     window->DC.ItemFlagsStack.pop_back();
6214     window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back();
6215 }
6216 
6217 // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system.
PushAllowKeyboardFocus(bool allow_keyboard_focus)6218 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
6219 {
6220     PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus);
6221 }
6222 
PopAllowKeyboardFocus()6223 void ImGui::PopAllowKeyboardFocus()
6224 {
6225     PopItemFlag();
6226 }
6227 
PushButtonRepeat(bool repeat)6228 void ImGui::PushButtonRepeat(bool repeat)
6229 {
6230     PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
6231 }
6232 
PopButtonRepeat()6233 void ImGui::PopButtonRepeat()
6234 {
6235     PopItemFlag();
6236 }
6237 
PushTextWrapPos(float wrap_pos_x)6238 void ImGui::PushTextWrapPos(float wrap_pos_x)
6239 {
6240     ImGuiWindow* window = GetCurrentWindow();
6241     window->DC.TextWrapPos = wrap_pos_x;
6242     window->DC.TextWrapPosStack.push_back(wrap_pos_x);
6243 }
6244 
PopTextWrapPos()6245 void ImGui::PopTextWrapPos()
6246 {
6247     ImGuiWindow* window = GetCurrentWindow();
6248     window->DC.TextWrapPosStack.pop_back();
6249     window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
6250 }
6251 
6252 // 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)6253 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
6254 {
6255     ImGuiContext& g = *GImGui;
6256     ImGuiColorMod backup;
6257     backup.Col = idx;
6258     backup.BackupValue = g.Style.Colors[idx];
6259     g.ColorModifiers.push_back(backup);
6260     g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
6261 }
6262 
PushStyleColor(ImGuiCol idx,const ImVec4 & col)6263 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
6264 {
6265     ImGuiContext& g = *GImGui;
6266     ImGuiColorMod backup;
6267     backup.Col = idx;
6268     backup.BackupValue = g.Style.Colors[idx];
6269     g.ColorModifiers.push_back(backup);
6270     g.Style.Colors[idx] = col;
6271 }
6272 
PopStyleColor(int count)6273 void ImGui::PopStyleColor(int count)
6274 {
6275     ImGuiContext& g = *GImGui;
6276     while (count > 0)
6277     {
6278         ImGuiColorMod& backup = g.ColorModifiers.back();
6279         g.Style.Colors[backup.Col] = backup.BackupValue;
6280         g.ColorModifiers.pop_back();
6281         count--;
6282     }
6283 }
6284 
6285 struct ImGuiStyleVarInfo
6286 {
6287     ImGuiDataType   Type;
6288     ImU32           Count;
6289     ImU32           Offset;
GetVarPtrImGuiStyleVarInfo6290     void*           GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
6291 };
6292 
6293 static const ImGuiStyleVarInfo GStyleVarInfo[] =
6294 {
6295     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) },               // ImGuiStyleVar_Alpha
6296     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) },       // ImGuiStyleVar_WindowPadding
6297     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) },      // ImGuiStyleVar_WindowRounding
6298     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) },    // ImGuiStyleVar_WindowBorderSize
6299     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) },       // ImGuiStyleVar_WindowMinSize
6300     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) },    // ImGuiStyleVar_WindowTitleAlign
6301     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) },       // ImGuiStyleVar_ChildRounding
6302     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) },     // ImGuiStyleVar_ChildBorderSize
6303     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) },       // ImGuiStyleVar_PopupRounding
6304     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) },     // ImGuiStyleVar_PopupBorderSize
6305     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) },        // ImGuiStyleVar_FramePadding
6306     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) },       // ImGuiStyleVar_FrameRounding
6307     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) },     // ImGuiStyleVar_FrameBorderSize
6308     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) },         // ImGuiStyleVar_ItemSpacing
6309     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) },    // ImGuiStyleVar_ItemInnerSpacing
6310     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) },       // ImGuiStyleVar_IndentSpacing
6311     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) },       // ImGuiStyleVar_ScrollbarSize
6312     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) },   // ImGuiStyleVar_ScrollbarRounding
6313     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) },         // ImGuiStyleVar_GrabMinSize
6314     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) },        // ImGuiStyleVar_GrabRounding
6315     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) },         // ImGuiStyleVar_TabRounding
6316     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) },     // ImGuiStyleVar_ButtonTextAlign
6317     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
6318 };
6319 
GetStyleVarInfo(ImGuiStyleVar idx)6320 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
6321 {
6322     IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
6323     IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
6324     return &GStyleVarInfo[idx];
6325 }
6326 
PushStyleVar(ImGuiStyleVar idx,float val)6327 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
6328 {
6329     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
6330     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
6331     {
6332         ImGuiContext& g = *GImGui;
6333         float* pvar = (float*)var_info->GetVarPtr(&g.Style);
6334         g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
6335         *pvar = val;
6336         return;
6337     }
6338     IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!");
6339 }
6340 
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)6341 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
6342 {
6343     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
6344     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
6345     {
6346         ImGuiContext& g = *GImGui;
6347         ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
6348         g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
6349         *pvar = val;
6350         return;
6351     }
6352     IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!");
6353 }
6354 
PopStyleVar(int count)6355 void ImGui::PopStyleVar(int count)
6356 {
6357     ImGuiContext& g = *GImGui;
6358     while (count > 0)
6359     {
6360         // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
6361         ImGuiStyleMod& backup = g.StyleModifiers.back();
6362         const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
6363         void* data = info->GetVarPtr(&g.Style);
6364         if (info->Type == ImGuiDataType_Float && info->Count == 1)      { ((float*)data)[0] = backup.BackupFloat[0]; }
6365         else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
6366         g.StyleModifiers.pop_back();
6367         count--;
6368     }
6369 }
6370 
GetStyleColorName(ImGuiCol idx)6371 const char* ImGui::GetStyleColorName(ImGuiCol idx)
6372 {
6373     // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
6374     switch (idx)
6375     {
6376     case ImGuiCol_Text: return "Text";
6377     case ImGuiCol_TextDisabled: return "TextDisabled";
6378     case ImGuiCol_WindowBg: return "WindowBg";
6379     case ImGuiCol_ChildBg: return "ChildBg";
6380     case ImGuiCol_PopupBg: return "PopupBg";
6381     case ImGuiCol_Border: return "Border";
6382     case ImGuiCol_BorderShadow: return "BorderShadow";
6383     case ImGuiCol_FrameBg: return "FrameBg";
6384     case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
6385     case ImGuiCol_FrameBgActive: return "FrameBgActive";
6386     case ImGuiCol_TitleBg: return "TitleBg";
6387     case ImGuiCol_TitleBgActive: return "TitleBgActive";
6388     case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
6389     case ImGuiCol_MenuBarBg: return "MenuBarBg";
6390     case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
6391     case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
6392     case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
6393     case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
6394     case ImGuiCol_CheckMark: return "CheckMark";
6395     case ImGuiCol_SliderGrab: return "SliderGrab";
6396     case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
6397     case ImGuiCol_Button: return "Button";
6398     case ImGuiCol_ButtonHovered: return "ButtonHovered";
6399     case ImGuiCol_ButtonActive: return "ButtonActive";
6400     case ImGuiCol_Header: return "Header";
6401     case ImGuiCol_HeaderHovered: return "HeaderHovered";
6402     case ImGuiCol_HeaderActive: return "HeaderActive";
6403     case ImGuiCol_Separator: return "Separator";
6404     case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
6405     case ImGuiCol_SeparatorActive: return "SeparatorActive";
6406     case ImGuiCol_ResizeGrip: return "ResizeGrip";
6407     case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
6408     case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
6409     case ImGuiCol_Tab: return "Tab";
6410     case ImGuiCol_TabHovered: return "TabHovered";
6411     case ImGuiCol_TabActive: return "TabActive";
6412     case ImGuiCol_TabUnfocused: return "TabUnfocused";
6413     case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive";
6414     case ImGuiCol_PlotLines: return "PlotLines";
6415     case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
6416     case ImGuiCol_PlotHistogram: return "PlotHistogram";
6417     case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
6418     case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
6419     case ImGuiCol_DragDropTarget: return "DragDropTarget";
6420     case ImGuiCol_NavHighlight: return "NavHighlight";
6421     case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
6422     case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
6423     case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
6424     }
6425     IM_ASSERT(0);
6426     return "Unknown";
6427 }
6428 
IsWindowChildOf(ImGuiWindow * window,ImGuiWindow * potential_parent)6429 bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
6430 {
6431     if (window->RootWindow == potential_parent)
6432         return true;
6433     while (window != NULL)
6434     {
6435         if (window == potential_parent)
6436             return true;
6437         window = window->ParentWindow;
6438     }
6439     return false;
6440 }
6441 
IsWindowHovered(ImGuiHoveredFlags flags)6442 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
6443 {
6444     IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0);   // Flags not supported by this function
6445     ImGuiContext& g = *GImGui;
6446 
6447     if (flags & ImGuiHoveredFlags_AnyWindow)
6448     {
6449         if (g.HoveredWindow == NULL)
6450             return false;
6451     }
6452     else
6453     {
6454         switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
6455         {
6456         case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
6457             if (g.HoveredRootWindow != g.CurrentWindow->RootWindow)
6458                 return false;
6459             break;
6460         case ImGuiHoveredFlags_RootWindow:
6461             if (g.HoveredWindow != g.CurrentWindow->RootWindow)
6462                 return false;
6463             break;
6464         case ImGuiHoveredFlags_ChildWindows:
6465             if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow))
6466                 return false;
6467             break;
6468         default:
6469             if (g.HoveredWindow != g.CurrentWindow)
6470                 return false;
6471             break;
6472         }
6473     }
6474 
6475     if (!IsWindowContentHoverable(g.HoveredWindow, flags))
6476         return false;
6477     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
6478         if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
6479             return false;
6480     return true;
6481 }
6482 
IsWindowFocused(ImGuiFocusedFlags flags)6483 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
6484 {
6485     ImGuiContext& g = *GImGui;
6486 
6487     if (flags & ImGuiFocusedFlags_AnyWindow)
6488         return g.NavWindow != NULL;
6489 
6490     IM_ASSERT(g.CurrentWindow);     // Not inside a Begin()/End()
6491     switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
6492     {
6493     case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
6494         return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
6495     case ImGuiFocusedFlags_RootWindow:
6496         return g.NavWindow == g.CurrentWindow->RootWindow;
6497     case ImGuiFocusedFlags_ChildWindows:
6498         return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
6499     default:
6500         return g.NavWindow == g.CurrentWindow;
6501     }
6502 }
6503 
6504 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
6505 // Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmaticaly.
6506 // If you want a window to never be focused, you may use the e.g. NoInputs flag.
IsWindowNavFocusable(ImGuiWindow * window)6507 bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
6508 {
6509     return window->Active && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
6510 }
6511 
GetWindowWidth()6512 float ImGui::GetWindowWidth()
6513 {
6514     ImGuiWindow* window = GImGui->CurrentWindow;
6515     return window->Size.x;
6516 }
6517 
GetWindowHeight()6518 float ImGui::GetWindowHeight()
6519 {
6520     ImGuiWindow* window = GImGui->CurrentWindow;
6521     return window->Size.y;
6522 }
6523 
GetWindowPos()6524 ImVec2 ImGui::GetWindowPos()
6525 {
6526     ImGuiContext& g = *GImGui;
6527     ImGuiWindow* window = g.CurrentWindow;
6528     return window->Pos;
6529 }
6530 
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiCond cond)6531 void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
6532 {
6533     // Test condition (NB: bit 0 is always true) and clear flags for next time
6534     if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
6535         return;
6536 
6537     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6538     window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6539     window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
6540 
6541     // Set
6542     const ImVec2 old_pos = window->Pos;
6543     window->Pos = ImFloor(pos);
6544     ImVec2 offset = window->Pos - old_pos;
6545     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
6546     window->DC.CursorMaxPos += offset;      // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.
6547     window->DC.CursorStartPos += offset;
6548 }
6549 
SetWindowPos(const ImVec2 & pos,ImGuiCond cond)6550 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
6551 {
6552     ImGuiWindow* window = GetCurrentWindowRead();
6553     SetWindowPos(window, pos, cond);
6554 }
6555 
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiCond cond)6556 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
6557 {
6558     if (ImGuiWindow* window = FindWindowByName(name))
6559         SetWindowPos(window, pos, cond);
6560 }
6561 
GetWindowSize()6562 ImVec2 ImGui::GetWindowSize()
6563 {
6564     ImGuiWindow* window = GetCurrentWindowRead();
6565     return window->Size;
6566 }
6567 
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiCond cond)6568 void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
6569 {
6570     // Test condition (NB: bit 0 is always true) and clear flags for next time
6571     if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
6572         return;
6573 
6574     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6575     window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6576 
6577     // Set
6578     if (size.x > 0.0f)
6579     {
6580         window->AutoFitFramesX = 0;
6581         window->SizeFull.x = IM_FLOOR(size.x);
6582     }
6583     else
6584     {
6585         window->AutoFitFramesX = 2;
6586         window->AutoFitOnlyGrows = false;
6587     }
6588     if (size.y > 0.0f)
6589     {
6590         window->AutoFitFramesY = 0;
6591         window->SizeFull.y = IM_FLOOR(size.y);
6592     }
6593     else
6594     {
6595         window->AutoFitFramesY = 2;
6596         window->AutoFitOnlyGrows = false;
6597     }
6598 }
6599 
SetWindowSize(const ImVec2 & size,ImGuiCond cond)6600 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
6601 {
6602     SetWindowSize(GImGui->CurrentWindow, size, cond);
6603 }
6604 
SetWindowSize(const char * name,const ImVec2 & size,ImGuiCond cond)6605 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
6606 {
6607     if (ImGuiWindow* window = FindWindowByName(name))
6608         SetWindowSize(window, size, cond);
6609 }
6610 
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiCond cond)6611 void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
6612 {
6613     // Test condition (NB: bit 0 is always true) and clear flags for next time
6614     if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
6615         return;
6616     window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6617 
6618     // Set
6619     window->Collapsed = collapsed;
6620 }
6621 
SetWindowCollapsed(bool collapsed,ImGuiCond cond)6622 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
6623 {
6624     SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
6625 }
6626 
IsWindowCollapsed()6627 bool ImGui::IsWindowCollapsed()
6628 {
6629     ImGuiWindow* window = GetCurrentWindowRead();
6630     return window->Collapsed;
6631 }
6632 
IsWindowAppearing()6633 bool ImGui::IsWindowAppearing()
6634 {
6635     ImGuiWindow* window = GetCurrentWindowRead();
6636     return window->Appearing;
6637 }
6638 
SetWindowCollapsed(const char * name,bool collapsed,ImGuiCond cond)6639 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
6640 {
6641     if (ImGuiWindow* window = FindWindowByName(name))
6642         SetWindowCollapsed(window, collapsed, cond);
6643 }
6644 
SetWindowFocus()6645 void ImGui::SetWindowFocus()
6646 {
6647     FocusWindow(GImGui->CurrentWindow);
6648 }
6649 
SetWindowFocus(const char * name)6650 void ImGui::SetWindowFocus(const char* name)
6651 {
6652     if (name)
6653     {
6654         if (ImGuiWindow* window = FindWindowByName(name))
6655             FocusWindow(window);
6656     }
6657     else
6658     {
6659         FocusWindow(NULL);
6660     }
6661 }
6662 
SetNextWindowPos(const ImVec2 & pos,ImGuiCond cond,const ImVec2 & pivot)6663 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
6664 {
6665     ImGuiContext& g = *GImGui;
6666     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6667     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos;
6668     g.NextWindowData.PosVal = pos;
6669     g.NextWindowData.PosPivotVal = pivot;
6670     g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
6671 }
6672 
SetNextWindowSize(const ImVec2 & size,ImGuiCond cond)6673 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
6674 {
6675     ImGuiContext& g = *GImGui;
6676     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6677     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize;
6678     g.NextWindowData.SizeVal = size;
6679     g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
6680 }
6681 
SetNextWindowSizeConstraints(const ImVec2 & size_min,const ImVec2 & size_max,ImGuiSizeCallback custom_callback,void * custom_callback_user_data)6682 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
6683 {
6684     ImGuiContext& g = *GImGui;
6685     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint;
6686     g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
6687     g.NextWindowData.SizeCallback = custom_callback;
6688     g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
6689 }
6690 
6691 // Content size = inner scrollable rectangle, padded with WindowPadding.
6692 // SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
SetNextWindowContentSize(const ImVec2 & size)6693 void ImGui::SetNextWindowContentSize(const ImVec2& size)
6694 {
6695     ImGuiContext& g = *GImGui;
6696     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize;
6697     g.NextWindowData.ContentSizeVal = size;
6698 }
6699 
SetNextWindowCollapsed(bool collapsed,ImGuiCond cond)6700 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
6701 {
6702     ImGuiContext& g = *GImGui;
6703     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6704     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed;
6705     g.NextWindowData.CollapsedVal = collapsed;
6706     g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
6707 }
6708 
SetNextWindowFocus()6709 void ImGui::SetNextWindowFocus()
6710 {
6711     ImGuiContext& g = *GImGui;
6712     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus;
6713 }
6714 
SetNextWindowBgAlpha(float alpha)6715 void ImGui::SetNextWindowBgAlpha(float alpha)
6716 {
6717     ImGuiContext& g = *GImGui;
6718     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha;
6719     g.NextWindowData.BgAlphaVal = alpha;
6720 }
6721 
6722 // FIXME: This is in window space (not screen space!). We should try to obsolete all those functions.
GetContentRegionMax()6723 ImVec2 ImGui::GetContentRegionMax()
6724 {
6725     ImGuiContext& g = *GImGui;
6726     ImGuiWindow* window = g.CurrentWindow;
6727     ImVec2 mx = window->ContentRegionRect.Max - window->Pos;
6728     if (window->DC.CurrentColumns)
6729         mx.x = window->WorkRect.Max.x - window->Pos.x;
6730     return mx;
6731 }
6732 
6733 // [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features.
GetContentRegionMaxAbs()6734 ImVec2 ImGui::GetContentRegionMaxAbs()
6735 {
6736     ImGuiContext& g = *GImGui;
6737     ImGuiWindow* window = g.CurrentWindow;
6738     ImVec2 mx = window->ContentRegionRect.Max;
6739     if (window->DC.CurrentColumns)
6740         mx.x = window->WorkRect.Max.x;
6741     return mx;
6742 }
6743 
GetContentRegionAvail()6744 ImVec2 ImGui::GetContentRegionAvail()
6745 {
6746     ImGuiWindow* window = GImGui->CurrentWindow;
6747     return GetContentRegionMaxAbs() - window->DC.CursorPos;
6748 }
6749 
6750 // In window space (not screen space!)
GetWindowContentRegionMin()6751 ImVec2 ImGui::GetWindowContentRegionMin()
6752 {
6753     ImGuiWindow* window = GImGui->CurrentWindow;
6754     return window->ContentRegionRect.Min - window->Pos;
6755 }
6756 
GetWindowContentRegionMax()6757 ImVec2 ImGui::GetWindowContentRegionMax()
6758 {
6759     ImGuiWindow* window = GImGui->CurrentWindow;
6760     return window->ContentRegionRect.Max - window->Pos;
6761 }
6762 
GetWindowContentRegionWidth()6763 float ImGui::GetWindowContentRegionWidth()
6764 {
6765     ImGuiWindow* window = GImGui->CurrentWindow;
6766     return window->ContentRegionRect.GetWidth();
6767 }
6768 
GetTextLineHeight()6769 float ImGui::GetTextLineHeight()
6770 {
6771     ImGuiContext& g = *GImGui;
6772     return g.FontSize;
6773 }
6774 
GetTextLineHeightWithSpacing()6775 float ImGui::GetTextLineHeightWithSpacing()
6776 {
6777     ImGuiContext& g = *GImGui;
6778     return g.FontSize + g.Style.ItemSpacing.y;
6779 }
6780 
GetFrameHeight()6781 float ImGui::GetFrameHeight()
6782 {
6783     ImGuiContext& g = *GImGui;
6784     return g.FontSize + g.Style.FramePadding.y * 2.0f;
6785 }
6786 
GetFrameHeightWithSpacing()6787 float ImGui::GetFrameHeightWithSpacing()
6788 {
6789     ImGuiContext& g = *GImGui;
6790     return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
6791 }
6792 
GetWindowDrawList()6793 ImDrawList* ImGui::GetWindowDrawList()
6794 {
6795     ImGuiWindow* window = GetCurrentWindow();
6796     return window->DrawList;
6797 }
6798 
GetFont()6799 ImFont* ImGui::GetFont()
6800 {
6801     return GImGui->Font;
6802 }
6803 
GetFontSize()6804 float ImGui::GetFontSize()
6805 {
6806     return GImGui->FontSize;
6807 }
6808 
GetFontTexUvWhitePixel()6809 ImVec2 ImGui::GetFontTexUvWhitePixel()
6810 {
6811     return GImGui->DrawListSharedData.TexUvWhitePixel;
6812 }
6813 
SetWindowFontScale(float scale)6814 void ImGui::SetWindowFontScale(float scale)
6815 {
6816     ImGuiContext& g = *GImGui;
6817     ImGuiWindow* window = GetCurrentWindow();
6818     window->FontWindowScale = scale;
6819     g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
6820 }
6821 
6822 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
6823 // 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()6824 ImVec2 ImGui::GetCursorPos()
6825 {
6826     ImGuiWindow* window = GetCurrentWindowRead();
6827     return window->DC.CursorPos - window->Pos + window->Scroll;
6828 }
6829 
GetCursorPosX()6830 float ImGui::GetCursorPosX()
6831 {
6832     ImGuiWindow* window = GetCurrentWindowRead();
6833     return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
6834 }
6835 
GetCursorPosY()6836 float ImGui::GetCursorPosY()
6837 {
6838     ImGuiWindow* window = GetCurrentWindowRead();
6839     return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
6840 }
6841 
SetCursorPos(const ImVec2 & local_pos)6842 void ImGui::SetCursorPos(const ImVec2& local_pos)
6843 {
6844     ImGuiWindow* window = GetCurrentWindow();
6845     window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
6846     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
6847 }
6848 
SetCursorPosX(float x)6849 void ImGui::SetCursorPosX(float x)
6850 {
6851     ImGuiWindow* window = GetCurrentWindow();
6852     window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
6853     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
6854 }
6855 
SetCursorPosY(float y)6856 void ImGui::SetCursorPosY(float y)
6857 {
6858     ImGuiWindow* window = GetCurrentWindow();
6859     window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
6860     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
6861 }
6862 
GetCursorStartPos()6863 ImVec2 ImGui::GetCursorStartPos()
6864 {
6865     ImGuiWindow* window = GetCurrentWindowRead();
6866     return window->DC.CursorStartPos - window->Pos;
6867 }
6868 
GetCursorScreenPos()6869 ImVec2 ImGui::GetCursorScreenPos()
6870 {
6871     ImGuiWindow* window = GetCurrentWindowRead();
6872     return window->DC.CursorPos;
6873 }
6874 
SetCursorScreenPos(const ImVec2 & pos)6875 void ImGui::SetCursorScreenPos(const ImVec2& pos)
6876 {
6877     ImGuiWindow* window = GetCurrentWindow();
6878     window->DC.CursorPos = pos;
6879     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
6880 }
6881 
ActivateItem(ImGuiID id)6882 void ImGui::ActivateItem(ImGuiID id)
6883 {
6884     ImGuiContext& g = *GImGui;
6885     g.NavNextActivateId = id;
6886 }
6887 
PushFocusScope(ImGuiID id)6888 void ImGui::PushFocusScope(ImGuiID id)
6889 {
6890     ImGuiContext& g = *GImGui;
6891     ImGuiWindow* window = g.CurrentWindow;
6892     window->IDStack.push_back(window->DC.NavFocusScopeIdCurrent);
6893     window->DC.NavFocusScopeIdCurrent = id;
6894 }
6895 
PopFocusScope()6896 void ImGui::PopFocusScope()
6897 {
6898     ImGuiContext& g = *GImGui;
6899     ImGuiWindow* window = g.CurrentWindow;
6900     window->DC.NavFocusScopeIdCurrent = window->IDStack.back();
6901     window->IDStack.pop_back();
6902 }
6903 
SetKeyboardFocusHere(int offset)6904 void ImGui::SetKeyboardFocusHere(int offset)
6905 {
6906     IM_ASSERT(offset >= -1);    // -1 is allowed but not below
6907     ImGuiContext& g = *GImGui;
6908     ImGuiWindow* window = g.CurrentWindow;
6909     g.FocusRequestNextWindow = window;
6910     g.FocusRequestNextCounterRegular = window->DC.FocusCounterRegular + 1 + offset;
6911     g.FocusRequestNextCounterTabStop = INT_MAX;
6912 }
6913 
SetItemDefaultFocus()6914 void ImGui::SetItemDefaultFocus()
6915 {
6916     ImGuiContext& g = *GImGui;
6917     ImGuiWindow* window = g.CurrentWindow;
6918     if (!window->Appearing)
6919         return;
6920     if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent)
6921     {
6922         g.NavInitRequest = false;
6923         g.NavInitResultId = g.NavWindow->DC.LastItemId;
6924         g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos);
6925         NavUpdateAnyRequestFlag();
6926         if (!IsItemVisible())
6927             SetScrollHereY();
6928     }
6929 }
6930 
SetStateStorage(ImGuiStorage * tree)6931 void ImGui::SetStateStorage(ImGuiStorage* tree)
6932 {
6933     ImGuiWindow* window = GImGui->CurrentWindow;
6934     window->DC.StateStorage = tree ? tree : &window->StateStorage;
6935 }
6936 
GetStateStorage()6937 ImGuiStorage* ImGui::GetStateStorage()
6938 {
6939     ImGuiWindow* window = GImGui->CurrentWindow;
6940     return window->DC.StateStorage;
6941 }
6942 
PushID(const char * str_id)6943 void ImGui::PushID(const char* str_id)
6944 {
6945     ImGuiWindow* window = GImGui->CurrentWindow;
6946     window->IDStack.push_back(window->GetIDNoKeepAlive(str_id));
6947 }
6948 
PushID(const char * str_id_begin,const char * str_id_end)6949 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
6950 {
6951     ImGuiWindow* window = GImGui->CurrentWindow;
6952     window->IDStack.push_back(window->GetIDNoKeepAlive(str_id_begin, str_id_end));
6953 }
6954 
PushID(const void * ptr_id)6955 void ImGui::PushID(const void* ptr_id)
6956 {
6957     ImGuiWindow* window = GImGui->CurrentWindow;
6958     window->IDStack.push_back(window->GetIDNoKeepAlive(ptr_id));
6959 }
6960 
PushID(int int_id)6961 void ImGui::PushID(int int_id)
6962 {
6963     ImGuiWindow* window = GImGui->CurrentWindow;
6964     window->IDStack.push_back(window->GetIDNoKeepAlive(int_id));
6965 }
6966 
6967 // Push a given id value ignoring the ID stack as a seed.
PushOverrideID(ImGuiID id)6968 void ImGui::PushOverrideID(ImGuiID id)
6969 {
6970     ImGuiWindow* window = GImGui->CurrentWindow;
6971     window->IDStack.push_back(id);
6972 }
6973 
PopID()6974 void ImGui::PopID()
6975 {
6976     ImGuiWindow* window = GImGui->CurrentWindow;
6977     window->IDStack.pop_back();
6978 }
6979 
GetID(const char * str_id)6980 ImGuiID ImGui::GetID(const char* str_id)
6981 {
6982     ImGuiWindow* window = GImGui->CurrentWindow;
6983     return window->GetID(str_id);
6984 }
6985 
GetID(const char * str_id_begin,const char * str_id_end)6986 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
6987 {
6988     ImGuiWindow* window = GImGui->CurrentWindow;
6989     return window->GetID(str_id_begin, str_id_end);
6990 }
6991 
GetID(const void * ptr_id)6992 ImGuiID ImGui::GetID(const void* ptr_id)
6993 {
6994     ImGuiWindow* window = GImGui->CurrentWindow;
6995     return window->GetID(ptr_id);
6996 }
6997 
IsRectVisible(const ImVec2 & size)6998 bool ImGui::IsRectVisible(const ImVec2& size)
6999 {
7000     ImGuiWindow* window = GImGui->CurrentWindow;
7001     return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
7002 }
7003 
IsRectVisible(const ImVec2 & rect_min,const ImVec2 & rect_max)7004 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
7005 {
7006     ImGuiWindow* window = GImGui->CurrentWindow;
7007     return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
7008 }
7009 
7010 // Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.)
BeginGroup()7011 void ImGui::BeginGroup()
7012 {
7013     ImGuiContext& g = *GImGui;
7014     ImGuiWindow* window = GetCurrentWindow();
7015 
7016     window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
7017     ImGuiGroupData& group_data = window->DC.GroupStack.back();
7018     group_data.BackupCursorPos = window->DC.CursorPos;
7019     group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
7020     group_data.BackupIndent = window->DC.Indent;
7021     group_data.BackupGroupOffset = window->DC.GroupOffset;
7022     group_data.BackupCurrLineSize = window->DC.CurrLineSize;
7023     group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;
7024     group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
7025     group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
7026     group_data.EmitItem = true;
7027 
7028     window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
7029     window->DC.Indent = window->DC.GroupOffset;
7030     window->DC.CursorMaxPos = window->DC.CursorPos;
7031     window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
7032     if (g.LogEnabled)
7033         g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return
7034 }
7035 
EndGroup()7036 void ImGui::EndGroup()
7037 {
7038     ImGuiContext& g = *GImGui;
7039     ImGuiWindow* window = GetCurrentWindow();
7040     IM_ASSERT(!window->DC.GroupStack.empty());  // Mismatched BeginGroup()/EndGroup() calls
7041 
7042     ImGuiGroupData& group_data = window->DC.GroupStack.back();
7043 
7044     ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos));
7045 
7046     window->DC.CursorPos = group_data.BackupCursorPos;
7047     window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
7048     window->DC.Indent = group_data.BackupIndent;
7049     window->DC.GroupOffset = group_data.BackupGroupOffset;
7050     window->DC.CurrLineSize = group_data.BackupCurrLineSize;
7051     window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;
7052     if (g.LogEnabled)
7053         g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return
7054 
7055     if (!group_data.EmitItem)
7056     {
7057         window->DC.GroupStack.pop_back();
7058         return;
7059     }
7060 
7061     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.
7062     ItemSize(group_bb.GetSize());
7063     ItemAdd(group_bb, 0);
7064 
7065     // 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.
7066     // 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.
7067     // Also if you grep for LastItemId you'll notice it is only used in that context.
7068     // (The tests not symmetrical because ActiveIdIsAlive is an ID itself, in order to be able to handle ActiveId being overwritten during the frame.)
7069     const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId;
7070     const bool group_contains_prev_active_id = !group_data.BackupActiveIdPreviousFrameIsAlive && g.ActiveIdPreviousFrameIsAlive;
7071     if (group_contains_curr_active_id)
7072         window->DC.LastItemId = g.ActiveId;
7073     else if (group_contains_prev_active_id)
7074         window->DC.LastItemId = g.ActiveIdPreviousFrame;
7075     window->DC.LastItemRect = group_bb;
7076 
7077     // Forward Edited flag
7078     if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame)
7079         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
7080 
7081     // Forward Deactivated flag
7082     window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDeactivated;
7083     if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame)
7084         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Deactivated;
7085 
7086     window->DC.GroupStack.pop_back();
7087     //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255));   // [Debug]
7088 }
7089 
7090 // Gets back to previous line and continue with horizontal layout
7091 //      offset_from_start_x == 0 : follow right after previous item
7092 //      offset_from_start_x != 0 : align to specified x position (relative to window/group left)
7093 //      spacing_w < 0            : use default spacing if pos_x == 0, no spacing if pos_x != 0
7094 //      spacing_w >= 0           : enforce spacing amount
SameLine(float offset_from_start_x,float spacing_w)7095 void ImGui::SameLine(float offset_from_start_x, float spacing_w)
7096 {
7097     ImGuiWindow* window = GetCurrentWindow();
7098     if (window->SkipItems)
7099         return;
7100 
7101     ImGuiContext& g = *GImGui;
7102     if (offset_from_start_x != 0.0f)
7103     {
7104         if (spacing_w < 0.0f) spacing_w = 0.0f;
7105         window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
7106         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
7107     }
7108     else
7109     {
7110         if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
7111         window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
7112         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
7113     }
7114     window->DC.CurrLineSize = window->DC.PrevLineSize;
7115     window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
7116 }
7117 
Indent(float indent_w)7118 void ImGui::Indent(float indent_w)
7119 {
7120     ImGuiContext& g = *GImGui;
7121     ImGuiWindow* window = GetCurrentWindow();
7122     window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
7123     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
7124 }
7125 
Unindent(float indent_w)7126 void ImGui::Unindent(float indent_w)
7127 {
7128     ImGuiContext& g = *GImGui;
7129     ImGuiWindow* window = GetCurrentWindow();
7130     window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
7131     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
7132 }
7133 
7134 
7135 //-----------------------------------------------------------------------------
7136 // [SECTION] ERROR CHECKING
7137 //-----------------------------------------------------------------------------
7138 
ErrorCheckEndFrame()7139 static void ImGui::ErrorCheckEndFrame()
7140 {
7141     // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you
7142     // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API).
7143     ImGuiContext& g = *GImGui;
7144     if (g.CurrentWindowStack.Size != 1)
7145     {
7146         if (g.CurrentWindowStack.Size > 1)
7147         {
7148             IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?");
7149             while (g.CurrentWindowStack.Size > 1)
7150                 End();
7151         }
7152         else
7153         {
7154             IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?");
7155         }
7156     }
7157 
7158 }
7159 
7160 // Save and compare stack sizes on Begin()/End() to detect usage errors
7161 // Begin() calls this with write=true
7162 // End() calls this with write=false
ErrorCheckBeginEndCompareStacksSize(ImGuiWindow * window,bool write)7163 static void ImGui::ErrorCheckBeginEndCompareStacksSize(ImGuiWindow* window, bool write)
7164 {
7165     ImGuiContext& g = *GImGui;
7166     short* p = &window->DC.StackSizesBackup[0];
7167 
7168     // Window stacks
7169     // NOT checking: DC.ItemWidth, DC.AllowKeyboardFocus, DC.ButtonRepeat, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin)
7170     { int n = window->IDStack.Size;       if (write) *p = (short)n; else IM_ASSERT(*p == n && "PushID/PopID or TreeNode/TreePop Mismatch!");   p++; }    // Too few or too many PopID()/TreePop()
7171     { int n = window->DC.GroupStack.Size; if (write) *p = (short)n; else IM_ASSERT(*p == n && "BeginGroup/EndGroup Mismatch!");                p++; }    // Too few or too many EndGroup()
7172 
7173     // Global stacks
7174     // 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.
7175     { int n = g.BeginPopupStack.Size;     if (write) *p = (short)n; else IM_ASSERT(*p == n && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch!"); p++; }// Too few or too many EndMenu()/EndPopup()
7176     { int n = g.ColorModifiers.Size;      if (write) *p = (short)n; else IM_ASSERT(*p >= n && "PushStyleColor/PopStyleColor Mismatch!");       p++; }    // Too few or too many PopStyleColor()
7177     { int n = g.StyleModifiers.Size;      if (write) *p = (short)n; else IM_ASSERT(*p >= n && "PushStyleVar/PopStyleVar Mismatch!");           p++; }    // Too few or too many PopStyleVar()
7178     { int n = g.FontStack.Size;           if (write) *p = (short)n; else IM_ASSERT(*p >= n && "PushFont/PopFont Mismatch!");                   p++; }    // Too few or too many PopFont()
7179     IM_ASSERT(p == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
7180 }
7181 
7182 
7183 //-----------------------------------------------------------------------------
7184 // [SECTION] SCROLLING
7185 //-----------------------------------------------------------------------------
7186 
CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow * window,bool snap_on_edges)7187 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges)
7188 {
7189     ImGuiContext& g = *GImGui;
7190     ImVec2 scroll = window->Scroll;
7191     if (window->ScrollTarget.x < FLT_MAX)
7192     {
7193         float cr_x = window->ScrollTargetCenterRatio.x;
7194         float target_x = window->ScrollTarget.x;
7195         if (snap_on_edges && cr_x <= 0.0f && target_x <= window->WindowPadding.x)
7196             target_x = 0.0f;
7197         else if (snap_on_edges && cr_x >= 1.0f && target_x >= window->ContentSize.x + window->WindowPadding.x + g.Style.ItemSpacing.x)
7198             target_x = window->ContentSize.x + window->WindowPadding.x * 2.0f;
7199         scroll.x = target_x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x);
7200     }
7201     if (window->ScrollTarget.y < FLT_MAX)
7202     {
7203         // 'snap_on_edges' allows for a discontinuity at the edge of scrolling limits to take account of WindowPadding so that scrolling to make the last item visible scroll far enough to see the padding.
7204         float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
7205         float cr_y = window->ScrollTargetCenterRatio.y;
7206         float target_y = window->ScrollTarget.y;
7207         if (snap_on_edges && cr_y <= 0.0f && target_y <= window->WindowPadding.y)
7208             target_y = 0.0f;
7209         if (snap_on_edges && cr_y >= 1.0f && target_y >= window->ContentSize.y + window->WindowPadding.y + g.Style.ItemSpacing.y)
7210             target_y = window->ContentSize.y + window->WindowPadding.y * 2.0f;
7211         scroll.y = target_y - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y - decoration_up_height);
7212     }
7213     scroll = ImMax(scroll, ImVec2(0.0f, 0.0f));
7214     if (!window->Collapsed && !window->SkipItems)
7215     {
7216         scroll.x = ImMin(scroll.x, window->ScrollMax.x);
7217         scroll.y = ImMin(scroll.y, window->ScrollMax.y);
7218     }
7219     return scroll;
7220 }
7221 
7222 // Scroll to keep newly navigated item fully into view
ScrollToBringRectIntoView(ImGuiWindow * window,const ImRect & item_rect)7223 ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect)
7224 {
7225     ImGuiContext& g = *GImGui;
7226     ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));
7227     //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
7228 
7229     ImVec2 delta_scroll;
7230     if (!window_rect.Contains(item_rect))
7231     {
7232         if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x)
7233             SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x + g.Style.ItemSpacing.x, 0.0f);
7234         else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x)
7235             SetScrollFromPosX(window, item_rect.Max.x - window->Pos.x + g.Style.ItemSpacing.x, 1.0f);
7236         if (item_rect.Min.y < window_rect.Min.y)
7237             SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y - g.Style.ItemSpacing.y, 0.0f);
7238         else if (item_rect.Max.y >= window_rect.Max.y)
7239             SetScrollFromPosY(window, item_rect.Max.y - window->Pos.y + g.Style.ItemSpacing.y, 1.0f);
7240 
7241         ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window, false);
7242         delta_scroll = next_scroll - window->Scroll;
7243     }
7244 
7245     // Also scroll parent window to keep us into view if necessary
7246     if (window->Flags & ImGuiWindowFlags_ChildWindow)
7247         delta_scroll += ScrollToBringRectIntoView(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll));
7248 
7249     return delta_scroll;
7250 }
7251 
GetScrollX()7252 float ImGui::GetScrollX()
7253 {
7254     ImGuiWindow* window = GImGui->CurrentWindow;
7255     return window->Scroll.x;
7256 }
7257 
GetScrollY()7258 float ImGui::GetScrollY()
7259 {
7260     ImGuiWindow* window = GImGui->CurrentWindow;
7261     return window->Scroll.y;
7262 }
7263 
GetScrollMaxX()7264 float ImGui::GetScrollMaxX()
7265 {
7266     ImGuiWindow* window = GImGui->CurrentWindow;
7267     return window->ScrollMax.x;
7268 }
7269 
GetScrollMaxY()7270 float ImGui::GetScrollMaxY()
7271 {
7272     ImGuiWindow* window = GImGui->CurrentWindow;
7273     return window->ScrollMax.y;
7274 }
7275 
SetScrollX(float scroll_x)7276 void ImGui::SetScrollX(float scroll_x)
7277 {
7278     ImGuiWindow* window = GetCurrentWindow();
7279     window->ScrollTarget.x = scroll_x;
7280     window->ScrollTargetCenterRatio.x = 0.0f;
7281 }
7282 
SetScrollY(float scroll_y)7283 void ImGui::SetScrollY(float scroll_y)
7284 {
7285     ImGuiWindow* window = GetCurrentWindow();
7286     window->ScrollTarget.y = scroll_y;
7287     window->ScrollTargetCenterRatio.y = 0.0f;
7288 }
7289 
SetScrollX(ImGuiWindow * window,float new_scroll_x)7290 void ImGui::SetScrollX(ImGuiWindow* window, float new_scroll_x)
7291 {
7292     window->ScrollTarget.x = new_scroll_x;
7293     window->ScrollTargetCenterRatio.x = 0.0f;
7294 }
7295 
SetScrollY(ImGuiWindow * window,float new_scroll_y)7296 void ImGui::SetScrollY(ImGuiWindow* window, float new_scroll_y)
7297 {
7298     window->ScrollTarget.y = new_scroll_y;
7299     window->ScrollTargetCenterRatio.y = 0.0f;
7300 }
7301 
7302 
SetScrollFromPosX(ImGuiWindow * window,float local_x,float center_x_ratio)7303 void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
7304 {
7305     // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
7306     IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);
7307     window->ScrollTarget.x = IM_FLOOR(local_x + window->Scroll.x);
7308     window->ScrollTargetCenterRatio.x = center_x_ratio;
7309 }
7310 
SetScrollFromPosY(ImGuiWindow * window,float local_y,float center_y_ratio)7311 void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
7312 {
7313     // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
7314     IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
7315     const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
7316     local_y -= decoration_up_height;
7317     window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y);
7318     window->ScrollTargetCenterRatio.y = center_y_ratio;
7319 }
7320 
SetScrollFromPosX(float local_x,float center_x_ratio)7321 void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)
7322 {
7323     ImGuiContext& g = *GImGui;
7324     SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio);
7325 }
7326 
SetScrollFromPosY(float local_y,float center_y_ratio)7327 void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
7328 {
7329     ImGuiContext& g = *GImGui;
7330     SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio);
7331 }
7332 
7333 // 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)7334 void ImGui::SetScrollHereX(float center_x_ratio)
7335 {
7336     ImGuiContext& g = *GImGui;
7337     ImGuiWindow* window = g.CurrentWindow;
7338     float target_x = window->DC.LastItemRect.Min.x - window->Pos.x; // Left of last item, in window space
7339     float last_item_width = window->DC.LastItemRect.GetWidth();
7340     target_x += (last_item_width * center_x_ratio) + (g.Style.ItemSpacing.x * (center_x_ratio - 0.5f) * 2.0f); // Precisely aim before, in the middle or after the last item.
7341     SetScrollFromPosX(target_x, center_x_ratio);
7342 }
7343 
7344 // 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)7345 void ImGui::SetScrollHereY(float center_y_ratio)
7346 {
7347     ImGuiContext& g = *GImGui;
7348     ImGuiWindow* window = g.CurrentWindow;
7349     float target_y = window->DC.CursorPosPrevLine.y - window->Pos.y; // Top of last item, in window space
7350     target_y += (window->DC.PrevLineSize.y * center_y_ratio) + (g.Style.ItemSpacing.y * (center_y_ratio - 0.5f) * 2.0f); // Precisely aim above, in the middle or below the last line.
7351     SetScrollFromPosY(target_y, center_y_ratio);
7352 }
7353 
7354 //-----------------------------------------------------------------------------
7355 // [SECTION] TOOLTIPS
7356 //-----------------------------------------------------------------------------
7357 
BeginTooltip()7358 void ImGui::BeginTooltip()
7359 {
7360     BeginTooltipEx(ImGuiWindowFlags_None, ImGuiTooltipFlags_None);
7361 }
7362 
BeginTooltipEx(ImGuiWindowFlags extra_flags,ImGuiTooltipFlags tooltip_flags)7363 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags)
7364 {
7365     ImGuiContext& g = *GImGui;
7366 
7367     if (g.DragDropWithinSourceOrTarget)
7368     {
7369         // 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)
7370         // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
7371         // 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.
7372         //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
7373         ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale);
7374         SetNextWindowPos(tooltip_pos);
7375         SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
7376         //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
7377         tooltip_flags |= ImGuiTooltipFlags_OverridePreviousTooltip;
7378     }
7379 
7380     char window_name[16];
7381     ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
7382     if (tooltip_flags & ImGuiTooltipFlags_OverridePreviousTooltip)
7383         if (ImGuiWindow* window = FindWindowByName(window_name))
7384             if (window->Active)
7385             {
7386                 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
7387                 window->Hidden = true;
7388                 window->HiddenFramesCanSkipItems = 1;
7389                 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
7390             }
7391     ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
7392     Begin(window_name, NULL, flags | extra_flags);
7393 }
7394 
EndTooltip()7395 void ImGui::EndTooltip()
7396 {
7397     IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip);   // Mismatched BeginTooltip()/EndTooltip() calls
7398     End();
7399 }
7400 
SetTooltipV(const char * fmt,va_list args)7401 void ImGui::SetTooltipV(const char* fmt, va_list args)
7402 {
7403     BeginTooltipEx(0, ImGuiTooltipFlags_OverridePreviousTooltip);
7404     TextV(fmt, args);
7405     EndTooltip();
7406 }
7407 
SetTooltip(const char * fmt,...)7408 void ImGui::SetTooltip(const char* fmt, ...)
7409 {
7410     va_list args;
7411     va_start(args, fmt);
7412     SetTooltipV(fmt, args);
7413     va_end(args);
7414 }
7415 
7416 //-----------------------------------------------------------------------------
7417 // [SECTION] POPUPS
7418 //-----------------------------------------------------------------------------
7419 
IsPopupOpen(ImGuiID id)7420 bool ImGui::IsPopupOpen(ImGuiID id)
7421 {
7422     ImGuiContext& g = *GImGui;
7423     return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
7424 }
7425 
IsPopupOpen(const char * str_id)7426 bool ImGui::IsPopupOpen(const char* str_id)
7427 {
7428     ImGuiContext& g = *GImGui;
7429     return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id);
7430 }
7431 
GetTopMostPopupModal()7432 ImGuiWindow* ImGui::GetTopMostPopupModal()
7433 {
7434     ImGuiContext& g = *GImGui;
7435     for (int n = g.OpenPopupStack.Size-1; n >= 0; n--)
7436         if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
7437             if (popup->Flags & ImGuiWindowFlags_Modal)
7438                 return popup;
7439     return NULL;
7440 }
7441 
OpenPopup(const char * str_id)7442 void ImGui::OpenPopup(const char* str_id)
7443 {
7444     ImGuiContext& g = *GImGui;
7445     OpenPopupEx(g.CurrentWindow->GetID(str_id));
7446 }
7447 
7448 // Mark popup as open (toggle toward open state).
7449 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
7450 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
7451 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
OpenPopupEx(ImGuiID id)7452 void ImGui::OpenPopupEx(ImGuiID id)
7453 {
7454     ImGuiContext& g = *GImGui;
7455     ImGuiWindow* parent_window = g.CurrentWindow;
7456     int current_stack_size = g.BeginPopupStack.Size;
7457     ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
7458     popup_ref.PopupId = id;
7459     popup_ref.Window = NULL;
7460     popup_ref.SourceWindow = g.NavWindow;
7461     popup_ref.OpenFrameCount = g.FrameCount;
7462     popup_ref.OpenParentId = parent_window->IDStack.back();
7463     popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
7464     popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
7465 
7466     //IMGUI_DEBUG_LOG("OpenPopupEx(0x%08X)\n", g.FrameCount, id);
7467     if (g.OpenPopupStack.Size < current_stack_size + 1)
7468     {
7469         g.OpenPopupStack.push_back(popup_ref);
7470     }
7471     else
7472     {
7473         // 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
7474         // 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
7475         // 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.
7476         if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
7477         {
7478             g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
7479         }
7480         else
7481         {
7482             // Close child popups if any, then flag popup for open/reopen
7483             g.OpenPopupStack.resize(current_stack_size + 1);
7484             g.OpenPopupStack[current_stack_size] = popup_ref;
7485         }
7486 
7487         // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
7488         // This is equivalent to what ClosePopupToLevel() does.
7489         //if (g.OpenPopupStack[current_stack_size].PopupId == id)
7490         //    FocusWindow(parent_window);
7491     }
7492 }
7493 
ClosePopupsOverWindow(ImGuiWindow * ref_window,bool restore_focus_to_window_under_popup)7494 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
7495 {
7496     ImGuiContext& g = *GImGui;
7497     if (g.OpenPopupStack.empty())
7498         return;
7499 
7500     // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
7501     // Don't close our own child popup windows.
7502     int popup_count_to_keep = 0;
7503     if (ref_window)
7504     {
7505         // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
7506         for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
7507         {
7508             ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];
7509             if (!popup.Window)
7510                 continue;
7511             IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
7512             if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
7513                 continue;
7514 
7515             // Trim the stack when popups are not direct descendant of the reference window (the reference window is often the NavWindow)
7516             bool popup_or_descendent_is_ref_window = false;
7517             for (int m = popup_count_to_keep; m < g.OpenPopupStack.Size && !popup_or_descendent_is_ref_window; m++)
7518                 if (ImGuiWindow* popup_window = g.OpenPopupStack[m].Window)
7519                     if (popup_window->RootWindow == ref_window->RootWindow)
7520                         popup_or_descendent_is_ref_window = true;
7521             if (!popup_or_descendent_is_ref_window)
7522                 break;
7523         }
7524     }
7525     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
7526     {
7527         //IMGUI_DEBUG_LOG("ClosePopupsOverWindow(%s) -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep);
7528         ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup);
7529     }
7530 }
7531 
ClosePopupToLevel(int remaining,bool restore_focus_to_window_under_popup)7532 void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
7533 {
7534     ImGuiContext& g = *GImGui;
7535     IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
7536     ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow;
7537     ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window;
7538     g.OpenPopupStack.resize(remaining);
7539 
7540     if (restore_focus_to_window_under_popup)
7541     {
7542         if (focus_window && !focus_window->WasActive && popup_window)
7543         {
7544             // Fallback
7545             FocusTopMostWindowUnderOne(popup_window, NULL);
7546         }
7547         else
7548         {
7549             if (g.NavLayer == 0 && focus_window)
7550                 focus_window = NavRestoreLastChildNavWindow(focus_window);
7551             FocusWindow(focus_window);
7552         }
7553     }
7554 }
7555 
7556 // Close the popup we have begin-ed into.
CloseCurrentPopup()7557 void ImGui::CloseCurrentPopup()
7558 {
7559     ImGuiContext& g = *GImGui;
7560     int popup_idx = g.BeginPopupStack.Size - 1;
7561     if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
7562         return;
7563 
7564     // Closing a menu closes its top-most parent popup (unless a modal)
7565     while (popup_idx > 0)
7566     {
7567         ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;
7568         ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
7569         bool close_parent = false;
7570         if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
7571             if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal))
7572                 close_parent = true;
7573         if (!close_parent)
7574             break;
7575         popup_idx--;
7576     }
7577     //IMGUI_DEBUG_LOG("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx);
7578     ClosePopupToLevel(popup_idx, true);
7579 
7580     // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
7581     // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
7582     // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
7583     if (ImGuiWindow* window = g.NavWindow)
7584         window->DC.NavHideHighlightOneFrame = true;
7585 }
7586 
BeginPopupEx(ImGuiID id,ImGuiWindowFlags flags)7587 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags)
7588 {
7589     ImGuiContext& g = *GImGui;
7590     if (!IsPopupOpen(id))
7591     {
7592         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
7593         return false;
7594     }
7595 
7596     char name[20];
7597     if (flags & ImGuiWindowFlags_ChildMenu)
7598         ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth
7599     else
7600         ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
7601 
7602     flags |= ImGuiWindowFlags_Popup;
7603     bool is_open = Begin(name, NULL, flags);
7604     if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
7605         EndPopup();
7606 
7607     return is_open;
7608 }
7609 
BeginPopup(const char * str_id,ImGuiWindowFlags flags)7610 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
7611 {
7612     ImGuiContext& g = *GImGui;
7613     if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
7614     {
7615         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
7616         return false;
7617     }
7618     flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
7619     return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags);
7620 }
7621 
7622 // If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
7623 // 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)7624 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
7625 {
7626     ImGuiContext& g = *GImGui;
7627     ImGuiWindow* window = g.CurrentWindow;
7628     const ImGuiID id = window->GetID(name);
7629     if (!IsPopupOpen(id))
7630     {
7631         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
7632         return false;
7633     }
7634 
7635     // Center modal windows by default
7636     // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
7637     if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
7638         SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
7639 
7640     flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings;
7641     const bool is_open = Begin(name, p_open, flags);
7642     if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
7643     {
7644         EndPopup();
7645         if (is_open)
7646             ClosePopupToLevel(g.BeginPopupStack.Size, true);
7647         return false;
7648     }
7649     return is_open;
7650 }
7651 
EndPopup()7652 void ImGui::EndPopup()
7653 {
7654     ImGuiContext& g = *GImGui;
7655     ImGuiWindow* window = g.CurrentWindow;
7656     IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup);  // Mismatched BeginPopup()/EndPopup() calls
7657     IM_ASSERT(g.BeginPopupStack.Size > 0);
7658 
7659     // Make all menus and popups wrap around for now, may need to expose that policy.
7660     NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
7661 
7662     // Child-popups don't need to be layed out
7663     IM_ASSERT(g.WithinEndChild == false);
7664     if (window->Flags & ImGuiWindowFlags_ChildWindow)
7665         g.WithinEndChild = true;
7666     End();
7667     g.WithinEndChild = false;
7668 }
7669 
OpenPopupOnItemClick(const char * str_id,ImGuiMouseButton mouse_button)7670 bool ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiMouseButton mouse_button)
7671 {
7672     ImGuiWindow* window = GImGui->CurrentWindow;
7673     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
7674     {
7675         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!
7676         IM_ASSERT(id != 0);                                                  // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
7677         OpenPopupEx(id);
7678         return true;
7679     }
7680     return false;
7681 }
7682 
7683 // This is a helper to handle the simplest case of associating one named popup to one given widget.
7684 // You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
7685 // You can pass a NULL str_id to use the identifier of the last item.
BeginPopupContextItem(const char * str_id,ImGuiMouseButton mouse_button)7686 bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiMouseButton mouse_button)
7687 {
7688     ImGuiWindow* window = GImGui->CurrentWindow;
7689     if (window->SkipItems)
7690         return false;
7691     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!
7692     IM_ASSERT(id != 0);                                                  // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
7693     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
7694         OpenPopupEx(id);
7695     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
7696 }
7697 
BeginPopupContextWindow(const char * str_id,ImGuiMouseButton mouse_button,bool also_over_items)7698 bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiMouseButton mouse_button, bool also_over_items)
7699 {
7700     if (!str_id)
7701         str_id = "window_context";
7702     ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
7703     if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
7704         if (also_over_items || !IsAnyItemHovered())
7705             OpenPopupEx(id);
7706     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
7707 }
7708 
BeginPopupContextVoid(const char * str_id,ImGuiMouseButton mouse_button)7709 bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiMouseButton mouse_button)
7710 {
7711     if (!str_id)
7712         str_id = "void_context";
7713     ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
7714     if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
7715         OpenPopupEx(id);
7716     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
7717 }
7718 
7719 // 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.)
7720 // 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)7721 ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
7722 {
7723     ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
7724     //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
7725     //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
7726 
7727     // Combo Box policy (we want a connecting edge)
7728     if (policy == ImGuiPopupPositionPolicy_ComboBox)
7729     {
7730         const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
7731         for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
7732         {
7733             const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
7734             if (n != -1 && dir == *last_dir) // Already tried this direction?
7735                 continue;
7736             ImVec2 pos;
7737             if (dir == ImGuiDir_Down)  pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y);          // Below, Toward Right (default)
7738             if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
7739             if (dir == ImGuiDir_Left)  pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
7740             if (dir == ImGuiDir_Up)    pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
7741             if (!r_outer.Contains(ImRect(pos, pos + size)))
7742                 continue;
7743             *last_dir = dir;
7744             return pos;
7745         }
7746     }
7747 
7748     // Default popup policy
7749     const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
7750     for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
7751     {
7752         const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
7753         if (n != -1 && dir == *last_dir) // Already tried this direction?
7754             continue;
7755         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);
7756         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);
7757         if (avail_w < size.x || avail_h < size.y)
7758             continue;
7759         ImVec2 pos;
7760         pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
7761         pos.y = (dir == ImGuiDir_Up)   ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down)  ? r_avoid.Max.y : base_pos_clamped.y;
7762         *last_dir = dir;
7763         return pos;
7764     }
7765 
7766     // Fallback, try to keep within display
7767     *last_dir = ImGuiDir_None;
7768     ImVec2 pos = ref_pos;
7769     pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
7770     pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
7771     return pos;
7772 }
7773 
GetWindowAllowedExtentRect(ImGuiWindow * window)7774 ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window)
7775 {
7776     IM_UNUSED(window);
7777     ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding;
7778     ImRect r_screen = GetViewportRect();
7779     r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
7780     return r_screen;
7781 }
7782 
FindBestWindowPosForPopup(ImGuiWindow * window)7783 ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
7784 {
7785     ImGuiContext& g = *GImGui;
7786 
7787     ImRect r_outer = GetWindowAllowedExtentRect(window);
7788     if (window->Flags & ImGuiWindowFlags_ChildMenu)
7789     {
7790         // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
7791         // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
7792         IM_ASSERT(g.CurrentWindow == window);
7793         ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2];
7794         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).
7795         ImRect r_avoid;
7796         if (parent_window->DC.MenuBarAppending)
7797             r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight());
7798         else
7799             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);
7800         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
7801     }
7802     if (window->Flags & ImGuiWindowFlags_Popup)
7803     {
7804         ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
7805         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
7806     }
7807     if (window->Flags & ImGuiWindowFlags_Tooltip)
7808     {
7809         // Position tooltip (always follows mouse)
7810         float sc = g.Style.MouseCursorScale;
7811         ImVec2 ref_pos = NavCalcPreferredRefPos();
7812         ImRect r_avoid;
7813         if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
7814             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
7815         else
7816             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.
7817         ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
7818         if (window->AutoPosLastDirection == ImGuiDir_None)
7819             pos = ref_pos + ImVec2(2, 2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
7820         return pos;
7821     }
7822     IM_ASSERT(0);
7823     return window->Pos;
7824 }
7825 
7826 
7827 //-----------------------------------------------------------------------------
7828 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
7829 //-----------------------------------------------------------------------------
7830 
7831 // FIXME-NAV: The existence of SetNavID vs SetNavIDWithRectRel vs SetFocusID is incredibly messy and confusing,
7832 // and needs some explanation or serious refactoring.
SetNavID(ImGuiID id,int nav_layer,ImGuiID focus_scope_id)7833 void ImGui::SetNavID(ImGuiID id, int nav_layer, ImGuiID focus_scope_id)
7834 {
7835     ImGuiContext& g = *GImGui;
7836     IM_ASSERT(g.NavWindow);
7837     IM_ASSERT(nav_layer == 0 || nav_layer == 1);
7838     g.NavId = id;
7839     g.NavFocusScopeId = focus_scope_id;
7840     g.NavWindow->NavLastIds[nav_layer] = id;
7841 }
7842 
SetNavIDWithRectRel(ImGuiID id,int nav_layer,ImGuiID focus_scope_id,const ImRect & rect_rel)7843 void ImGui::SetNavIDWithRectRel(ImGuiID id, int nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel)
7844 {
7845     ImGuiContext& g = *GImGui;
7846     SetNavID(id, nav_layer, focus_scope_id);
7847     g.NavWindow->NavRectRel[nav_layer] = rect_rel;
7848     g.NavMousePosDirty = true;
7849     g.NavDisableHighlight = false;
7850     g.NavDisableMouseHover = true;
7851 }
7852 
SetFocusID(ImGuiID id,ImGuiWindow * window)7853 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
7854 {
7855     ImGuiContext& g = *GImGui;
7856     IM_ASSERT(id != 0);
7857 
7858     // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and window->DC.NavFocusScopeIdCurrent are valid.
7859     // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text)
7860     const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
7861     if (g.NavWindow != window)
7862         g.NavInitRequest = false;
7863     g.NavWindow = window;
7864     g.NavId = id;
7865     g.NavLayer = nav_layer;
7866     g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
7867     window->NavLastIds[nav_layer] = id;
7868     if (window->DC.LastItemId == id)
7869         window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos);
7870 
7871     if (g.ActiveIdSource == ImGuiInputSource_Nav)
7872         g.NavDisableMouseHover = true;
7873     else
7874         g.NavDisableHighlight = true;
7875 }
7876 
ImGetDirQuadrantFromDelta(float dx,float dy)7877 ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
7878 {
7879     if (ImFabs(dx) > ImFabs(dy))
7880         return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
7881     return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
7882 }
7883 
NavScoreItemDistInterval(float a0,float a1,float b0,float b1)7884 static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
7885 {
7886     if (a1 < b0)
7887         return a1 - b0;
7888     if (b1 < a0)
7889         return a0 - b1;
7890     return 0.0f;
7891 }
7892 
NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir,ImRect & r,const ImRect & clip_rect)7893 static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect)
7894 {
7895     if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
7896     {
7897         r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y);
7898         r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y);
7899     }
7900     else
7901     {
7902         r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x);
7903         r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x);
7904     }
7905 }
7906 
7907 // Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057
NavScoreItem(ImGuiNavMoveResult * result,ImRect cand)7908 static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
7909 {
7910     ImGuiContext& g = *GImGui;
7911     ImGuiWindow* window = g.CurrentWindow;
7912     if (g.NavLayer != window->DC.NavLayerCurrent)
7913         return false;
7914 
7915     const ImRect& curr = g.NavScoringRectScreen; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width)
7916     g.NavScoringCount++;
7917 
7918     // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
7919     if (window->ParentWindow == g.NavWindow)
7920     {
7921         IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened);
7922         if (!window->ClipRect.Overlaps(cand))
7923             return false;
7924         cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
7925     }
7926 
7927     // 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)
7928     // For example, this ensure that items in one column are not reached when moving vertically from items in another column.
7929     NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect);
7930 
7931     // Compute distance between boxes
7932     // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
7933     float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
7934     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
7935     if (dby != 0.0f && dbx != 0.0f)
7936        dbx = (dbx/1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
7937     float dist_box = ImFabs(dbx) + ImFabs(dby);
7938 
7939     // 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)
7940     float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
7941     float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
7942     float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
7943 
7944     // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
7945     ImGuiDir quadrant;
7946     float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
7947     if (dbx != 0.0f || dby != 0.0f)
7948     {
7949         // For non-overlapping boxes, use distance between boxes
7950         dax = dbx;
7951         day = dby;
7952         dist_axial = dist_box;
7953         quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
7954     }
7955     else if (dcx != 0.0f || dcy != 0.0f)
7956     {
7957         // For overlapping boxes with different centers, use distance between centers
7958         dax = dcx;
7959         day = dcy;
7960         dist_axial = dist_center;
7961         quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
7962     }
7963     else
7964     {
7965         // 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)
7966         quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
7967     }
7968 
7969 #if IMGUI_DEBUG_NAV_SCORING
7970     char buf[128];
7971     if (IsMouseHoveringRect(cand.Min, cand.Max))
7972     {
7973         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]);
7974         ImDrawList* draw_list = GetForegroundDrawList(window);
7975         draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
7976         draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
7977         draw_list->AddRectFilled(cand.Max - ImVec2(4,4), cand.Max + CalcTextSize(buf) + ImVec2(4,4), IM_COL32(40,0,0,150));
7978         draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
7979     }
7980     else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
7981     {
7982         if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; }
7983         if (quadrant == g.NavMoveDir)
7984         {
7985             ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
7986             ImDrawList* draw_list = GetForegroundDrawList(window);
7987             draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
7988             draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
7989         }
7990     }
7991  #endif
7992 
7993     // Is it in the quadrant we're interesting in moving to?
7994     bool new_best = false;
7995     if (quadrant == g.NavMoveDir)
7996     {
7997         // Does it beat the current best candidate?
7998         if (dist_box < result->DistBox)
7999         {
8000             result->DistBox = dist_box;
8001             result->DistCenter = dist_center;
8002             return true;
8003         }
8004         if (dist_box == result->DistBox)
8005         {
8006             // Try using distance between center points to break ties
8007             if (dist_center < result->DistCenter)
8008             {
8009                 result->DistCenter = dist_center;
8010                 new_best = true;
8011             }
8012             else if (dist_center == result->DistCenter)
8013             {
8014                 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
8015                 // (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),
8016                 // 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.
8017                 if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
8018                     new_best = true;
8019             }
8020         }
8021     }
8022 
8023     // 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
8024     // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
8025     // 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.
8026     // 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.
8027     // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
8028     if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial)  // Check axial match
8029         if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
8030             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))
8031             {
8032                 result->DistAxial = dist_axial;
8033                 new_best = true;
8034             }
8035 
8036     return new_best;
8037 }
8038 
8039 // 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)8040 static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
8041 {
8042     ImGuiContext& g = *GImGui;
8043     //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.
8044     //    return;
8045 
8046     const ImGuiItemFlags item_flags = window->DC.ItemFlags;
8047     const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
8048 
8049     // Process Init Request
8050     if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
8051     {
8052         // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
8053         if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0)
8054         {
8055             g.NavInitResultId = id;
8056             g.NavInitResultRectRel = nav_bb_rel;
8057         }
8058         if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus))
8059         {
8060             g.NavInitRequest = false; // Found a match, clear request
8061             NavUpdateAnyRequestFlag();
8062         }
8063     }
8064 
8065     // Process Move Request (scoring for navigation)
8066     // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
8067     if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled|ImGuiItemFlags_NoNav)))
8068     {
8069         ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
8070 #if IMGUI_DEBUG_NAV_SCORING
8071         // [DEBUG] Score all items in NavWindow at all times
8072         if (!g.NavMoveRequest)
8073             g.NavMoveDir = g.NavMoveDirLast;
8074         bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest;
8075 #else
8076         bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb);
8077 #endif
8078         if (new_best)
8079         {
8080             result->Window = window;
8081             result->ID = id;
8082             result->FocusScopeId = window->DC.NavFocusScopeIdCurrent;
8083             result->RectRel = nav_bb_rel;
8084         }
8085 
8086         // Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
8087         const float VISIBLE_RATIO = 0.70f;
8088         if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
8089             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)
8090                 if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb))
8091                 {
8092                     result = &g.NavMoveResultLocalVisibleSet;
8093                     result->Window = window;
8094                     result->ID = id;
8095                     result->FocusScopeId = window->DC.NavFocusScopeIdCurrent;
8096                     result->RectRel = nav_bb_rel;
8097                 }
8098     }
8099 
8100     // Update window-relative bounding box of navigated item
8101     if (g.NavId == id)
8102     {
8103         g.NavWindow = window;                                           // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
8104         g.NavLayer = window->DC.NavLayerCurrent;
8105         g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
8106         g.NavIdIsAlive = true;
8107         g.NavIdTabCounter = window->DC.FocusCounterTabStop;
8108         window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel;    // Store item bounding box (relative to window position)
8109     }
8110 }
8111 
NavMoveRequestButNoResultYet()8112 bool ImGui::NavMoveRequestButNoResultYet()
8113 {
8114     ImGuiContext& g = *GImGui;
8115     return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
8116 }
8117 
NavMoveRequestCancel()8118 void ImGui::NavMoveRequestCancel()
8119 {
8120     ImGuiContext& g = *GImGui;
8121     g.NavMoveRequest = false;
8122     NavUpdateAnyRequestFlag();
8123 }
8124 
NavMoveRequestForward(ImGuiDir move_dir,ImGuiDir clip_dir,const ImRect & bb_rel,ImGuiNavMoveFlags move_flags)8125 void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags)
8126 {
8127     ImGuiContext& g = *GImGui;
8128     IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None);
8129     NavMoveRequestCancel();
8130     g.NavMoveDir = move_dir;
8131     g.NavMoveClipDir = clip_dir;
8132     g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
8133     g.NavMoveRequestFlags = move_flags;
8134     g.NavWindow->NavRectRel[g.NavLayer] = bb_rel;
8135 }
8136 
NavMoveRequestTryWrapping(ImGuiWindow * window,ImGuiNavMoveFlags move_flags)8137 void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
8138 {
8139     ImGuiContext& g = *GImGui;
8140     if (g.NavWindow != window || !NavMoveRequestButNoResultYet() || g.NavMoveRequestForward != ImGuiNavForward_None || g.NavLayer != 0)
8141         return;
8142     IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
8143     ImRect bb_rel = window->NavRectRel[0];
8144 
8145     ImGuiDir clip_dir = g.NavMoveDir;
8146     if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
8147     {
8148         bb_rel.Min.x = bb_rel.Max.x = ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x;
8149         if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(-bb_rel.GetHeight()); clip_dir = ImGuiDir_Up; }
8150         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
8151     }
8152     if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
8153     {
8154         bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
8155         if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(+bb_rel.GetHeight()); clip_dir = ImGuiDir_Down; }
8156         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
8157     }
8158     if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
8159     {
8160         bb_rel.Min.y = bb_rel.Max.y = ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y;
8161         if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(-bb_rel.GetWidth()); clip_dir = ImGuiDir_Left; }
8162         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
8163     }
8164     if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
8165     {
8166         bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
8167         if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(+bb_rel.GetWidth()); clip_dir = ImGuiDir_Right; }
8168         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
8169     }
8170 }
8171 
8172 // FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
8173 // This way we could find the last focused window among our children. It would be much less confusing this way?
NavSaveLastChildNavWindowIntoParent(ImGuiWindow * nav_window)8174 static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window)
8175 {
8176     ImGuiWindow* parent_window = nav_window;
8177     while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
8178         parent_window = parent_window->ParentWindow;
8179     if (parent_window && parent_window != nav_window)
8180         parent_window->NavLastChildNavWindow = nav_window;
8181 }
8182 
8183 // Restore the last focused child.
8184 // Call when we are expected to land on the Main Layer (0) after FocusWindow()
NavRestoreLastChildNavWindow(ImGuiWindow * window)8185 static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
8186 {
8187     return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window;
8188 }
8189 
NavRestoreLayer(ImGuiNavLayer layer)8190 static void NavRestoreLayer(ImGuiNavLayer layer)
8191 {
8192     ImGuiContext& g = *GImGui;
8193     g.NavLayer = layer;
8194     if (layer == 0)
8195         g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow);
8196     ImGuiWindow* window = g.NavWindow;
8197     if (layer == 0 && window->NavLastIds[0] != 0)
8198         ImGui::SetNavIDWithRectRel(window->NavLastIds[0], layer, 0, window->NavRectRel[0]);
8199     else
8200         ImGui::NavInitWindow(window, true);
8201 }
8202 
NavUpdateAnyRequestFlag()8203 static inline void ImGui::NavUpdateAnyRequestFlag()
8204 {
8205     ImGuiContext& g = *GImGui;
8206     g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
8207     if (g.NavAnyRequest)
8208         IM_ASSERT(g.NavWindow != NULL);
8209 }
8210 
8211 // This needs to be called before we submit any widget (aka in or before Begin)
NavInitWindow(ImGuiWindow * window,bool force_reinit)8212 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
8213 {
8214     ImGuiContext& g = *GImGui;
8215     IM_ASSERT(window == g.NavWindow);
8216     bool init_for_nav = false;
8217     if (!(window->Flags & ImGuiWindowFlags_NoNavInputs))
8218         if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
8219             init_for_nav = true;
8220     //IMGUI_DEBUG_LOG("[Nav] NavInitWindow() init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer);
8221     if (init_for_nav)
8222     {
8223         SetNavID(0, g.NavLayer, 0);
8224         g.NavInitRequest = true;
8225         g.NavInitRequestFromMove = false;
8226         g.NavInitResultId = 0;
8227         g.NavInitResultRectRel = ImRect();
8228         NavUpdateAnyRequestFlag();
8229     }
8230     else
8231     {
8232         g.NavId = window->NavLastIds[0];
8233         g.NavFocusScopeId = 0;
8234     }
8235 }
8236 
NavCalcPreferredRefPos()8237 static ImVec2 ImGui::NavCalcPreferredRefPos()
8238 {
8239     ImGuiContext& g = *GImGui;
8240     if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow)
8241     {
8242         // Mouse (we need a fallback in case the mouse becomes invalid after being used)
8243         if (IsMousePosValid(&g.IO.MousePos))
8244             return g.IO.MousePos;
8245         return g.LastValidMousePos;
8246     }
8247     else
8248     {
8249         // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item.
8250         const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
8251         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()));
8252         ImRect visible_rect = GetViewportRect();
8253         return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max));   // ImFloor() is important because non-integer mouse position application in back-end might be lossy and result in undesirable non-zero delta.
8254     }
8255 }
8256 
GetNavInputAmount(ImGuiNavInput n,ImGuiInputReadMode mode)8257 float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode)
8258 {
8259     ImGuiContext& g = *GImGui;
8260     if (mode == ImGuiInputReadMode_Down)
8261         return g.IO.NavInputs[n];                         // Instant, read analog input (0.0f..1.0f, as provided by user)
8262 
8263     const float t = g.IO.NavInputsDownDuration[n];
8264     if (t < 0.0f && mode == ImGuiInputReadMode_Released)  // Return 1.0f when just released, no repeat, ignore analog input.
8265         return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
8266     if (t < 0.0f)
8267         return 0.0f;
8268     if (mode == ImGuiInputReadMode_Pressed)               // Return 1.0f when just pressed, no repeat, ignore analog input.
8269         return (t == 0.0f) ? 1.0f : 0.0f;
8270     if (mode == ImGuiInputReadMode_Repeat)
8271         return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.80f);
8272     if (mode == ImGuiInputReadMode_RepeatSlow)
8273         return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 1.25f, g.IO.KeyRepeatRate * 2.00f);
8274     if (mode == ImGuiInputReadMode_RepeatFast)
8275         return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.30f);
8276     return 0.0f;
8277 }
8278 
GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources,ImGuiInputReadMode mode,float slow_factor,float fast_factor)8279 ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor)
8280 {
8281     ImVec2 delta(0.0f, 0.0f);
8282     if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
8283         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode)   - GetNavInputAmount(ImGuiNavInput_KeyLeft_,   mode), GetNavInputAmount(ImGuiNavInput_KeyDown_,   mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_,   mode));
8284     if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
8285         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode)   - GetNavInputAmount(ImGuiNavInput_DpadLeft,   mode), GetNavInputAmount(ImGuiNavInput_DpadDown,   mode) - GetNavInputAmount(ImGuiNavInput_DpadUp,   mode));
8286     if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
8287         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode));
8288     if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow))
8289         delta *= slow_factor;
8290     if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast))
8291         delta *= fast_factor;
8292     return delta;
8293 }
8294 
NavUpdate()8295 static void ImGui::NavUpdate()
8296 {
8297     ImGuiContext& g = *GImGui;
8298     g.IO.WantSetMousePos = false;
8299 #if 0
8300     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);
8301 #endif
8302 
8303     // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard)
8304     // (do it before we map Keyboard input!)
8305     bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
8306     bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
8307     if (nav_gamepad_active)
8308         if (g.IO.NavInputs[ImGuiNavInput_Activate] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Input] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Cancel] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Menu] > 0.0f)
8309             g.NavInputSource = ImGuiInputSource_NavGamepad;
8310 
8311     // Update Keyboard->Nav inputs mapping
8312     if (nav_keyboard_active)
8313     {
8314         #define NAV_MAP_KEY(_KEY, _NAV_INPUT)  do { if (IsKeyDown(g.IO.KeyMap[_KEY])) { g.IO.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; } } while (0)
8315         NAV_MAP_KEY(ImGuiKey_Space,     ImGuiNavInput_Activate );
8316         NAV_MAP_KEY(ImGuiKey_Enter,     ImGuiNavInput_Input    );
8317         NAV_MAP_KEY(ImGuiKey_Escape,    ImGuiNavInput_Cancel   );
8318         NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ );
8319         NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
8320         NAV_MAP_KEY(ImGuiKey_UpArrow,   ImGuiNavInput_KeyUp_   );
8321         NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
8322         if (g.IO.KeyCtrl)
8323             g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
8324         if (g.IO.KeyShift)
8325             g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
8326         if (g.IO.KeyAlt && !g.IO.KeyCtrl) // AltGR is Alt+Ctrl, also even on keyboards without AltGR we don't want Alt+Ctrl to open menu.
8327             g.IO.NavInputs[ImGuiNavInput_KeyMenu_]  = 1.0f;
8328         #undef NAV_MAP_KEY
8329     }
8330     memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration));
8331     for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++)
8332         g.IO.NavInputsDownDuration[i] = (g.IO.NavInputs[i] > 0.0f) ? (g.IO.NavInputsDownDuration[i] < 0.0f ? 0.0f : g.IO.NavInputsDownDuration[i] + g.IO.DeltaTime) : -1.0f;
8333 
8334     // Process navigation init request (select first/default focus)
8335     // 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)
8336     if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove) && g.NavWindow)
8337     {
8338         // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
8339         //IMGUI_DEBUG_LOG("[Nav] Apply NavInitRequest result: 0x%08X Layer %d in \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name);
8340         if (g.NavInitRequestFromMove)
8341             SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel);
8342         else
8343             SetNavID(g.NavInitResultId, g.NavLayer, 0);
8344         g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel;
8345     }
8346     g.NavInitRequest = false;
8347     g.NavInitRequestFromMove = false;
8348     g.NavInitResultId = 0;
8349     g.NavJustMovedToId = 0;
8350 
8351     // Process navigation move request
8352     if (g.NavMoveRequest)
8353         NavUpdateMoveResult();
8354 
8355     // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
8356     if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
8357     {
8358         IM_ASSERT(g.NavMoveRequest);
8359         if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
8360             g.NavDisableHighlight = false;
8361         g.NavMoveRequestForward = ImGuiNavForward_None;
8362     }
8363 
8364     // Apply application mouse position movement, after we had a chance to process move request result.
8365     if (g.NavMousePosDirty && g.NavIdIsAlive)
8366     {
8367         // Set mouse position given our knowledge of the navigated item position from last frame
8368         if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
8369         {
8370             if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
8371             {
8372                 g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos();
8373                 g.IO.WantSetMousePos = true;
8374             }
8375         }
8376         g.NavMousePosDirty = false;
8377     }
8378     g.NavIdIsAlive = false;
8379     g.NavJustTabbedId = 0;
8380     IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
8381 
8382     // 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
8383     if (g.NavWindow)
8384         NavSaveLastChildNavWindowIntoParent(g.NavWindow);
8385     if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0)
8386         g.NavWindow->NavLastChildNavWindow = NULL;
8387 
8388     // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
8389     NavUpdateWindowing();
8390 
8391     // Set output flags for user application
8392     g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
8393     g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL);
8394 
8395     // Process NavCancel input (to close a popup, get back to parent, clear focus)
8396     if (IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
8397     {
8398         if (g.ActiveId != 0)
8399         {
8400             if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel))
8401                 ClearActiveID();
8402         }
8403         else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
8404         {
8405             // Exit child window
8406             ImGuiWindow* child_window = g.NavWindow;
8407             ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
8408             IM_ASSERT(child_window->ChildId != 0);
8409             FocusWindow(parent_window);
8410             SetNavID(child_window->ChildId, 0, 0);
8411             // Reassigning with same value, we're being explicit here.
8412             g.NavIdIsAlive = false;     // -V1048
8413             if (g.NavDisableMouseHover)
8414                 g.NavMousePosDirty = true;
8415         }
8416         else if (g.OpenPopupStack.Size > 0)
8417         {
8418             // Close open popup/menu
8419             if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
8420                 ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
8421         }
8422         else if (g.NavLayer != 0)
8423         {
8424             // Leave the "menu" layer
8425             NavRestoreLayer(ImGuiNavLayer_Main);
8426         }
8427         else
8428         {
8429             // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
8430             if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
8431                 g.NavWindow->NavLastIds[0] = 0;
8432             g.NavId = g.NavFocusScopeId = 0;
8433         }
8434     }
8435 
8436     // Process manual activation request
8437     g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
8438     if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8439     {
8440         bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
8441         bool activate_pressed = activate_down && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
8442         if (g.ActiveId == 0 && activate_pressed)
8443             g.NavActivateId = g.NavId;
8444         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
8445             g.NavActivateDownId = g.NavId;
8446         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
8447             g.NavActivatePressedId = g.NavId;
8448         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
8449             g.NavInputId = g.NavId;
8450     }
8451     if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8452         g.NavDisableHighlight = true;
8453     if (g.NavActivateId != 0)
8454         IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
8455     g.NavMoveRequest = false;
8456 
8457     // Process programmatic activation request
8458     if (g.NavNextActivateId != 0)
8459         g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
8460     g.NavNextActivateId = 0;
8461 
8462     // Initiate directional inputs request
8463     if (g.NavMoveRequestForward == ImGuiNavForward_None)
8464     {
8465         g.NavMoveDir = ImGuiDir_None;
8466         g.NavMoveRequestFlags = ImGuiNavMoveFlags_None;
8467         if (g.NavWindow && !g.NavWindowingTarget && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8468         {
8469             const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat;
8470             if (!IsActiveIdUsingNavDir(ImGuiDir_Left)  && (IsNavInputTest(ImGuiNavInput_DpadLeft,  read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_,  read_mode))) { g.NavMoveDir = ImGuiDir_Left; }
8471             if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; }
8472             if (!IsActiveIdUsingNavDir(ImGuiDir_Up)    && (IsNavInputTest(ImGuiNavInput_DpadUp,    read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_,    read_mode))) { g.NavMoveDir = ImGuiDir_Up; }
8473             if (!IsActiveIdUsingNavDir(ImGuiDir_Down)  && (IsNavInputTest(ImGuiNavInput_DpadDown,  read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_,  read_mode))) { g.NavMoveDir = ImGuiDir_Down; }
8474         }
8475         g.NavMoveClipDir = g.NavMoveDir;
8476     }
8477     else
8478     {
8479         // 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)
8480         // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function)
8481         IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
8482         IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
8483         g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
8484     }
8485 
8486     // Update PageUp/PageDown/Home/End scroll
8487     // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
8488     float nav_scoring_rect_offset_y = 0.0f;
8489     if (nav_keyboard_active)
8490         nav_scoring_rect_offset_y = NavUpdatePageUpPageDown();
8491 
8492     // 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
8493     if (g.NavMoveDir != ImGuiDir_None)
8494     {
8495         g.NavMoveRequest = true;
8496         g.NavMoveDirLast = g.NavMoveDir;
8497     }
8498     if (g.NavMoveRequest && g.NavId == 0)
8499     {
8500         //IMGUI_DEBUG_LOG("[Nav] NavInitRequest from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer);
8501         g.NavInitRequest = g.NavInitRequestFromMove = true;
8502         // Reassigning with same value, we're being explicit here.
8503         g.NavInitResultId = 0;     // -V1048
8504         g.NavDisableHighlight = false;
8505     }
8506     NavUpdateAnyRequestFlag();
8507 
8508     // Scrolling
8509     if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
8510     {
8511         // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
8512         ImGuiWindow* window = g.NavWindow;
8513         const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * g.IO.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.
8514         if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
8515         {
8516             if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
8517                 SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
8518             if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
8519                 SetScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
8520         }
8521 
8522         // *Normal* Manual scroll with NavScrollXXX keys
8523         // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
8524         ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f);
8525         if (scroll_dir.x != 0.0f && window->ScrollbarX)
8526         {
8527             SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
8528             g.NavMoveFromClampedRefRect = true;
8529         }
8530         if (scroll_dir.y != 0.0f)
8531         {
8532             SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
8533             g.NavMoveFromClampedRefRect = true;
8534         }
8535     }
8536 
8537     // Reset search results
8538     g.NavMoveResultLocal.Clear();
8539     g.NavMoveResultLocalVisibleSet.Clear();
8540     g.NavMoveResultOther.Clear();
8541 
8542     // When we have manually scrolled (without using navigation) and NavId becomes out of bounds, we project its bounding box to the visible area to restart navigation within visible items
8543     if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0)
8544     {
8545         ImGuiWindow* window = g.NavWindow;
8546         ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1,1), window->InnerRect.Max - window->Pos + ImVec2(1,1));
8547         if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
8548         {
8549             float pad = window->CalcFontSize() * 0.5f;
8550             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
8551             window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel);
8552             g.NavId = g.NavFocusScopeId = 0;
8553         }
8554         g.NavMoveFromClampedRefRect = false;
8555     }
8556 
8557     // 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)
8558     ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0);
8559     g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect();
8560     g.NavScoringRectScreen.TranslateY(nav_scoring_rect_offset_y);
8561     g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x);
8562     g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x;
8563     IM_ASSERT(!g.NavScoringRectScreen.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem().
8564     //GetForegroundDrawList()->AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
8565     g.NavScoringCount = 0;
8566 #if IMGUI_DEBUG_NAV_RECTS
8567     if (g.NavWindow)
8568     {
8569         ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow);
8570         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]
8571         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); }
8572     }
8573 #endif
8574 }
8575 
8576 // Apply result from previous frame navigation directional move request
NavUpdateMoveResult()8577 static void ImGui::NavUpdateMoveResult()
8578 {
8579     ImGuiContext& g = *GImGui;
8580     if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
8581     {
8582         // 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)
8583         if (g.NavId != 0)
8584         {
8585             g.NavDisableHighlight = false;
8586             g.NavDisableMouseHover = true;
8587         }
8588         return;
8589     }
8590 
8591     // Select which result to use
8592     ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
8593 
8594     // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
8595     if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
8596         if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId)
8597             result = &g.NavMoveResultLocalVisibleSet;
8598 
8599     // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
8600     if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
8601         if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
8602             result = &g.NavMoveResultOther;
8603     IM_ASSERT(g.NavWindow && result->Window);
8604 
8605     // Scroll to keep newly navigated item fully into view.
8606     if (g.NavLayer == 0)
8607     {
8608         ImVec2 delta_scroll;
8609         if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_ScrollToEdge)
8610         {
8611             float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
8612             delta_scroll.y = result->Window->Scroll.y - scroll_target;
8613             SetScrollY(result->Window, scroll_target);
8614         }
8615         else
8616         {
8617             ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos);
8618             delta_scroll = ScrollToBringRectIntoView(result->Window, rect_abs);
8619         }
8620 
8621         // Offset our result position so mouse position can be applied immediately after in NavUpdate()
8622         result->RectRel.TranslateX(-delta_scroll.x);
8623         result->RectRel.TranslateY(-delta_scroll.y);
8624     }
8625 
8626     ClearActiveID();
8627     g.NavWindow = result->Window;
8628     if (g.NavId != result->ID)
8629     {
8630         // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
8631         g.NavJustMovedToId = result->ID;
8632         g.NavJustMovedToFocusScopeId = result->FocusScopeId;
8633 
8634     }
8635     SetNavIDWithRectRel(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
8636     g.NavMoveFromClampedRefRect = false;
8637 }
8638 
8639 // Handle PageUp/PageDown/Home/End keys
NavUpdatePageUpPageDown()8640 static float ImGui::NavUpdatePageUpPageDown()
8641 {
8642     ImGuiContext& g = *GImGui;
8643     if (g.NavMoveDir != ImGuiDir_None || g.NavWindow == NULL)
8644         return 0.0f;
8645     if ((g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != 0)
8646         return 0.0f;
8647 
8648     ImGuiWindow* window = g.NavWindow;
8649     const bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp);
8650     const bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown);
8651     const bool home_pressed = IsKeyPressed(g.IO.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home);
8652     const bool end_pressed = IsKeyPressed(g.IO.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End);
8653     if (page_up_held != page_down_held || home_pressed != end_pressed) // If either (not both) are pressed
8654     {
8655         if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll)
8656         {
8657             // Fallback manual-scroll when window has no navigable item
8658             if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
8659                 SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
8660             else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
8661                 SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
8662             else if (home_pressed)
8663                 SetScrollY(window, 0.0f);
8664             else if (end_pressed)
8665                 SetScrollY(window, window->ScrollMax.y);
8666         }
8667         else
8668         {
8669             ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
8670             const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
8671             float nav_scoring_rect_offset_y = 0.0f;
8672             if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
8673             {
8674                 nav_scoring_rect_offset_y = -page_offset_y;
8675                 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)
8676                 g.NavMoveClipDir = ImGuiDir_Up;
8677                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
8678             }
8679             else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
8680             {
8681                 nav_scoring_rect_offset_y = +page_offset_y;
8682                 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)
8683                 g.NavMoveClipDir = ImGuiDir_Down;
8684                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
8685             }
8686             else if (home_pressed)
8687             {
8688                 // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
8689                 // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result.
8690                 // Preserve current horizontal position if we have any.
8691                 nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y;
8692                 if (nav_rect_rel.IsInverted())
8693                     nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
8694                 g.NavMoveDir = ImGuiDir_Down;
8695                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
8696             }
8697             else if (end_pressed)
8698             {
8699                 nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y;
8700                 if (nav_rect_rel.IsInverted())
8701                     nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
8702                 g.NavMoveDir = ImGuiDir_Up;
8703                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
8704             }
8705             return nav_scoring_rect_offset_y;
8706         }
8707     }
8708     return 0.0f;
8709 }
8710 
FindWindowFocusIndex(ImGuiWindow * window)8711 static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N)
8712 {
8713     ImGuiContext& g = *GImGui;
8714     for (int i = g.WindowsFocusOrder.Size-1; i >= 0; i--)
8715         if (g.WindowsFocusOrder[i] == window)
8716             return i;
8717     return -1;
8718 }
8719 
FindWindowNavFocusable(int i_start,int i_stop,int dir)8720 static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
8721 {
8722     ImGuiContext& g = *GImGui;
8723     for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
8724         if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
8725             return g.WindowsFocusOrder[i];
8726     return NULL;
8727 }
8728 
NavUpdateWindowingHighlightWindow(int focus_change_dir)8729 static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
8730 {
8731     ImGuiContext& g = *GImGui;
8732     IM_ASSERT(g.NavWindowingTarget);
8733     if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
8734         return;
8735 
8736     const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget);
8737     ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
8738     if (!window_target)
8739         window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
8740     if (window_target) // Don't reset windowing target if there's a single window in the list
8741         g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
8742     g.NavWindowingToggleLayer = false;
8743 }
8744 
8745 // Windowing management mode
8746 // Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer)
8747 // Gamepad:  Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)
NavUpdateWindowing()8748 static void ImGui::NavUpdateWindowing()
8749 {
8750     ImGuiContext& g = *GImGui;
8751     ImGuiWindow* apply_focus_window = NULL;
8752     bool apply_toggle_layer = false;
8753 
8754     ImGuiWindow* modal_window = GetTopMostPopupModal();
8755     if (modal_window != NULL)
8756     {
8757         g.NavWindowingTarget = NULL;
8758         return;
8759     }
8760 
8761     // Fade out
8762     if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
8763     {
8764         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f);
8765         if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
8766             g.NavWindowingTargetAnim = NULL;
8767     }
8768 
8769     // Start CTRL-TAB or Square+L/R window selection
8770     bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
8771     bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
8772     if (start_windowing_with_gamepad || start_windowing_with_keyboard)
8773         if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
8774         {
8775             g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; // FIXME-DOCK: Will need to use RootWindowDockStop
8776             g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
8777             g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true;
8778             g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad;
8779         }
8780 
8781     // Gamepad update
8782     g.NavWindowingTimer += g.IO.DeltaTime;
8783     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad)
8784     {
8785         // 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
8786         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
8787 
8788         // Select window to focus
8789         const int focus_change_dir = (int)IsNavInputTest(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputTest(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow);
8790         if (focus_change_dir != 0)
8791         {
8792             NavUpdateWindowingHighlightWindow(focus_change_dir);
8793             g.NavWindowingHighlightAlpha = 1.0f;
8794         }
8795 
8796         // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)
8797         if (!IsNavInputDown(ImGuiNavInput_Menu))
8798         {
8799             g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
8800             if (g.NavWindowingToggleLayer && g.NavWindow)
8801                 apply_toggle_layer = true;
8802             else if (!g.NavWindowingToggleLayer)
8803                 apply_focus_window = g.NavWindowingTarget;
8804             g.NavWindowingTarget = NULL;
8805         }
8806     }
8807 
8808     // Keyboard: Focus
8809     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard)
8810     {
8811         // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
8812         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
8813         if (IsKeyPressedMap(ImGuiKey_Tab, true))
8814             NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1);
8815         if (!g.IO.KeyCtrl)
8816             apply_focus_window = g.NavWindowingTarget;
8817     }
8818 
8819     // Keyboard: Press and Release ALT to toggle menu layer
8820     // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of back-end clearing releases all keys on ALT-TAB
8821     if (IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Pressed))
8822         g.NavWindowingToggleLayer = true;
8823     if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && g.NavWindowingToggleLayer && IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released))
8824         if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev))
8825             apply_toggle_layer = true;
8826 
8827     // Move window
8828     if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
8829     {
8830         ImVec2 move_delta;
8831         if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift)
8832             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
8833         if (g.NavInputSource == ImGuiInputSource_NavGamepad)
8834             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down);
8835         if (move_delta.x != 0.0f || move_delta.y != 0.0f)
8836         {
8837             const float NAV_MOVE_SPEED = 800.0f;
8838             const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't code variable framerate very well
8839             SetWindowPos(g.NavWindowingTarget->RootWindow, g.NavWindowingTarget->RootWindow->Pos + move_delta * move_speed, ImGuiCond_Always);
8840             g.NavDisableMouseHover = true;
8841             MarkIniSettingsDirty(g.NavWindowingTarget);
8842         }
8843     }
8844 
8845     // Apply final focus
8846     if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
8847     {
8848         ClearActiveID();
8849         g.NavDisableHighlight = false;
8850         g.NavDisableMouseHover = true;
8851         apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
8852         ClosePopupsOverWindow(apply_focus_window, false);
8853         FocusWindow(apply_focus_window);
8854         if (apply_focus_window->NavLastIds[0] == 0)
8855             NavInitWindow(apply_focus_window, false);
8856 
8857         // If the window only has a menu layer, select it directly
8858         if (apply_focus_window->DC.NavLayerActiveMask == (1 << ImGuiNavLayer_Menu))
8859             g.NavLayer = ImGuiNavLayer_Menu;
8860     }
8861     if (apply_focus_window)
8862         g.NavWindowingTarget = NULL;
8863 
8864     // Apply menu/layer toggle
8865     if (apply_toggle_layer && g.NavWindow)
8866     {
8867         // Move to parent menu if necessary
8868         ImGuiWindow* new_nav_window = g.NavWindow;
8869         while (new_nav_window->ParentWindow
8870             && (new_nav_window->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) == 0
8871             && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
8872             && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
8873             new_nav_window = new_nav_window->ParentWindow;
8874         if (new_nav_window != g.NavWindow)
8875         {
8876             ImGuiWindow* old_nav_window = g.NavWindow;
8877             FocusWindow(new_nav_window);
8878             new_nav_window->NavLastChildNavWindow = old_nav_window;
8879         }
8880         g.NavDisableHighlight = false;
8881         g.NavDisableMouseHover = true;
8882 
8883         // When entering a regular menu bar with the Alt key, we always reinitialize the navigation ID.
8884         const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;
8885         NavRestoreLayer(new_nav_layer);
8886     }
8887 }
8888 
8889 // Window has already passed the IsWindowNavFocusable()
GetFallbackWindowNameForWindowingList(ImGuiWindow * window)8890 static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
8891 {
8892     if (window->Flags & ImGuiWindowFlags_Popup)
8893         return "(Popup)";
8894     if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
8895         return "(Main menu bar)";
8896     return "(Untitled)";
8897 }
8898 
8899 // Overlay displayed when using CTRL+TAB. Called by EndFrame().
NavUpdateWindowingOverlay()8900 void ImGui::NavUpdateWindowingOverlay()
8901 {
8902     ImGuiContext& g = *GImGui;
8903     IM_ASSERT(g.NavWindowingTarget != NULL);
8904 
8905     if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
8906         return;
8907 
8908     if (g.NavWindowingList == NULL)
8909         g.NavWindowingList = FindWindowByName("###NavWindowingList");
8910     SetNextWindowSizeConstraints(ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
8911     SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
8912     PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
8913     Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
8914     for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
8915     {
8916         ImGuiWindow* window = g.WindowsFocusOrder[n];
8917         if (!IsWindowNavFocusable(window))
8918             continue;
8919         const char* label = window->Name;
8920         if (label == FindRenderedTextEnd(label))
8921             label = GetFallbackWindowNameForWindowingList(window);
8922         Selectable(label, g.NavWindowingTarget == window);
8923     }
8924     End();
8925     PopStyleVar();
8926 }
8927 
8928 
8929 //-----------------------------------------------------------------------------
8930 // [SECTION] DRAG AND DROP
8931 //-----------------------------------------------------------------------------
8932 
ClearDragDrop()8933 void ImGui::ClearDragDrop()
8934 {
8935     ImGuiContext& g = *GImGui;
8936     g.DragDropActive = false;
8937     g.DragDropPayload.Clear();
8938     g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
8939     g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
8940     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
8941     g.DragDropAcceptFrameCount = -1;
8942 
8943     g.DragDropPayloadBufHeap.clear();
8944     memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
8945 }
8946 
8947 // Call when current ID is active.
8948 // 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)8949 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
8950 {
8951     ImGuiContext& g = *GImGui;
8952     ImGuiWindow* window = g.CurrentWindow;
8953 
8954     bool source_drag_active = false;
8955     ImGuiID source_id = 0;
8956     ImGuiID source_parent_id = 0;
8957     ImGuiMouseButton mouse_button = ImGuiMouseButton_Left;
8958     if (!(flags & ImGuiDragDropFlags_SourceExtern))
8959     {
8960         source_id = window->DC.LastItemId;
8961         if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case
8962             return false;
8963         if (g.IO.MouseDown[mouse_button] == false)
8964             return false;
8965 
8966         if (source_id == 0)
8967         {
8968             // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
8969             // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
8970             if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
8971             {
8972                 IM_ASSERT(0);
8973                 return false;
8974             }
8975 
8976             // Early out
8977             if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
8978                 return false;
8979 
8980             // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
8981             // We build a throwaway ID based on current ID stack + relative AABB of items in window.
8982             // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
8983             // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
8984             source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect);
8985             bool is_hovered = ItemHoverable(window->DC.LastItemRect, source_id);
8986             if (is_hovered && g.IO.MouseClicked[mouse_button])
8987             {
8988                 SetActiveID(source_id, window);
8989                 FocusWindow(window);
8990             }
8991             if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
8992                 g.ActiveIdAllowOverlap = is_hovered;
8993         }
8994         else
8995         {
8996             g.ActiveIdAllowOverlap = false;
8997         }
8998         if (g.ActiveId != source_id)
8999             return false;
9000         source_parent_id = window->IDStack.back();
9001         source_drag_active = IsMouseDragging(mouse_button);
9002     }
9003     else
9004     {
9005         window = NULL;
9006         source_id = ImHashStr("#SourceExtern");
9007         source_drag_active = true;
9008     }
9009 
9010     if (source_drag_active)
9011     {
9012         if (!g.DragDropActive)
9013         {
9014             IM_ASSERT(source_id != 0);
9015             ClearDragDrop();
9016             ImGuiPayload& payload = g.DragDropPayload;
9017             payload.SourceId = source_id;
9018             payload.SourceParentId = source_parent_id;
9019             g.DragDropActive = true;
9020             g.DragDropSourceFlags = flags;
9021             g.DragDropMouseButton = mouse_button;
9022         }
9023         g.DragDropSourceFrameCount = g.FrameCount;
9024         g.DragDropWithinSourceOrTarget = true;
9025 
9026         if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
9027         {
9028             // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
9029             // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
9030             BeginTooltip();
9031             if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
9032             {
9033                 ImGuiWindow* tooltip_window = g.CurrentWindow;
9034                 tooltip_window->SkipItems = true;
9035                 tooltip_window->HiddenFramesCanSkipItems = 1;
9036             }
9037         }
9038 
9039         if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
9040             window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
9041 
9042         return true;
9043     }
9044     return false;
9045 }
9046 
EndDragDropSource()9047 void ImGui::EndDragDropSource()
9048 {
9049     ImGuiContext& g = *GImGui;
9050     IM_ASSERT(g.DragDropActive);
9051     IM_ASSERT(g.DragDropWithinSourceOrTarget && "Not after a BeginDragDropSource()?");
9052 
9053     if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
9054         EndTooltip();
9055 
9056     // Discard the drag if have not called SetDragDropPayload()
9057     if (g.DragDropPayload.DataFrameCount == -1)
9058         ClearDragDrop();
9059     g.DragDropWithinSourceOrTarget = false;
9060 }
9061 
9062 // 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)9063 bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
9064 {
9065     ImGuiContext& g = *GImGui;
9066     ImGuiPayload& payload = g.DragDropPayload;
9067     if (cond == 0)
9068         cond = ImGuiCond_Always;
9069 
9070     IM_ASSERT(type != NULL);
9071     IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
9072     IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
9073     IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
9074     IM_ASSERT(payload.SourceId != 0);                               // Not called between BeginDragDropSource() and EndDragDropSource()
9075 
9076     if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
9077     {
9078         // Copy payload
9079         ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
9080         g.DragDropPayloadBufHeap.resize(0);
9081         if (data_size > sizeof(g.DragDropPayloadBufLocal))
9082         {
9083             // Store in heap
9084             g.DragDropPayloadBufHeap.resize((int)data_size);
9085             payload.Data = g.DragDropPayloadBufHeap.Data;
9086             memcpy(payload.Data, data, data_size);
9087         }
9088         else if (data_size > 0)
9089         {
9090             // Store locally
9091             memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
9092             payload.Data = g.DragDropPayloadBufLocal;
9093             memcpy(payload.Data, data, data_size);
9094         }
9095         else
9096         {
9097             payload.Data = NULL;
9098         }
9099         payload.DataSize = (int)data_size;
9100     }
9101     payload.DataFrameCount = g.FrameCount;
9102 
9103     return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
9104 }
9105 
BeginDragDropTargetCustom(const ImRect & bb,ImGuiID id)9106 bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
9107 {
9108     ImGuiContext& g = *GImGui;
9109     if (!g.DragDropActive)
9110         return false;
9111 
9112     ImGuiWindow* window = g.CurrentWindow;
9113     if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
9114         return false;
9115     IM_ASSERT(id != 0);
9116     if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
9117         return false;
9118     if (window->SkipItems)
9119         return false;
9120 
9121     IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
9122     g.DragDropTargetRect = bb;
9123     g.DragDropTargetId = id;
9124     g.DragDropWithinSourceOrTarget = true;
9125     return true;
9126 }
9127 
9128 // We don't use BeginDragDropTargetCustom() and duplicate its code because:
9129 // 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
9130 // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
9131 // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
BeginDragDropTarget()9132 bool ImGui::BeginDragDropTarget()
9133 {
9134     ImGuiContext& g = *GImGui;
9135     if (!g.DragDropActive)
9136         return false;
9137 
9138     ImGuiWindow* window = g.CurrentWindow;
9139     if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
9140         return false;
9141     if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
9142         return false;
9143 
9144     const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect;
9145     ImGuiID id = window->DC.LastItemId;
9146     if (id == 0)
9147         id = window->GetIDFromRectangle(display_rect);
9148     if (g.DragDropPayload.SourceId == id)
9149         return false;
9150 
9151     IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
9152     g.DragDropTargetRect = display_rect;
9153     g.DragDropTargetId = id;
9154     g.DragDropWithinSourceOrTarget = true;
9155     return true;
9156 }
9157 
IsDragDropPayloadBeingAccepted()9158 bool ImGui::IsDragDropPayloadBeingAccepted()
9159 {
9160     ImGuiContext& g = *GImGui;
9161     return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
9162 }
9163 
AcceptDragDropPayload(const char * type,ImGuiDragDropFlags flags)9164 const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
9165 {
9166     ImGuiContext& g = *GImGui;
9167     ImGuiWindow* window = g.CurrentWindow;
9168     ImGuiPayload& payload = g.DragDropPayload;
9169     IM_ASSERT(g.DragDropActive);                        // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
9170     IM_ASSERT(payload.DataFrameCount != -1);            // Forgot to call EndDragDropTarget() ?
9171     if (type != NULL && !payload.IsDataType(type))
9172         return NULL;
9173 
9174     // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
9175     // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
9176     const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
9177     ImRect r = g.DragDropTargetRect;
9178     float r_surface = r.GetWidth() * r.GetHeight();
9179     if (r_surface < g.DragDropAcceptIdCurrRectSurface)
9180     {
9181         g.DragDropAcceptFlags = flags;
9182         g.DragDropAcceptIdCurr = g.DragDropTargetId;
9183         g.DragDropAcceptIdCurrRectSurface = r_surface;
9184     }
9185 
9186     // Render default drop visuals
9187     payload.Preview = was_accepted_previously;
9188     flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
9189     if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
9190     {
9191         // FIXME-DRAG: Settle on a proper default visuals for drop target.
9192         r.Expand(3.5f);
9193         bool push_clip_rect = !window->ClipRect.Contains(r);
9194         if (push_clip_rect) window->DrawList->PushClipRect(r.Min-ImVec2(1,1), r.Max+ImVec2(1,1));
9195         window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f);
9196         if (push_clip_rect) window->DrawList->PopClipRect();
9197     }
9198 
9199     g.DragDropAcceptFrameCount = g.FrameCount;
9200     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()
9201     if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
9202         return NULL;
9203 
9204     return &payload;
9205 }
9206 
GetDragDropPayload()9207 const ImGuiPayload* ImGui::GetDragDropPayload()
9208 {
9209     ImGuiContext& g = *GImGui;
9210     return g.DragDropActive ? &g.DragDropPayload : NULL;
9211 }
9212 
9213 // We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
EndDragDropTarget()9214 void ImGui::EndDragDropTarget()
9215 {
9216     ImGuiContext& g = *GImGui;
9217     IM_ASSERT(g.DragDropActive);
9218     IM_ASSERT(g.DragDropWithinSourceOrTarget);
9219     g.DragDropWithinSourceOrTarget = false;
9220 }
9221 
9222 
9223 //-----------------------------------------------------------------------------
9224 // [SECTION] LOGGING/CAPTURING
9225 //-----------------------------------------------------------------------------
9226 // All text output from the interface can be captured into tty/file/clipboard.
9227 // By default, tree nodes are automatically opened during logging.
9228 //-----------------------------------------------------------------------------
9229 
9230 // Pass text data straight to log (without being displayed)
LogText(const char * fmt,...)9231 void ImGui::LogText(const char* fmt, ...)
9232 {
9233     ImGuiContext& g = *GImGui;
9234     if (!g.LogEnabled)
9235         return;
9236 
9237     va_list args;
9238     va_start(args, fmt);
9239     if (g.LogFile)
9240     {
9241         g.LogBuffer.Buf.resize(0);
9242         g.LogBuffer.appendfv(fmt, args);
9243         ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile);
9244     }
9245     else
9246     {
9247         g.LogBuffer.appendfv(fmt, args);
9248     }
9249     va_end(args);
9250 }
9251 
9252 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
9253 // We split text into individual lines to add current tree level padding
LogRenderedText(const ImVec2 * ref_pos,const char * text,const char * text_end)9254 void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
9255 {
9256     ImGuiContext& g = *GImGui;
9257     ImGuiWindow* window = g.CurrentWindow;
9258 
9259     if (!text_end)
9260         text_end = FindRenderedTextEnd(text, text_end);
9261 
9262     const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + 1);
9263     if (ref_pos)
9264         g.LogLinePosY = ref_pos->y;
9265     if (log_new_line)
9266         g.LogLineFirstItem = true;
9267 
9268     const char* text_remaining = text;
9269     if (g.LogDepthRef > window->DC.TreeDepth)  // Re-adjust padding if we have popped out of our starting depth
9270         g.LogDepthRef = window->DC.TreeDepth;
9271     const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);
9272     for (;;)
9273     {
9274         // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
9275         // We don't add a trailing \n to allow a subsequent item on the same line to be captured.
9276         const char* line_start = text_remaining;
9277         const char* line_end = ImStreolRange(line_start, text_end);
9278         const bool is_first_line = (line_start == text);
9279         const bool is_last_line = (line_end == text_end);
9280         if (!is_last_line || (line_start != line_end))
9281         {
9282             const int char_count = (int)(line_end - line_start);
9283             if (log_new_line || !is_first_line)
9284                 LogText(IM_NEWLINE "%*s%.*s", tree_depth * 4, "", char_count, line_start);
9285             else if (g.LogLineFirstItem)
9286                 LogText("%*s%.*s", tree_depth * 4, "", char_count, line_start);
9287             else
9288                 LogText(" %.*s", char_count, line_start);
9289             g.LogLineFirstItem = false;
9290         }
9291         else if (log_new_line)
9292         {
9293             // An empty "" string at a different Y position should output a carriage return.
9294             LogText(IM_NEWLINE);
9295             break;
9296         }
9297 
9298         if (is_last_line)
9299             break;
9300         text_remaining = line_end + 1;
9301     }
9302 }
9303 
9304 // Start logging/capturing text output
LogBegin(ImGuiLogType type,int auto_open_depth)9305 void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth)
9306 {
9307     ImGuiContext& g = *GImGui;
9308     ImGuiWindow* window = g.CurrentWindow;
9309     IM_ASSERT(g.LogEnabled == false);
9310     IM_ASSERT(g.LogFile == NULL);
9311     IM_ASSERT(g.LogBuffer.empty());
9312     g.LogEnabled = true;
9313     g.LogType = type;
9314     g.LogDepthRef = window->DC.TreeDepth;
9315     g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);
9316     g.LogLinePosY = FLT_MAX;
9317     g.LogLineFirstItem = true;
9318 }
9319 
LogToTTY(int auto_open_depth)9320 void ImGui::LogToTTY(int auto_open_depth)
9321 {
9322     ImGuiContext& g = *GImGui;
9323     if (g.LogEnabled)
9324         return;
9325     IM_UNUSED(auto_open_depth);
9326 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
9327     LogBegin(ImGuiLogType_TTY, auto_open_depth);
9328     g.LogFile = stdout;
9329 #endif
9330 }
9331 
9332 // Start logging/capturing text output to given file
LogToFile(int auto_open_depth,const char * filename)9333 void ImGui::LogToFile(int auto_open_depth, const char* filename)
9334 {
9335     ImGuiContext& g = *GImGui;
9336     if (g.LogEnabled)
9337         return;
9338 
9339     // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still
9340     // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE.
9341     // By opening the file in binary mode "ab" we have consistent output everywhere.
9342     if (!filename)
9343         filename = g.IO.LogFilename;
9344     if (!filename || !filename[0])
9345         return;
9346     ImFileHandle f = ImFileOpen(filename, "ab");
9347     if (!f)
9348     {
9349         IM_ASSERT(0);
9350         return;
9351     }
9352 
9353     LogBegin(ImGuiLogType_File, auto_open_depth);
9354     g.LogFile = f;
9355 }
9356 
9357 // Start logging/capturing text output to clipboard
LogToClipboard(int auto_open_depth)9358 void ImGui::LogToClipboard(int auto_open_depth)
9359 {
9360     ImGuiContext& g = *GImGui;
9361     if (g.LogEnabled)
9362         return;
9363     LogBegin(ImGuiLogType_Clipboard, auto_open_depth);
9364 }
9365 
LogToBuffer(int auto_open_depth)9366 void ImGui::LogToBuffer(int auto_open_depth)
9367 {
9368     ImGuiContext& g = *GImGui;
9369     if (g.LogEnabled)
9370         return;
9371     LogBegin(ImGuiLogType_Buffer, auto_open_depth);
9372 }
9373 
LogFinish()9374 void ImGui::LogFinish()
9375 {
9376     ImGuiContext& g = *GImGui;
9377     if (!g.LogEnabled)
9378         return;
9379 
9380     LogText(IM_NEWLINE);
9381     switch (g.LogType)
9382     {
9383     case ImGuiLogType_TTY:
9384 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
9385         fflush(g.LogFile);
9386 #endif
9387         break;
9388     case ImGuiLogType_File:
9389         ImFileClose(g.LogFile);
9390         break;
9391     case ImGuiLogType_Buffer:
9392         break;
9393     case ImGuiLogType_Clipboard:
9394         if (!g.LogBuffer.empty())
9395             SetClipboardText(g.LogBuffer.begin());
9396         break;
9397     case ImGuiLogType_None:
9398         IM_ASSERT(0);
9399         break;
9400     }
9401 
9402     g.LogEnabled = false;
9403     g.LogType = ImGuiLogType_None;
9404     g.LogFile = NULL;
9405     g.LogBuffer.clear();
9406 }
9407 
9408 // Helper to display logging buttons
9409 // FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!)
LogButtons()9410 void ImGui::LogButtons()
9411 {
9412     ImGuiContext& g = *GImGui;
9413 
9414     PushID("LogButtons");
9415 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
9416     const bool log_to_tty = Button("Log To TTY"); SameLine();
9417 #else
9418     const bool log_to_tty = false;
9419 #endif
9420     const bool log_to_file = Button("Log To File"); SameLine();
9421     const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
9422     PushAllowKeyboardFocus(false);
9423     SetNextItemWidth(80.0f);
9424     SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL);
9425     PopAllowKeyboardFocus();
9426     PopID();
9427 
9428     // Start logging at the end of the function so that the buttons don't appear in the log
9429     if (log_to_tty)
9430         LogToTTY();
9431     if (log_to_file)
9432         LogToFile();
9433     if (log_to_clipboard)
9434         LogToClipboard();
9435 }
9436 
9437 //-----------------------------------------------------------------------------
9438 // [SECTION] SETTINGS
9439 //-----------------------------------------------------------------------------
9440 
MarkIniSettingsDirty()9441 void ImGui::MarkIniSettingsDirty()
9442 {
9443     ImGuiContext& g = *GImGui;
9444     if (g.SettingsDirtyTimer <= 0.0f)
9445         g.SettingsDirtyTimer = g.IO.IniSavingRate;
9446 }
9447 
MarkIniSettingsDirty(ImGuiWindow * window)9448 void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
9449 {
9450     ImGuiContext& g = *GImGui;
9451     if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
9452         if (g.SettingsDirtyTimer <= 0.0f)
9453             g.SettingsDirtyTimer = g.IO.IniSavingRate;
9454 }
9455 
CreateNewWindowSettings(const char * name)9456 ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
9457 {
9458     ImGuiContext& g = *GImGui;
9459 
9460 #if !IMGUI_DEBUG_INI_SETTINGS
9461     // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
9462     // Preserve the full string when IMGUI_DEBUG_INI_SETTINGS is set to make .ini inspection easier.
9463     if (const char* p = strstr(name, "###"))
9464         name = p;
9465 #endif
9466     const size_t name_len = strlen(name);
9467 
9468     // Allocate chunk
9469     const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1;
9470     ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size);
9471     IM_PLACEMENT_NEW(settings) ImGuiWindowSettings();
9472     settings->ID = ImHashStr(name, name_len);
9473     memcpy(settings->GetName(), name, name_len + 1);   // Store with zero terminator
9474 
9475     return settings;
9476 }
9477 
FindWindowSettings(ImGuiID id)9478 ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
9479 {
9480     ImGuiContext& g = *GImGui;
9481     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
9482         if (settings->ID == id)
9483             return settings;
9484     return NULL;
9485 }
9486 
FindOrCreateWindowSettings(const char * name)9487 ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name)
9488 {
9489     if (ImGuiWindowSettings* settings = FindWindowSettings(ImHashStr(name)))
9490         return settings;
9491     return CreateNewWindowSettings(name);
9492 }
9493 
LoadIniSettingsFromDisk(const char * ini_filename)9494 void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
9495 {
9496     size_t file_data_size = 0;
9497     char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
9498     if (!file_data)
9499         return;
9500     LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
9501     IM_FREE(file_data);
9502 }
9503 
FindSettingsHandler(const char * type_name)9504 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
9505 {
9506     ImGuiContext& g = *GImGui;
9507     const ImGuiID type_hash = ImHashStr(type_name);
9508     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
9509         if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
9510             return &g.SettingsHandlers[handler_n];
9511     return NULL;
9512 }
9513 
9514 // Zero-tolerance, no error reporting, cheap .ini parsing
LoadIniSettingsFromMemory(const char * ini_data,size_t ini_size)9515 void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
9516 {
9517     ImGuiContext& g = *GImGui;
9518     IM_ASSERT(g.Initialized);
9519     IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
9520 
9521     // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
9522     // 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..
9523     if (ini_size == 0)
9524         ini_size = strlen(ini_data);
9525     char* buf = (char*)IM_ALLOC(ini_size + 1);
9526     char* buf_end = buf + ini_size;
9527     memcpy(buf, ini_data, ini_size);
9528     buf[ini_size] = 0;
9529 
9530     void* entry_data = NULL;
9531     ImGuiSettingsHandler* entry_handler = NULL;
9532 
9533     char* line_end = NULL;
9534     for (char* line = buf; line < buf_end; line = line_end + 1)
9535     {
9536         // Skip new lines markers, then find end of the line
9537         while (*line == '\n' || *line == '\r')
9538             line++;
9539         line_end = line;
9540         while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
9541             line_end++;
9542         line_end[0] = 0;
9543         if (line[0] == ';')
9544             continue;
9545         if (line[0] == '[' && line_end > line && line_end[-1] == ']')
9546         {
9547             // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
9548             line_end[-1] = 0;
9549             const char* name_end = line_end - 1;
9550             const char* type_start = line + 1;
9551             char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']');
9552             const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
9553             if (!type_end || !name_start)
9554                 continue;
9555             *type_end = 0; // Overwrite first ']'
9556             name_start++;  // Skip second '['
9557             entry_handler = FindSettingsHandler(type_start);
9558             entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
9559         }
9560         else if (entry_handler != NULL && entry_data != NULL)
9561         {
9562             // Let type handler parse the line
9563             entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
9564         }
9565     }
9566     IM_FREE(buf);
9567     g.SettingsLoaded = true;
9568 }
9569 
SaveIniSettingsToDisk(const char * ini_filename)9570 void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
9571 {
9572     ImGuiContext& g = *GImGui;
9573     g.SettingsDirtyTimer = 0.0f;
9574     if (!ini_filename)
9575         return;
9576 
9577     size_t ini_data_size = 0;
9578     const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
9579     ImFileHandle f = ImFileOpen(ini_filename, "wt");
9580     if (!f)
9581         return;
9582     ImFileWrite(ini_data, sizeof(char), ini_data_size, f);
9583     ImFileClose(f);
9584 }
9585 
9586 // Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
SaveIniSettingsToMemory(size_t * out_size)9587 const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
9588 {
9589     ImGuiContext& g = *GImGui;
9590     g.SettingsDirtyTimer = 0.0f;
9591     g.SettingsIniData.Buf.resize(0);
9592     g.SettingsIniData.Buf.push_back(0);
9593     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
9594     {
9595         ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
9596         handler->WriteAllFn(&g, handler, &g.SettingsIniData);
9597     }
9598     if (out_size)
9599         *out_size = (size_t)g.SettingsIniData.size();
9600     return g.SettingsIniData.c_str();
9601 }
9602 
WindowSettingsHandler_ReadOpen(ImGuiContext *,ImGuiSettingsHandler *,const char * name)9603 static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
9604 {
9605     ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHashStr(name));
9606     if (!settings)
9607         settings = ImGui::CreateNewWindowSettings(name);
9608     return (void*)settings;
9609 }
9610 
WindowSettingsHandler_ReadLine(ImGuiContext *,ImGuiSettingsHandler *,void * entry,const char * line)9611 static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
9612 {
9613     ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
9614     int x, y;
9615     int i;
9616     if (sscanf(line, "Pos=%i,%i", &x, &y) == 2)         settings->Pos = ImVec2ih((short)x, (short)y);
9617     else if (sscanf(line, "Size=%i,%i", &x, &y) == 2)   settings->Size = ImVec2ih((short)x, (short)y);
9618     else if (sscanf(line, "Collapsed=%d", &i) == 1)     settings->Collapsed = (i != 0);
9619 }
9620 
WindowSettingsHandler_WriteAll(ImGuiContext * ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)9621 static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
9622 {
9623     // Gather data from windows that were active during this session
9624     // (if a window wasn't opened in this session we preserve its settings)
9625     ImGuiContext& g = *ctx;
9626     for (int i = 0; i != g.Windows.Size; i++)
9627     {
9628         ImGuiWindow* window = g.Windows[i];
9629         if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
9630             continue;
9631 
9632         ImGuiWindowSettings* settings = (window->SettingsOffset != -1) ? g.SettingsWindows.ptr_from_offset(window->SettingsOffset) : ImGui::FindWindowSettings(window->ID);
9633         if (!settings)
9634         {
9635             settings = ImGui::CreateNewWindowSettings(window->Name);
9636             window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
9637         }
9638         IM_ASSERT(settings->ID == window->ID);
9639         settings->Pos = ImVec2ih((short)window->Pos.x, (short)window->Pos.y);
9640         settings->Size = ImVec2ih((short)window->SizeFull.x, (short)window->SizeFull.y);
9641         settings->Collapsed = window->Collapsed;
9642     }
9643 
9644     // Write to text buffer
9645     buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve
9646     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
9647     {
9648         const char* settings_name = settings->GetName();
9649         buf->appendf("[%s][%s]\n", handler->TypeName, settings_name);
9650         buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
9651         buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
9652         buf->appendf("Collapsed=%d\n", settings->Collapsed);
9653         buf->append("\n");
9654     }
9655 }
9656 
9657 
9658 //-----------------------------------------------------------------------------
9659 // [SECTION] VIEWPORTS, PLATFORM WINDOWS
9660 //-----------------------------------------------------------------------------
9661 
9662 // (this section is filled in the 'docking' branch)
9663 
9664 
9665 //-----------------------------------------------------------------------------
9666 // [SECTION] DOCKING
9667 //-----------------------------------------------------------------------------
9668 
9669 // (this section is filled in the 'docking' branch)
9670 
9671 
9672 //-----------------------------------------------------------------------------
9673 // [SECTION] PLATFORM DEPENDENT HELPERS
9674 //-----------------------------------------------------------------------------
9675 
9676 #if defined(_WIN32) && !defined(_WINDOWS_) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS))
9677 #ifndef WIN32_LEAN_AND_MEAN
9678 #define WIN32_LEAN_AND_MEAN
9679 #endif
9680 #ifndef __MINGW32__
9681 #include <Windows.h>
9682 #else
9683 #include <windows.h>
9684 #endif
9685 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) // UWP doesn't have Win32 functions
9686 #define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
9687 #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
9688 #endif
9689 #elif defined(__APPLE__)
9690 #include <TargetConditionals.h>
9691 #endif
9692 
9693 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
9694 
9695 #ifdef _MSC_VER
9696 #pragma comment(lib, "user32")
9697 #endif
9698 
9699 // Win32 clipboard implementation
GetClipboardTextFn_DefaultImpl(void *)9700 static const char* GetClipboardTextFn_DefaultImpl(void*)
9701 {
9702     static ImVector<char> buf_local;
9703     buf_local.clear();
9704     if (!::OpenClipboard(NULL))
9705         return NULL;
9706     HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
9707     if (wbuf_handle == NULL)
9708     {
9709         ::CloseClipboard();
9710         return NULL;
9711     }
9712     if (ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle))
9713     {
9714         int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
9715         buf_local.resize(buf_len);
9716         ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL);
9717     }
9718     ::GlobalUnlock(wbuf_handle);
9719     ::CloseClipboard();
9720     return buf_local.Data;
9721 }
9722 
SetClipboardTextFn_DefaultImpl(void *,const char * text)9723 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9724 {
9725     if (!::OpenClipboard(NULL))
9726         return;
9727     const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
9728     HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
9729     if (wbuf_handle == NULL)
9730     {
9731         ::CloseClipboard();
9732         return;
9733     }
9734     ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle);
9735     ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
9736     ::GlobalUnlock(wbuf_handle);
9737     ::EmptyClipboard();
9738     if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
9739         ::GlobalFree(wbuf_handle);
9740     ::CloseClipboard();
9741 }
9742 
9743 #elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS)
9744 
9745 #include <Carbon/Carbon.h>  // Use old API to avoid need for separate .mm file
9746 static PasteboardRef main_clipboard = 0;
9747 
9748 // OSX clipboard implementation
9749 // If you enable this you will need to add '-framework ApplicationServices' to your linker command-line!
SetClipboardTextFn_DefaultImpl(void *,const char * text)9750 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9751 {
9752     if (!main_clipboard)
9753         PasteboardCreate(kPasteboardClipboard, &main_clipboard);
9754     PasteboardClear(main_clipboard);
9755     CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text));
9756     if (cf_data)
9757     {
9758         PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0);
9759         CFRelease(cf_data);
9760     }
9761 }
9762 
GetClipboardTextFn_DefaultImpl(void *)9763 static const char* GetClipboardTextFn_DefaultImpl(void*)
9764 {
9765     if (!main_clipboard)
9766         PasteboardCreate(kPasteboardClipboard, &main_clipboard);
9767     PasteboardSynchronize(main_clipboard);
9768 
9769     ItemCount item_count = 0;
9770     PasteboardGetItemCount(main_clipboard, &item_count);
9771     for (ItemCount i = 0; i < item_count; i++)
9772     {
9773         PasteboardItemID item_id = 0;
9774         PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id);
9775         CFArrayRef flavor_type_array = 0;
9776         PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array);
9777         for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++)
9778         {
9779             CFDataRef cf_data;
9780             if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr)
9781             {
9782                 static ImVector<char> clipboard_text;
9783                 int length = (int)CFDataGetLength(cf_data);
9784                 clipboard_text.resize(length + 1);
9785                 CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)clipboard_text.Data);
9786                 clipboard_text[length] = 0;
9787                 CFRelease(cf_data);
9788                 return clipboard_text.Data;
9789             }
9790         }
9791     }
9792     return NULL;
9793 }
9794 
9795 #else
9796 
9797 // Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers.
GetClipboardTextFn_DefaultImpl(void *)9798 static const char* GetClipboardTextFn_DefaultImpl(void*)
9799 {
9800     ImGuiContext& g = *GImGui;
9801     return g.PrivateClipboard.empty() ? NULL : g.PrivateClipboard.begin();
9802 }
9803 
SetClipboardTextFn_DefaultImpl(void *,const char * text)9804 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9805 {
9806     ImGuiContext& g = *GImGui;
9807     g.PrivateClipboard.clear();
9808     const char* text_end = text + strlen(text);
9809     g.PrivateClipboard.resize((int)(text_end - text) + 1);
9810     memcpy(&g.PrivateClipboard[0], text, (size_t)(text_end - text));
9811     g.PrivateClipboard[(int)(text_end - text)] = 0;
9812 }
9813 
9814 #endif
9815 
9816 // Win32 API IME support (for Asian languages, etc.)
9817 #if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
9818 
9819 #include <imm.h>
9820 #ifdef _MSC_VER
9821 #pragma comment(lib, "imm32")
9822 #endif
9823 
ImeSetInputScreenPosFn_DefaultImpl(int x,int y)9824 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
9825 {
9826     // Notify OS Input Method Editor of text input position
9827     ImGuiIO& io = ImGui::GetIO();
9828     if (HWND hwnd = (HWND)io.ImeWindowHandle)
9829         if (HIMC himc = ::ImmGetContext(hwnd))
9830         {
9831             COMPOSITIONFORM cf;
9832             cf.ptCurrentPos.x = x;
9833             cf.ptCurrentPos.y = y;
9834             cf.dwStyle = CFS_FORCE_POSITION;
9835             ::ImmSetCompositionWindow(himc, &cf);
9836             ::ImmReleaseContext(hwnd, himc);
9837         }
9838 }
9839 
9840 #else
9841 
ImeSetInputScreenPosFn_DefaultImpl(int,int)9842 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
9843 
9844 #endif
9845 
9846 //-----------------------------------------------------------------------------
9847 // [SECTION] METRICS/DEBUG WINDOW
9848 //-----------------------------------------------------------------------------
9849 
9850 #ifndef IMGUI_DISABLE_METRICS_WINDOW
9851 // Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds.
MetricsHelpMarker(const char * desc)9852 static void MetricsHelpMarker(const char* desc)
9853 {
9854     ImGui::TextDisabled("(?)");
9855     if (ImGui::IsItemHovered())
9856     {
9857         ImGui::BeginTooltip();
9858         ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
9859         ImGui::TextUnformatted(desc);
9860         ImGui::PopTextWrapPos();
9861         ImGui::EndTooltip();
9862     }
9863 }
9864 
ShowMetricsWindow(bool * p_open)9865 void ImGui::ShowMetricsWindow(bool* p_open)
9866 {
9867     if (!ImGui::Begin("Dear ImGui Metrics", p_open))
9868     {
9869         ImGui::End();
9870         return;
9871     }
9872 
9873     // Debugging enums
9874     enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type
9875     const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentRegionRect" };
9876     enum { TRT_OuterRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersDesired, TRT_ColumnsContentRowsFrozen, TRT_ColumnsContentRowsUnfrozen, TRT_Count }; // Tables Rect Type
9877     const char* trt_rects_names[TRT_Count] = { "OuterRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersDesired", "ColumnsContentRowsFrozen", "ColumnsContentRowsUnfrozen" };
9878 
9879     // State
9880     static bool show_windows_rects = false;
9881     static int  show_windows_rect_type = WRT_WorkRect;
9882     static bool show_windows_begin_order = false;
9883     static bool show_tables_rects = false;
9884     static int  show_tables_rect_type = TRT_WorkRect;
9885     static bool show_drawcmd_details = true;
9886 
9887     // Basic info
9888     ImGuiContext& g = *GImGui;
9889     ImGuiIO& io = ImGui::GetIO();
9890     ImGui::Text("Dear ImGui %s", ImGui::GetVersion());
9891     ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
9892     ImGui::Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
9893     ImGui::Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
9894     ImGui::Text("%d active allocations", io.MetricsActiveAllocations);
9895     ImGui::Separator();
9896 
9897     // Helper functions to display common structures:
9898     // - NodeDrawList()
9899     // - NodeColumns()
9900     // - NodeWindow()
9901     // - NodeWindows()
9902     // - NodeTabBar()
9903     // - NodeStorage()
9904     struct Funcs
9905     {
9906         static ImRect GetWindowRect(ImGuiWindow* window, int rect_type)
9907         {
9908             if (rect_type == WRT_OuterRect)                 { return window->Rect(); }
9909             else if (rect_type == WRT_OuterRectClipped)     { return window->OuterRectClipped; }
9910             else if (rect_type == WRT_InnerRect)            { return window->InnerRect; }
9911             else if (rect_type == WRT_InnerClipRect)        { return window->InnerClipRect; }
9912             else if (rect_type == WRT_WorkRect)             { return window->WorkRect; }
9913             else if (rect_type == WRT_Content)              { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
9914             else if (rect_type == WRT_ContentRegionRect)    { return window->ContentRegionRect; }
9915             IM_ASSERT(0);
9916             return ImRect();
9917         }
9918 
9919         static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label)
9920         {
9921             bool node_open = ImGui::TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, draw_list->CmdBuffer.Size);
9922             if (draw_list == ImGui::GetWindowDrawList())
9923             {
9924                 ImGui::SameLine();
9925                 ImGui::TextColored(ImVec4(1.0f,0.4f,0.4f,1.0f), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
9926                 if (node_open) ImGui::TreePop();
9927                 return;
9928             }
9929 
9930             ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list
9931             if (window && IsItemHovered())
9932                 fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
9933             if (!node_open)
9934                 return;
9935 
9936             if (window && !window->WasActive)
9937                 ImGui::TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!");
9938 
9939             unsigned int elem_offset = 0;
9940             for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
9941             {
9942                 if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0)
9943                     continue;
9944                 if (pcmd->UserCallback)
9945                 {
9946                     ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
9947                     continue;
9948                 }
9949 
9950                 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
9951                 char buf[300];
9952                 ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd: %4d triangles, Tex 0x%p, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
9953                     pcmd->ElemCount/3, (void*)(intptr_t)pcmd->TextureId,
9954                     pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
9955                 bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf);
9956                 if (show_drawcmd_details && fg_draw_list && ImGui::IsItemHovered())
9957                 {
9958                     ImRect clip_rect = pcmd->ClipRect;
9959                     ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
9960                     for (unsigned int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++)
9961                         vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos);
9962                     fg_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255,0,255,255));
9963                     fg_draw_list->AddRect(ImFloor(vtxs_rect.Min), ImFloor(vtxs_rect.Max), IM_COL32(255,255,0,255));
9964                 }
9965                 if (!pcmd_node_open)
9966                     continue;
9967 
9968                 // Calculate approximate coverage area (touched pixel count)
9969                 // This will be in pixels squared as long there's no post-scaling happening to the renderer output.
9970                 float total_area = 0.0f;
9971                 for (unsigned int base_idx = elem_offset; base_idx < (elem_offset + pcmd->ElemCount); base_idx += 3)
9972                 {
9973                     ImVec2 triangle[3];
9974                     for (int n = 0; n < 3; n++)
9975                         triangle[n] = draw_list->VtxBuffer[idx_buffer ? idx_buffer[base_idx + n] : (base_idx + n)].pos;
9976                     total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]);
9977                 }
9978 
9979                 // Display vertex information summary. Hover to get all triangles drawn in wire-frame
9980                 ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area);
9981                 ImGui::Selectable(buf);
9982                 if (fg_draw_list && ImGui::IsItemHovered() && show_drawcmd_details)
9983                 {
9984                     // Draw wire-frame version of everything
9985                     ImDrawListFlags backup_flags = fg_draw_list->Flags;
9986                     fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
9987                     ImRect clip_rect = pcmd->ClipRect;
9988                     fg_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255, 0, 255, 255));
9989                     for (unsigned int base_idx = elem_offset; base_idx < (elem_offset + pcmd->ElemCount); base_idx += 3)
9990                     {
9991                         ImVec2 triangle[3];
9992                         for (int n = 0; n < 3; n++)
9993                             triangle[n] = draw_list->VtxBuffer[idx_buffer ? idx_buffer[base_idx + n] : (base_idx + n)].pos;
9994                         fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), true, 1.0f);
9995                     }
9996                     fg_draw_list->Flags = backup_flags;
9997                 }
9998 
9999                 // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
10000                 ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
10001                 while (clipper.Step())
10002                     for (int prim = clipper.DisplayStart, idx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++)
10003                     {
10004                         char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf);
10005                         ImVec2 triangle[3];
10006                         for (int n = 0; n < 3; n++, idx_i++)
10007                         {
10008                             ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[idx_i] : idx_i];
10009                             triangle[n] = v.pos;
10010                             buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n",
10011                                 (n == 0) ? "Vert:" : "     ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
10012                         }
10013 
10014                         ImGui::Selectable(buf, false);
10015                         if (fg_draw_list && ImGui::IsItemHovered())
10016                         {
10017                             ImDrawListFlags backup_flags = fg_draw_list->Flags;
10018                             fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
10019                             fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255,255,0,255), true, 1.0f);
10020                             fg_draw_list->Flags = backup_flags;
10021                         }
10022                     }
10023                 ImGui::TreePop();
10024             }
10025             ImGui::TreePop();
10026         }
10027 
10028         static void NodeColumns(const ImGuiColumns* columns)
10029         {
10030             if (!ImGui::TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
10031                 return;
10032             ImGui::BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX);
10033             for (int column_n = 0; column_n < columns->Columns.Size; column_n++)
10034                 ImGui::BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, GetColumnOffsetFromNorm(columns, columns->Columns[column_n].OffsetNorm));
10035             ImGui::TreePop();
10036         }
10037 
10038         static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label)
10039         {
10040             if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size))
10041                 return;
10042             for (int i = 0; i < windows.Size; i++)
10043             {
10044                 ImGui::PushID(windows[i]);
10045                 Funcs::NodeWindow(windows[i], "Window");
10046                 ImGui::PopID();
10047             }
10048             ImGui::TreePop();
10049         }
10050 
10051         static void NodeWindow(ImGuiWindow* window, const char* label)
10052         {
10053             if (window == NULL)
10054             {
10055                 ImGui::BulletText("%s: NULL", label);
10056                 return;
10057             }
10058             bool open = ImGui::TreeNode(label, "%s '%s', %d @ 0x%p", label, window->Name, (window->Active || window->WasActive), window);
10059             if (ImGui::IsItemHovered() && window->WasActive)
10060                 ImGui::GetForegroundDrawList()->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
10061             if (!open)
10062                 return;
10063             ImGuiWindowFlags flags = window->Flags;
10064             NodeDrawList(window, window->DrawList, "DrawList");
10065             ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y);
10066             ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
10067                 (flags & ImGuiWindowFlags_ChildWindow)  ? "Child " : "",      (flags & ImGuiWindowFlags_Tooltip)     ? "Tooltip "   : "",  (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
10068                 (flags & ImGuiWindowFlags_Modal)        ? "Modal " : "",      (flags & ImGuiWindowFlags_ChildMenu)   ? "ChildMenu " : "",  (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
10069                 (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
10070             ImGui::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" : "");
10071             ImGui::BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
10072             ImGui::BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems);
10073             ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask);
10074             ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
10075             if (!window->NavRectRel[0].IsInverted())
10076                 ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y);
10077             else
10078                 ImGui::BulletText("NavRectRel[0]: <None>");
10079             if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
10080             if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow");
10081             if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
10082             if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
10083             {
10084                 for (int n = 0; n < window->ColumnsStorage.Size; n++)
10085                     NodeColumns(&window->ColumnsStorage[n]);
10086                 ImGui::TreePop();
10087             }
10088             NodeStorage(&window->StateStorage, "Storage");
10089             ImGui::TreePop();
10090         }
10091 
10092         static void NodeTabBar(ImGuiTabBar* tab_bar)
10093         {
10094             // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
10095             char buf[256];
10096             char* p = buf;
10097             const char* buf_end = buf + IM_ARRAYSIZE(buf);
10098             p += ImFormatString(p, buf_end - p, "TabBar (%d tabs)%s", tab_bar->Tabs.Size, (tab_bar->PrevFrameVisible < ImGui::GetFrameCount() - 2) ? " *Inactive*" : "");
10099             if (ImGui::TreeNode(tab_bar, "%s", buf))
10100             {
10101                 for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
10102                 {
10103                     const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
10104                     ImGui::PushID(tab);
10105                     if (ImGui::SmallButton("<")) { TabBarQueueChangeTabOrder(tab_bar, tab, -1); } ImGui::SameLine(0, 2);
10106                     if (ImGui::SmallButton(">")) { TabBarQueueChangeTabOrder(tab_bar, tab, +1); } ImGui::SameLine();
10107                     ImGui::Text("%02d%c Tab 0x%08X '%s'", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "");
10108                     ImGui::PopID();
10109                 }
10110                 ImGui::TreePop();
10111             }
10112         }
10113 
10114         static void NodeStorage(ImGuiStorage* storage, const char* label)
10115         {
10116             if (!ImGui::TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes()))
10117                 return;
10118             for (int n = 0; n < storage->Data.Size; n++)
10119             {
10120                 const ImGuiStorage::ImGuiStoragePair& p = storage->Data[n];
10121                 ImGui::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.
10122             }
10123             ImGui::TreePop();
10124         }
10125     };
10126 
10127     Funcs::NodeWindows(g.Windows, "Windows");
10128     //Funcs::NodeWindows(g.WindowsFocusOrder, "WindowsFocusOrder");
10129     if (ImGui::TreeNode("DrawLists", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size))
10130     {
10131         for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++)
10132             Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList");
10133         ImGui::TreePop();
10134     }
10135 
10136     // Details for Popups
10137     if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
10138     {
10139         for (int i = 0; i < g.OpenPopupStack.Size; i++)
10140         {
10141             ImGuiWindow* window = g.OpenPopupStack[i].Window;
10142             ImGui::BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenPopupStack[i].PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : "");
10143         }
10144         ImGui::TreePop();
10145     }
10146 
10147     // Details for TabBars
10148     if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetSize()))
10149     {
10150         for (int n = 0; n < g.TabBars.GetSize(); n++)
10151             Funcs::NodeTabBar(g.TabBars.GetByIndex(n));
10152         ImGui::TreePop();
10153     }
10154 
10155     // Details for Tables
10156     IM_UNUSED(trt_rects_names);
10157     IM_UNUSED(show_tables_rects);
10158     IM_UNUSED(show_tables_rect_type);
10159 #ifdef IMGUI_HAS_TABLE
10160     if (ImGui::TreeNode("Tables", "Tables (%d)", g.Tables.GetSize()))
10161     {
10162         for (int n = 0; n < g.Tables.GetSize(); n++)
10163             Funcs::NodeTable(g.Tables.GetByIndex(n));
10164         ImGui::TreePop();
10165     }
10166 #endif // #define IMGUI_HAS_TABLE
10167 
10168     // Details for Docking
10169 #ifdef IMGUI_HAS_DOCK
10170     if (ImGui::TreeNode("Docking"))
10171     {
10172         ImGui::TreePop();
10173     }
10174 #endif // #define IMGUI_HAS_DOCK
10175 
10176     // Misc Details
10177     if (ImGui::TreeNode("Internal state"))
10178     {
10179         const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
10180         ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
10181         ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
10182         ImGui::Text("HoveredId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not
10183         ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, input_source_names[g.ActiveIdSource]);
10184         ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
10185         ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
10186         ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
10187         ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
10188         ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
10189         ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
10190         ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
10191         ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
10192         ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
10193         ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
10194         ImGui::TreePop();
10195     }
10196 
10197     // Tools
10198     if (ImGui::TreeNode("Tools"))
10199     {
10200         // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
10201         if (ImGui::Button("Item Picker.."))
10202             ImGui::DebugStartItemPicker();
10203         ImGui::SameLine();
10204         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.");
10205 
10206         ImGui::Checkbox("Show windows begin order", &show_windows_begin_order);
10207         ImGui::Checkbox("Show windows rectangles", &show_windows_rects);
10208         ImGui::SameLine();
10209         ImGui::SetNextItemWidth(ImGui::GetFontSize() * 12);
10210         show_windows_rects |= ImGui::Combo("##show_windows_rect_type", &show_windows_rect_type, wrt_rects_names, WRT_Count, WRT_Count);
10211         if (show_windows_rects && g.NavWindow)
10212         {
10213             ImGui::BulletText("'%s':", g.NavWindow->Name);
10214             ImGui::Indent();
10215             for (int rect_n = 0; rect_n < WRT_Count; rect_n++)
10216             {
10217                 ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n);
10218                 ImGui::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]);
10219             }
10220             ImGui::Unindent();
10221         }
10222         ImGui::Checkbox("Show details when hovering ImDrawCmd node", &show_drawcmd_details);
10223         ImGui::TreePop();
10224     }
10225 
10226     // Overlay: Display windows Rectangles and Begin Order
10227     if (show_windows_rects || show_windows_begin_order)
10228     {
10229         for (int n = 0; n < g.Windows.Size; n++)
10230         {
10231             ImGuiWindow* window = g.Windows[n];
10232             if (!window->WasActive)
10233                 continue;
10234             ImDrawList* draw_list = GetForegroundDrawList(window);
10235             if (show_windows_rects)
10236             {
10237                 ImRect r = Funcs::GetWindowRect(window, show_windows_rect_type);
10238                 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
10239             }
10240             if (show_windows_begin_order && !(window->Flags & ImGuiWindowFlags_ChildWindow))
10241             {
10242                 char buf[32];
10243                 ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
10244                 float font_size = ImGui::GetFontSize();
10245                 draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
10246                 draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf);
10247             }
10248         }
10249     }
10250 
10251 #ifdef IMGUI_HAS_TABLE
10252     // Overlay: Display Tables Rectangles
10253     if (show_tables_rects)
10254     {
10255         for (int table_n = 0; table_n < g.Tables.GetSize(); table_n++)
10256         {
10257             ImGuiTable* table = g.Tables.GetByIndex(table_n);
10258         }
10259     }
10260 #endif // #define IMGUI_HAS_TABLE
10261 
10262 #ifdef IMGUI_HAS_DOCK
10263     // Overlay: Display Docking info
10264     if (show_docking_nodes && g.IO.KeyCtrl)
10265     {
10266     }
10267 #endif // #define IMGUI_HAS_DOCK
10268 
10269     ImGui::End();
10270 }
10271 
10272 #else
10273 
ShowMetricsWindow(bool *)10274 void ImGui::ShowMetricsWindow(bool*) { }
10275 
10276 #endif
10277 
10278 //-----------------------------------------------------------------------------
10279 
10280 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
10281 // 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.
10282 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
10283 #include "imgui_user.inl"
10284 #endif
10285 
10286 //-----------------------------------------------------------------------------
10287 
10288 #endif // #ifndef IMGUI_DISABLE
10289