1 // dear imgui, v1.74 WIP
2 // (main code and documentation)
3 
4 // Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code.
5 // Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase.
6 // Get latest version at https://github.com/ocornut/imgui
7 // Releases change-log at https://github.com/ocornut/imgui/releases
8 // Technical Support for Getting Started https://github.com/ocornut/imgui/wiki
9 // Gallery (please post your screenshots/video there!): https://github.com/ocornut/imgui/issues/2847
10 
11 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
12 // See LICENSE.txt for copyright and licensing details (standard MIT License).
13 // This library is free but I need your support to sustain development and maintenance.
14 // Businesses: you can support continued maintenance and development via support contracts or sponsoring, see docs/README.
15 // Individuals: you can support continued maintenance and development via donations or Patreon https://www.patreon.com/imgui.
16 
17 // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
18 // Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
19 // modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
20 // come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
21 // to a better solution or official support for them.
22 
23 /*
24 
25 Index of this file:
26 
27 DOCUMENTATION
28 
29 - MISSION STATEMENT
30 - END-USER GUIDE
31 - PROGRAMMER GUIDE
32   - Read first.
33   - How to update to a newer version of Dear ImGui.
34   - Getting started with integrating Dear ImGui in your code/engine.
35   - This is how a simple application may look like (2 variations).
36   - This is how a simple rendering function may look like.
37   - Using gamepad/keyboard navigation controls.
38 - API BREAKING CHANGES (read me when you update!)
39 - FREQUENTLY ASKED QUESTIONS (FAQ)
40   - Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer)
41 
42 CODE
43 (search for "[SECTION]" in the code to find them)
44 
45 // [SECTION] FORWARD DECLARATIONS
46 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
47 // [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
48 // [SECTION] MISC HELPERS/UTILITIES (Maths, String, Format, Hash, File functions)
49 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
50 // [SECTION] MISC HELPERS/UTILITIES (Color functions)
51 // [SECTION] ImGuiStorage
52 // [SECTION] ImGuiTextFilter
53 // [SECTION] ImGuiTextBuffer
54 // [SECTION] ImGuiListClipper
55 // [SECTION] RENDER HELPERS
56 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
57 // [SECTION] SCROLLING
58 // [SECTION] TOOLTIPS
59 // [SECTION] POPUPS
60 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
61 // [SECTION] DRAG AND DROP
62 // [SECTION] LOGGING/CAPTURING
63 // [SECTION] SETTINGS
64 // [SECTION] PLATFORM DEPENDENT HELPERS
65 // [SECTION] METRICS/DEBUG WINDOW
66 
67 */
68 
69 //-----------------------------------------------------------------------------
70 // DOCUMENTATION
71 //-----------------------------------------------------------------------------
72 
73 /*
74 
75  MISSION STATEMENT
76  =================
77 
78  - Easy to use to create code-driven and data-driven tools.
79  - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
80  - Easy to hack and improve.
81  - Minimize screen real-estate usage.
82  - Minimize setup and maintenance.
83  - Minimize state storage on user side.
84  - Portable, minimize dependencies, run on target (consoles, phones, etc.).
85  - Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window,.
86    opening a tree node for the first time, etc. but a typical frame should not allocate anything).
87 
88  Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
89  - Doesn't look fancy, doesn't animate.
90  - Limited layout features, intricate layouts are typically crafted in code.
91 
92 
93  END-USER GUIDE
94  ==============
95 
96  - Double-click on title bar to collapse window.
97  - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin().
98  - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents).
99  - Click and drag on any empty space to move window.
100  - TAB/SHIFT+TAB to cycle through keyboard editable fields.
101  - CTRL+Click on a slider or drag box to input value as text.
102  - Use mouse wheel to scroll.
103  - Text editor:
104    - Hold SHIFT or use mouse to select text.
105    - CTRL+Left/Right to word jump.
106    - CTRL+Shift+Left/Right to select words.
107    - CTRL+A our Double-Click to select all.
108    - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
109    - CTRL+Z,CTRL+Y to undo/redo.
110    - ESCAPE to revert text to its original value.
111    - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
112    - Controls are automatically adjusted for OSX to match standard OSX text editing operations.
113  - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard.
114  - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://goo.gl/9LgVZW
115 
116 
117  PROGRAMMER GUIDE
118  ================
119 
120  READ FIRST:
121 
122  - Remember to read the FAQ (https://www.dearimgui.org/faq)
123  - 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
124    or destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, less bugs.
125  - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
126  - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
127  - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
128    You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links docs/README.md.
129  - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
130    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,
131    where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
132  - Our origin are on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
133  - This codebase is also optimized to yield decent performances with typical "Debug" builds settings.
134  - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).
135    If you get an assert, read the messages and comments around the assert.
136  - 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.
137  - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
138    See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
139    However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase.
140  - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!).
141 
142  HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI:
143 
144  - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
145  - Or maintain your own branch where you have imconfig.h modified.
146  - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
147    If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
148    from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
149    likely be a comment about it. Please report any issue to the GitHub page!
150  - Try to keep your copy of dear imgui reasonably up to date.
151 
152  GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE:
153 
154  - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
155  - Add the Dear ImGui source files to your projects or using your preferred build system.
156    It is recommended you build and statically link the .cpp files as part of your project and not as shared library (DLL).
157  - 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.
158  - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
159  - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
160    Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
161    phases of your own application. All rendering information are stored into command-lists that you will retrieve after calling ImGui::Render().
162  - Refer to the bindings and demo applications in the examples/ folder for instruction on how to setup your code.
163  - 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.
164 
165  HOW A SIMPLE APPLICATION MAY LOOK LIKE:
166  EXHIBIT 1: USING THE EXAMPLE BINDINGS (imgui_impl_XXX.cpp files from the examples/ folder).
167 
168      // Application init: create a dear imgui context, setup some options, load fonts
169      ImGui::CreateContext();
170      ImGuiIO& io = ImGui::GetIO();
171      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
172      // TODO: Fill optional fields of the io structure later.
173      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
174 
175      // Initialize helper Platform and Renderer bindings (here we are using imgui_impl_win32 and imgui_impl_dx11)
176      ImGui_ImplWin32_Init(hwnd);
177      ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
178 
179      // Application main loop
180      while (true)
181      {
182          // Feed inputs to dear imgui, start new frame
183          ImGui_ImplDX11_NewFrame();
184          ImGui_ImplWin32_NewFrame();
185          ImGui::NewFrame();
186 
187          // Any application code here
188          ImGui::Text("Hello, world!");
189 
190          // Render dear imgui into screen
191          ImGui::Render();
192          ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
193          g_pSwapChain->Present(1, 0);
194      }
195 
196      // Shutdown
197      ImGui_ImplDX11_Shutdown();
198      ImGui_ImplWin32_Shutdown();
199      ImGui::DestroyContext();
200 
201  HOW A SIMPLE APPLICATION MAY LOOK LIKE:
202  EXHIBIT 2: IMPLEMENTING CUSTOM BINDING / CUSTOM ENGINE.
203 
204      // Application init: create a dear imgui context, setup some options, load fonts
205      ImGui::CreateContext();
206      ImGuiIO& io = ImGui::GetIO();
207      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
208      // TODO: Fill optional fields of the io structure later.
209      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
210 
211      // Build and load the texture atlas into a texture
212      // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
213      int width, height;
214      unsigned char* pixels = NULL;
215      io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
216 
217      // At this point you've got the texture data and you need to upload that your your graphic system:
218      // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
219      // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID.
220      MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
221      io.Fonts->TexID = (void*)texture;
222 
223      // Application main loop
224      while (true)
225      {
226         // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
227         // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform bindings)
228         io.DeltaTime = 1.0f/60.0f;              // set the time elapsed since the previous frame (in seconds)
229         io.DisplaySize.x = 1920.0f;             // set the current display width
230         io.DisplaySize.y = 1280.0f;             // set the current display height here
231         io.MousePos = my_mouse_pos;             // set the mouse position
232         io.MouseDown[0] = my_mouse_buttons[0];  // set the mouse button states
233         io.MouseDown[1] = my_mouse_buttons[1];
234 
235         // Call NewFrame(), after this point you can use ImGui::* functions anytime
236         // (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use Dear ImGui everywhere)
237         ImGui::NewFrame();
238 
239         // Most of your application code here
240         ImGui::Text("Hello, world!");
241         MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
242         MyGameRender(); // may use any Dear ImGui functions as well!
243 
244         // Render dear imgui, swap buffers
245         // (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)
246         ImGui::EndFrame();
247         ImGui::Render();
248         ImDrawData* draw_data = ImGui::GetDrawData();
249         MyImGuiRenderFunction(draw_data);
250         SwapBuffers();
251      }
252 
253      // Shutdown
254      ImGui::DestroyContext();
255 
256  HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE:
257 
258     void void MyImGuiRenderFunction(ImDrawData* draw_data)
259     {
260        // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
261        // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
262        // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
263        // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
264        for (int n = 0; n < draw_data->CmdListsCount; n++)
265        {
266           const ImDrawList* cmd_list = draw_data->CmdLists[n];
267           const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;  // vertex buffer generated by Dear ImGui
268           const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;   // index buffer generated by Dear ImGui
269           for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
270           {
271              const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
272              if (pcmd->UserCallback)
273              {
274                  pcmd->UserCallback(cmd_list, pcmd);
275              }
276              else
277              {
278                  // The texture for the draw call is specified by pcmd->TextureId.
279                  // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization.
280                  MyEngineBindTexture((MyTexture*)pcmd->TextureId);
281 
282                  // We are using scissoring to clip some objects. All low-level graphics API should supports it.
283                  // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
284                  //   (some elements visible outside their bounds) but you can fix that once everything else works!
285                  // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize)
286                  //   In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize.
287                  //   However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github),
288                  //   always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
289                  // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
290                  ImVec2 pos = draw_data->DisplayPos;
291                  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));
292 
293                  // Render 'pcmd->ElemCount/3' indexed triangles.
294                  // By default the indices ImDrawIdx are 16-bits, you can change them to 32-bits in imconfig.h if your engine doesn't support 16-bits indices.
295                  MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
296              }
297              idx_buffer += pcmd->ElemCount;
298           }
299        }
300     }
301 
302  - The examples/ folders contains many actual implementation of the pseudo-codes above.
303  - When calling NewFrame(), the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags are updated.
304    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
305    rest of your application. In every cases you need to pass on the inputs to Dear ImGui.
306  - Refer to the FAQ for more information. Amusingly, it is called a FAQ because people frequently run into the same issues!
307 
308  USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
309 
310  - The gamepad/keyboard navigation is fairly functional and keeps being improved.
311  - Gamepad support is particularly useful to use dear imgui on a console system (e.g. PS4, Switch, XB1) without a mouse!
312  - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787
313  - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
314  - Gamepad:
315     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
316     - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
317       Note that io.NavInputs[] is cleared by EndFrame().
318     - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
319          0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
320     - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
321       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.).
322     - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://goo.gl/9LgVZW.
323     - 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
324       to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
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  - Mouse:
335     - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
336     - 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.
337     - 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.
338       Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
339       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.
340       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.
341       (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!)
342       (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
343        to set a boolean to ignore your other external mouse positions until the external source is moved again.)
344 
345 
346  API BREAKING CHANGES
347  ====================
348 
349  Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
350  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.
351  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.
352  You can read releases logs https://github.com/ocornut/imgui/releases for more details.
353 
354  - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017): Begin() (5 arguments signature), IsRootWindowOrAnyChildHovered(), AlignFirstTextHeightToWidgets(), SetNextWindowPosCenter(), ImFont::Glyph. Grep this log for details and new names, or see how they were implemented until 1.73.
355  - 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.
356                        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.
357                        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).
358                        If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you.
359  - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete).
360  - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete).
361  - 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.
362  - 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
363                        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.
364                        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.
365                        Please reach out if you are affected.
366  - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete).
367  - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c).
368  - 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.
369  - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).
370  - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).
371  - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).
372  - 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!
373  - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
374  - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
375  - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).
376  - 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.
377  - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
378  - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
379  - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
380  - 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.
381                        If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
382  - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
383  - 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.
384                        NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
385                        Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
386  - 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).
387  - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
388  - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
389  - 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.
390  - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
391  - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
392  - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
393  - 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.).
394                        old bindings will still work as is, however prefer using the separated bindings as they will be updated to support multi-viewports.
395                        when adopting new bindings follow the main.cpp code of your preferred examples/ folder to know which functions to call.
396                        in particular, note that old bindings called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function.
397  - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
398  - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
399  - 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.
400                        If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
401                        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.
402                        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.
403  - 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",
404                        consistent with other functions. Kept redirection functions (will obsolete).
405  - 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.
406  - 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).
407  - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
408  - 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.
409  - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
410  - 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.
411  - 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.
412  - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
413                        - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
414                        - removed Shutdown() function, as DestroyContext() serve this purpose.
415                        - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
416                        - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
417                        - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
418  - 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.
419  - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
420  - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
421  - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
422  - 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.
423  - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
424  - 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
425  - 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.
426  - 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.
427  - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
428  - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
429                      - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
430  - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
431  - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
432  - 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.
433  - 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.
434                        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.
435  - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
436  - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
437  - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
438  - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
439  - 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.
440  - 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.
441  - 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.
442                        removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
443                          IsItemHoveredRect()        --> IsItemHovered(ImGuiHoveredFlags_RectOnly)
444                          IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
445                          IsMouseHoveringWindow()    --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior]
446  - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
447  - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
448  - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).
449  - 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).
450  - 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)".
451  - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
452                      - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
453                      - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
454  - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
455  - 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.
456  - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type.
457  - 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.
458  - 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).
459  - 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).
460  - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
461  - 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.
462                      - 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.
463                      - 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))'
464  - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
465  - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
466  - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
467  - 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().
468  - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
469  - 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.
470  - 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.
471  - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
472                        If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you.
473                        If your TitleBg/TitleBgActive alpha was <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
474                        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.
475                            ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col)
476                            {
477                                float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a;
478                                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);
479                            }
480                        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.
481  - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
482  - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
483  - 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).
484  - 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.
485  - 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).
486  - 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)
487  - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
488  - 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.
489  - 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.
490  - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
491  - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
492  - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
493                        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.
494                        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!
495  - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
496  - 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.
497  - 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
498  - 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.
499                        you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
500  - 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.
501                        this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
502                      - 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.
503                      - the signature of the io.RenderDrawListsFn handler has changed!
504                        old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
505                        new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
506                          parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
507                          ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
508                          ImDrawCmd:  'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
509                      - 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.
510                      - 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!
511                      - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
512  - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
513  - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
514  - 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.
515  - 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
516  - 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!
517  - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
518  - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
519  - 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.
520  - 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.
521  - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
522  - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
523  - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
524  - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
525  - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
526  - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
527  - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
528  - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
529  - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
530  - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
531  - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
532  - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
533  - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
534  - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
535  - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
536  - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
537  - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
538               (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
539                        font init:  { const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); <..Upload texture to GPU..>; }
540                        became:     { unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); <..Upload texture to GPU>; io.Fonts->TexId = YourTextureIdentifier; }
541                        you now have more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs.
542                        it is now recommended that you sample the font texture with bilinear interpolation.
543               (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
544               (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
545               (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
546  - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
547  - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
548  - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
549  - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
550  - 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)
551  - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
552  - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
553  - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
554  - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
555  - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
556  - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
557 
558 
559  FREQUENTLY ASKED QUESTIONS (FAQ)
560  ================================
561 
562  Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer)
563  Some answers are copied down here to facilitate searching in code.
564 
565  Q&A: Basics
566  ===========
567 
568  Q: Where is the documentation?
569  A: This library is poorly documented at the moment and expects of the user to be acquainted with C/C++.
570     - Run the examples/ and explore them.
571     - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function.
572     - The demo covers most features of Dear ImGui, so you can read the code and see its output.
573     - See documentation and comments at the top of imgui.cpp + effectively imgui.h.
574     - Dozens of standalone example applications using e.g. OpenGL/DirectX are provided in the examples/
575       folder to explain how to integrate Dear ImGui with your own engine/application.
576     - Your programming IDE is your friend, find the type or function declaration to find comments
577       associated to it.
578 
579  Q: Which version should I get?
580  Q: Why the names "Dear ImGui" vs "ImGui"?
581  >> See https://www.dearimgui.org/faq
582 
583  Q&A: Concerns
584  =============
585 
586  Q: Who uses Dear ImGui?
587  Q: Can you create elaborate/serious tools with Dear ImGui?
588  Q: Can you reskin the look of Dear ImGui?
589  Q: Why using C++ (as opposed to C)?
590  >> See https://www.dearimgui.org/faq
591 
592  Q&A: Integration
593  ================
594 
595  Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or to my application?
596  A: You can read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags from the ImGuiIO structure (e.g. if (ImGui::GetIO().WantCaptureMouse) { ... } )
597     - When 'io.WantCaptureMouse' is set, imgui wants to use your mouse state, and you may want to discard/hide the inputs from the rest of your application.
598     - When 'io.WantCaptureKeyboard' is set, imgui wants to use your keyboard state, and you may want to discard/hide the inputs from the rest of your application.
599     - When 'io.WantTextInput' is set to may want to notify your OS to popup an on-screen keyboard, if available (e.g. on a mobile phone, or console OS).
600     Note: you should always pass your mouse/keyboard inputs to imgui, even when the io.WantCaptureXXX flag are set false.
601      This is because imgui needs to detect that you clicked in the void to unfocus its own windows.
602     Note: The 'io.WantCaptureMouse' is more accurate that any attempt to "check if the mouse is hovering a window" (don't do that!).
603      It handle mouse dragging correctly (both dragging that started over your application or over an imgui window) and handle e.g. modal windows blocking inputs.
604      Those flags are updated by ImGui::NewFrame(). Preferably read the flags after calling NewFrame() if you can afford it, but reading them before is also
605      perfectly fine, as the bool toggle fairly rarely. If you have on a touch device, you might find use for an early call to UpdateHoveredWindowAndCaptureFlags().
606     Note: Text input widget releases focus on "Return KeyDown", so the subsequent "Return KeyUp" event that your application receive will typically
607      have 'io.WantCaptureKeyboard=false'. Depending on your application logic it may or not be inconvenient. You might want to track which key-downs
608      were targeted for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.)
609 
610  Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display)
611  Q: I integrated Dear ImGui in my engine and the text or lines are blurry..
612  Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
613  >> See https://www.dearimgui.org/faq
614 
615  Q&A: Usage
616  ----------
617 
618  Q: Why are multiple widgets reacting when I interact with a single one?
619  Q: How can I have multiple widgets with the same label or with an empty label?
620  A: A primer on labels and the ID Stack...
621 
622     Dear ImGui internally need to uniquely identify UI elements.
623     Elements that are typically not clickable (such as calls to the Text functions) don't need an ID.
624     Interactive widgets (such as calls to Button buttons) need a unique ID.
625     Unique ID are used internally to track active widgets and occasionally associate state to widgets.
626     Unique ID are implicitly built from the hash of multiple elements that identify the "path" to the UI element.
627 
628    - Unique ID are often derived from a string label:
629 
630        Button("OK");          // Label = "OK",     ID = hash of (..., "OK")
631        Button("Cancel");      // Label = "Cancel", ID = hash of (..., "Cancel")
632 
633    - ID are uniquely scoped within windows, tree nodes, etc. which all pushes to the ID stack. Having
634      two buttons labeled "OK" in different windows or different tree locations is fine.
635      We used "..." above to signify whatever was already pushed to the ID stack previously:
636 
637        Begin("MyWindow");
638        Button("OK");          // Label = "OK",     ID = hash of ("MyWindow", "OK")
639        End();
640        Begin("MyOtherWindow");
641        Button("OK");          // Label = "OK",     ID = hash of ("MyOtherWindow", "OK")
642        End();
643 
644    - If you have a same ID twice in the same location, you'll have a conflict:
645 
646        Button("OK");
647        Button("OK");          // ID collision! Interacting with either button will trigger the first one.
648 
649      Fear not! this is easy to solve and there are many ways to solve it!
650 
651    - Solving ID conflict in a simple/local context:
652      When passing a label you can optionally specify extra ID information within string itself.
653      Use "##" to pass a complement to the ID that won't be visible to the end-user.
654      This helps solving the simple collision cases when you know e.g. at compilation time which items
655      are going to be created:
656 
657        Begin("MyWindow");
658        Button("Play");        // Label = "Play",   ID = hash of ("MyWindow", "Play")
659        Button("Play##foo1");  // Label = "Play",   ID = hash of ("MyWindow", "Play##foo1")  // Different from above
660        Button("Play##foo2");  // Label = "Play",   ID = hash of ("MyWindow", "Play##foo2")  // Different from above
661        End();
662 
663    - If you want to completely hide the label, but still need an ID:
664 
665        Checkbox("##On", &b);  // Label = "",       ID = hash of (..., "##On")   // No visible label, just a checkbox!
666 
667    - Occasionally/rarely you might want change a label while preserving a constant ID. This allows
668      you to animate labels. For example you may want to include varying information in a window title bar,
669      but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID:
670 
671        Button("Hello###ID");  // Label = "Hello",  ID = hash of (..., "###ID")
672        Button("World###ID");  // Label = "World",  ID = hash of (..., "###ID")  // Same as above, even though the label looks different
673 
674        sprintf(buf, "My game (%f FPS)###MyGame", fps);
675        Begin(buf);            // Variable title,   ID = hash of "MyGame"
676 
677    - Solving ID conflict in a more general manner:
678      Use PushID() / PopID() to create scopes and manipulate the ID stack, as to avoid ID conflicts
679      within the same window. This is the most convenient way of distinguishing ID when iterating and
680      creating many UI elements programmatically.
681      You can push a pointer, a string or an integer value into the ID stack.
682      Remember that ID are formed from the concatenation of _everything_ pushed into the ID stack.
683      At each level of the stack we store the seed used for items at this level of the ID stack.
684 
685      Begin("Window");
686        for (int i = 0; i < 100; i++)
687        {
688          PushID(i);           // Push i to the id tack
689          Button("Click");     // Label = "Click",  ID = hash of ("Window", i, "Click")
690          PopID();
691        }
692        for (int i = 0; i < 100; i++)
693        {
694          MyObject* obj = Objects[i];
695          PushID(obj);
696          Button("Click");     // Label = "Click",  ID = hash of ("Window", obj pointer, "Click")
697          PopID();
698        }
699        for (int i = 0; i < 100; i++)
700        {
701          MyObject* obj = Objects[i];
702          PushID(obj->Name);
703          Button("Click");     // Label = "Click",  ID = hash of ("Window", obj->Name, "Click")
704          PopID();
705        }
706        End();
707 
708    - You can stack multiple prefixes into the ID stack:
709 
710        Button("Click");       // Label = "Click",  ID = hash of (..., "Click")
711        PushID("node");
712        Button("Click");       // Label = "Click",  ID = hash of (..., "node", "Click")
713          PushID(my_ptr);
714            Button("Click");   // Label = "Click",  ID = hash of (..., "node", my_ptr, "Click")
715          PopID();
716        PopID();
717 
718    - Tree nodes implicitly creates a scope for you by calling PushID().
719 
720        Button("Click");       // Label = "Click",  ID = hash of (..., "Click")
721        if (TreeNode("node"))  // <-- this function call will do a PushID() for you (unless instructed not to, with a special flag)
722        {
723          Button("Click");     // Label = "Click",  ID = hash of (..., "node", "Click")
724          TreePop();
725        }
726 
727    - When working with trees, ID are used to preserve the open/close state of each tree node.
728      Depending on your use cases you may want to use strings, indices or pointers as ID.
729       e.g. when following a single pointer that may change over time, using a static string as ID
730        will preserve your node open/closed state when the targeted object change.
731       e.g. when displaying a list of objects, using indices or pointers as ID will preserve the
732        node open/closed state differently. See what makes more sense in your situation!
733 
734  Q: How can I display an image? What is ImTextureID, how does it works?
735  >> See https://www.dearimgui.org/faq and https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
736 
737  Q: How can I use my own math types instead of ImVec2/ImVec4?
738  Q: How can I interact with standard C++ types (such as std::string and std::vector)?
739  Q: How can I display custom shapes? (using low-level ImDrawList API)
740  >> See https://www.dearimgui.org/faq
741 
742  Q&A: Fonts, Text
743  ================
744 
745  Q: How can I load a different font than the default?
746  Q: How can I easily use icons in my application?
747  Q: How can I load multiple fonts?
748  Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
749  >> See https://www.dearimgui.org/faq and misc/fonts/README.txt
750 
751  Q&A: Community
752  ==============
753 
754  Q: How can I help?
755  A: - If you are experienced with Dear ImGui and C++, look at the github issues, look at the Wiki, read docs/TODO.txt
756       and see how you want to help and can help!
757     - Businesses: convince your company to fund development via support contracts/sponsoring! This is among the most useful thing you can do for dear imgui.
758     - Individuals: you can also become a Patron (http://www.patreon.com/imgui) or donate on PayPal! See README.
759     - Disclose your usage of dear imgui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
760       You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/2847). Visuals are ideal as they inspire other programmers.
761       But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions.
762     - 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).
763 
764 */
765 
766 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
767 #define _CRT_SECURE_NO_WARNINGS
768 #endif
769 
770 #include "imgui.h"
771 #ifndef IMGUI_DEFINE_MATH_OPERATORS
772 #define IMGUI_DEFINE_MATH_OPERATORS
773 #endif
774 #include "imgui_internal.h"
775 
776 #include <ctype.h>      // toupper
777 #include <stdio.h>      // vsnprintf, sscanf, printf
778 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
779 #include <stddef.h>     // intptr_t
780 #else
781 #include <stdint.h>     // intptr_t
782 #endif
783 
784 // Debug options
785 #define IMGUI_DEBUG_NAV_SCORING     0   // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL
786 #define IMGUI_DEBUG_NAV_RECTS       0   // Display the reference navigation rectangle for each window
787 #define IMGUI_DEBUG_INI_SETTINGS    0   // Save additional comments in .ini file
788 
789 // Visual Studio warnings
790 #ifdef _MSC_VER
791 #pragma warning (disable: 4127) // condition expression is constant
792 #pragma warning (disable: 4201) // nonstandard extension used: nameless struct/union
793 #pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
794 #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
795 #endif
796 
797 // Clang/GCC warnings with -Weverything
798 #if defined(__clang__)
799 #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!
800 #pragma clang diagnostic ignored "-Wold-style-cast"         // warning : use of old-style cast                              // yes, they are more terse.
801 #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.
802 #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.
803 #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.
804 #pragma clang diagnostic ignored "-Wglobal-constructors"    // warning : declaration requires a global destructor           // similar to above, not sure what the exact difference is.
805 #pragma clang diagnostic ignored "-Wsign-conversion"        // warning : implicit conversion changes signedness             //
806 #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.
807 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast"       // warning : cast to 'void *' from smaller integer type 'int'
808 #if __has_warning("-Wzero-as-null-pointer-constant")
809 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"  // warning : zero as null pointer constant              // some standard header variations use #define NULL 0
810 #endif
811 #if __has_warning("-Wdouble-promotion")
812 #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.
813 #endif
814 #elif defined(__GNUC__)
815 // We disable -Wpragmas because GCC doesn't provide an has_warning equivalent and some forks/patches may not following the warning/version association.
816 #pragma GCC diagnostic ignored "-Wpragmas"                  // warning: unknown option after '#pragma GCC diagnostic' kind
817 #pragma GCC diagnostic ignored "-Wunused-function"          // warning: 'xxxx' defined but not used
818 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"      // warning: cast to pointer from integer of different size
819 #pragma GCC diagnostic ignored "-Wformat"                   // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
820 #pragma GCC diagnostic ignored "-Wdouble-promotion"         // warning: implicit conversion from 'float' to 'double' when passing argument to function
821 #pragma GCC diagnostic ignored "-Wconversion"               // warning: conversion to 'xxxx' from 'xxxx' may alter its value
822 #pragma GCC diagnostic ignored "-Wformat-nonliteral"        // warning: format not a string literal, format string not checked
823 #pragma GCC diagnostic ignored "-Wstrict-overflow"          // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
824 #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
825 #endif
826 
827 // 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.
828 static const float NAV_WINDOWING_HIGHLIGHT_DELAY            = 0.20f;    // Time before the highlight and screen dimming starts fading in
829 static const float NAV_WINDOWING_LIST_APPEAR_DELAY          = 0.15f;    // Time before the window list starts to appear
830 
831 // Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by back-end)
832 static const float WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS = 4.0f;     // Extend outside and inside windows. Affect FindHoveredWindow().
833 static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f;    // Reduce visual noise by only highlighting the border after a certain time.
834 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.
835 
836 //-------------------------------------------------------------------------
837 // [SECTION] FORWARD DECLARATIONS
838 //-------------------------------------------------------------------------
839 
840 static void             SetCurrentWindow(ImGuiWindow* window);
841 static void             FindHoveredWindow();
842 static ImGuiWindow*     CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
843 static void             CheckStacksSize(ImGuiWindow* window, bool write);
844 static ImVec2           CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges);
845 
846 static void             AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
847 static void             AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
848 
849 static ImRect           GetViewportRect();
850 
851 // Settings
852 static void*            SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
853 static void             SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
854 static void             SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf);
855 
856 // Platform Dependents default implementation for IO functions
857 static const char*      GetClipboardTextFn_DefaultImpl(void* user_data);
858 static void             SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
859 static void             ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
860 
861 namespace ImGui
862 {
863 static bool             BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags);
864 
865 // Navigation
866 static void             NavUpdate();
867 static void             NavUpdateWindowing();
868 static void             NavUpdateWindowingOverlay();
869 static void             NavUpdateMoveResult();
870 static float            NavUpdatePageUpPageDown();
871 static inline void      NavUpdateAnyRequestFlag();
872 static bool             NavScoreItem(ImGuiNavMoveResult* result, ImRect cand);
873 static void             NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id);
874 static ImVec2           NavCalcPreferredRefPos();
875 static void             NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
876 static ImGuiWindow*     NavRestoreLastChildNavWindow(ImGuiWindow* window);
877 static int              FindWindowFocusIndex(ImGuiWindow* window);
878 
879 // Misc
880 static void             UpdateMouseInputs();
881 static void             UpdateMouseWheel();
882 static bool             UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]);
883 static void             UpdateDebugToolItemPicker();
884 static void             RenderWindowOuterBorders(ImGuiWindow* window);
885 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);
886 static void             RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
887 
888 }
889 
890 //-----------------------------------------------------------------------------
891 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
892 //-----------------------------------------------------------------------------
893 
894 // Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
895 // ImGui::CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext().
896 // 1) Important: globals are not shared across DLL boundaries! If you use DLLs or any form of hot-reloading: you will need to call
897 //    SetCurrentContext() (with the pointer you got from CreateContext) from each unique static/DLL boundary, and after each hot-reloading.
898 //    In your debugger, add GImGui to your watch window and notice how its value changes depending on which location you are currently stepping into.
899 // 2) Important: Dear ImGui functions are not thread-safe because of this pointer.
900 //    If you want thread-safety to allow N threads to access N different contexts, you can:
901 //    - Change this variable to use thread local storage so each thread can refer to a different context, in imconfig.h:
902 //          struct ImGuiContext;
903 //          extern thread_local ImGuiContext* MyImGuiTLS;
904 //          #define GImGui MyImGuiTLS
905 //      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.
906 //    - Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
907 //    - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from different namespace.
908 #ifndef GImGui
909 ImGuiContext*   GImGui = NULL;
910 #endif
911 
912 // Memory Allocator functions. Use SetAllocatorFunctions() to change them.
913 // If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file.
914 // 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.
915 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
MallocWrapper(size_t size,void * user_data)916 static void*   MallocWrapper(size_t size, void* user_data)    { IM_UNUSED(user_data); return malloc(size); }
FreeWrapper(void * ptr,void * user_data)917 static void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); free(ptr); }
918 #else
MallocWrapper(size_t size,void * user_data)919 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)920 static void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }
921 #endif
922 
923 static void*  (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper;
924 static void   (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper;
925 static void*    GImAllocatorUserData = NULL;
926 
927 //-----------------------------------------------------------------------------
928 // [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
929 //-----------------------------------------------------------------------------
930 
ImGuiStyle()931 ImGuiStyle::ImGuiStyle()
932 {
933     Alpha                   = 1.0f;             // Global alpha applies to everything in ImGui
934     WindowPadding           = ImVec2(8,8);      // Padding within a window
935     WindowRounding          = 7.0f;             // Radius of window corners rounding. Set to 0.0f to have rectangular windows
936     WindowBorderSize        = 1.0f;             // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
937     WindowMinSize           = ImVec2(32,32);    // Minimum window size
938     WindowTitleAlign        = ImVec2(0.0f,0.5f);// Alignment for title bar text
939     WindowMenuButtonPosition= ImGuiDir_Left;    // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.
940     ChildRounding           = 0.0f;             // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
941     ChildBorderSize         = 1.0f;             // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
942     PopupRounding           = 0.0f;             // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
943     PopupBorderSize         = 1.0f;             // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
944     FramePadding            = ImVec2(4,3);      // Padding within a framed rectangle (used by most widgets)
945     FrameRounding           = 0.0f;             // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
946     FrameBorderSize         = 0.0f;             // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
947     ItemSpacing             = ImVec2(8,4);      // Horizontal and vertical spacing between widgets/lines
948     ItemInnerSpacing        = ImVec2(4,4);      // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
949     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!
950     IndentSpacing           = 21.0f;            // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
951     ColumnsMinSpacing       = 6.0f;             // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
952     ScrollbarSize           = 14.0f;            // Width of the vertical scrollbar, Height of the horizontal scrollbar
953     ScrollbarRounding       = 9.0f;             // Radius of grab corners rounding for scrollbar
954     GrabMinSize             = 10.0f;            // Minimum width/height of a grab box for slider/scrollbar
955     GrabRounding            = 0.0f;             // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
956     TabRounding             = 4.0f;             // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
957     TabBorderSize           = 0.0f;             // Thickness of border around tabs.
958     ColorButtonPosition     = ImGuiDir_Right;   // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
959     ButtonTextAlign         = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
960     SelectableTextAlign     = ImVec2(0.0f,0.0f);// Alignment of selectable text when button is larger than text.
961     DisplayWindowPadding    = ImVec2(19,19);    // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows.
962     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.
963     MouseCursorScale        = 1.0f;             // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
964     AntiAliasedLines        = true;             // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
965     AntiAliasedFill         = true;             // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
966     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.
967 
968     // Default theme
969     ImGui::StyleColorsDark(this);
970 }
971 
972 // 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.
973 // 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)974 void ImGuiStyle::ScaleAllSizes(float scale_factor)
975 {
976     WindowPadding = ImFloor(WindowPadding * scale_factor);
977     WindowRounding = ImFloor(WindowRounding * scale_factor);
978     WindowMinSize = ImFloor(WindowMinSize * scale_factor);
979     ChildRounding = ImFloor(ChildRounding * scale_factor);
980     PopupRounding = ImFloor(PopupRounding * scale_factor);
981     FramePadding = ImFloor(FramePadding * scale_factor);
982     FrameRounding = ImFloor(FrameRounding * scale_factor);
983     ItemSpacing = ImFloor(ItemSpacing * scale_factor);
984     ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
985     TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
986     IndentSpacing = ImFloor(IndentSpacing * scale_factor);
987     ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
988     ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
989     ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
990     GrabMinSize = ImFloor(GrabMinSize * scale_factor);
991     GrabRounding = ImFloor(GrabRounding * scale_factor);
992     TabRounding = ImFloor(TabRounding * scale_factor);
993     DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
994     DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
995     MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
996 }
997 
ImGuiIO()998 ImGuiIO::ImGuiIO()
999 {
1000     // Most fields are initialized with zero
1001     memset(this, 0, sizeof(*this));
1002 
1003     // Settings
1004     ConfigFlags = ImGuiConfigFlags_None;
1005     BackendFlags = ImGuiBackendFlags_None;
1006     DisplaySize = ImVec2(-1.0f, -1.0f);
1007     DeltaTime = 1.0f/60.0f;
1008     IniSavingRate = 5.0f;
1009     IniFilename = "imgui.ini";
1010     LogFilename = "imgui_log.txt";
1011     MouseDoubleClickTime = 0.30f;
1012     MouseDoubleClickMaxDist = 6.0f;
1013     for (int i = 0; i < ImGuiKey_COUNT; i++)
1014         KeyMap[i] = -1;
1015     KeyRepeatDelay = 0.275f;
1016     KeyRepeatRate = 0.050f;
1017     UserData = NULL;
1018 
1019     Fonts = NULL;
1020     FontGlobalScale = 1.0f;
1021     FontDefault = NULL;
1022     FontAllowUserScaling = false;
1023     DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1024 
1025     // Miscellaneous options
1026     MouseDrawCursor = false;
1027 #ifdef __APPLE__
1028     ConfigMacOSXBehaviors = true;  // Set Mac OS X style defaults based on __APPLE__ compile time flag
1029 #else
1030     ConfigMacOSXBehaviors = false;
1031 #endif
1032     ConfigInputTextCursorBlink = true;
1033     ConfigWindowsResizeFromEdges = true;
1034     ConfigWindowsMoveFromTitleBarOnly = false;
1035     ConfigWindowsMemoryCompactTimer = 60.0f;
1036 
1037     // Platform Functions
1038     BackendPlatformName = BackendRendererName = NULL;
1039     BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1040     GetClipboardTextFn = GetClipboardTextFn_DefaultImpl;   // Platform dependent default implementations
1041     SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
1042     ClipboardUserData = NULL;
1043     ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
1044     ImeWindowHandle = NULL;
1045 
1046 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1047     RenderDrawListsFn = NULL;
1048 #endif
1049 
1050     // Input (NB: we already have memset zero the entire structure!)
1051     MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1052     MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1053     MouseDragThreshold = 6.0f;
1054     for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1055     for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i]  = KeysDownDurationPrev[i] = -1.0f;
1056     for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
1057 }
1058 
1059 // Pass in translated ASCII characters for text input.
1060 // - with glfw you can get those from the callback set in glfwSetCharCallback()
1061 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(unsigned int c)1062 void ImGuiIO::AddInputCharacter(unsigned int c)
1063 {
1064     if (c > 0 && c < 0x10000)
1065         InputQueueCharacters.push_back((ImWchar)c);
1066 }
1067 
AddInputCharactersUTF8(const char * utf8_chars)1068 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1069 {
1070     while (*utf8_chars != 0)
1071     {
1072         unsigned int c = 0;
1073         utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
1074         if (c > 0 && c < 0x10000)
1075             InputQueueCharacters.push_back((ImWchar)c);
1076     }
1077 }
1078 
ClearInputCharacters()1079 void ImGuiIO::ClearInputCharacters()
1080 {
1081     InputQueueCharacters.resize(0);
1082 }
1083 
1084 //-----------------------------------------------------------------------------
1085 // [SECTION] MISC HELPERS/UTILITIES (Maths, String, Format, Hash, File functions)
1086 //-----------------------------------------------------------------------------
1087 
ImLineClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & p)1088 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1089 {
1090     ImVec2 ap = p - a;
1091     ImVec2 ab_dir = b - a;
1092     float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1093     if (dot < 0.0f)
1094         return a;
1095     float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1096     if (dot > ab_len_sqr)
1097         return b;
1098     return a + ab_dir * dot / ab_len_sqr;
1099 }
1100 
ImTriangleContainsPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1101 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1102 {
1103     bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1104     bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1105     bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1106     return ((b1 == b2) && (b2 == b3));
1107 }
1108 
ImTriangleBarycentricCoords(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p,float & out_u,float & out_v,float & out_w)1109 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1110 {
1111     ImVec2 v0 = b - a;
1112     ImVec2 v1 = c - a;
1113     ImVec2 v2 = p - a;
1114     const float denom = v0.x * v1.y - v1.x * v0.y;
1115     out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1116     out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1117     out_u = 1.0f - out_v - out_w;
1118 }
1119 
ImTriangleClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1120 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1121 {
1122     ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1123     ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1124     ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1125     float dist2_ab = ImLengthSqr(p - proj_ab);
1126     float dist2_bc = ImLengthSqr(p - proj_bc);
1127     float dist2_ca = ImLengthSqr(p - proj_ca);
1128     float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1129     if (m == dist2_ab)
1130         return proj_ab;
1131     if (m == dist2_bc)
1132         return proj_bc;
1133     return proj_ca;
1134 }
1135 
1136 // 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)1137 int ImStricmp(const char* str1, const char* str2)
1138 {
1139     int d;
1140     while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
1141     return d;
1142 }
1143 
ImStrnicmp(const char * str1,const char * str2,size_t count)1144 int ImStrnicmp(const char* str1, const char* str2, size_t count)
1145 {
1146     int d = 0;
1147     while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
1148     return d;
1149 }
1150 
ImStrncpy(char * dst,const char * src,size_t count)1151 void ImStrncpy(char* dst, const char* src, size_t count)
1152 {
1153     if (count < 1)
1154         return;
1155     if (count > 1)
1156         strncpy(dst, src, count - 1);
1157     dst[count - 1] = 0;
1158 }
1159 
ImStrdup(const char * str)1160 char* ImStrdup(const char* str)
1161 {
1162     size_t len = strlen(str);
1163     void* buf = IM_ALLOC(len + 1);
1164     return (char*)memcpy(buf, (const void*)str, len + 1);
1165 }
1166 
ImStrdupcpy(char * dst,size_t * p_dst_size,const char * src)1167 char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
1168 {
1169     size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1;
1170     size_t src_size = strlen(src) + 1;
1171     if (dst_buf_size < src_size)
1172     {
1173         IM_FREE(dst);
1174         dst = (char*)IM_ALLOC(src_size);
1175         if (p_dst_size)
1176             *p_dst_size = src_size;
1177     }
1178     return (char*)memcpy(dst, (const void*)src, src_size);
1179 }
1180 
ImStrchrRange(const char * str,const char * str_end,char c)1181 const char* ImStrchrRange(const char* str, const char* str_end, char c)
1182 {
1183     const char* p = (const char*)memchr(str, (int)c, str_end - str);
1184     return p;
1185 }
1186 
ImStrlenW(const ImWchar * str)1187 int ImStrlenW(const ImWchar* str)
1188 {
1189     //return (int)wcslen((const wchar_t*)str);  // FIXME-OPT: Could use this when wchar_t are 16-bits
1190     int n = 0;
1191     while (*str++) n++;
1192     return n;
1193 }
1194 
1195 // Find end-of-line. Return pointer will point to either first \n, either str_end.
ImStreolRange(const char * str,const char * str_end)1196 const char* ImStreolRange(const char* str, const char* str_end)
1197 {
1198     const char* p = (const char*)memchr(str, '\n', str_end - str);
1199     return p ? p : str_end;
1200 }
1201 
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)1202 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
1203 {
1204     while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1205         buf_mid_line--;
1206     return buf_mid_line;
1207 }
1208 
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)1209 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1210 {
1211     if (!needle_end)
1212         needle_end = needle + strlen(needle);
1213 
1214     const char un0 = (char)toupper(*needle);
1215     while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1216     {
1217         if (toupper(*haystack) == un0)
1218         {
1219             const char* b = needle + 1;
1220             for (const char* a = haystack + 1; b < needle_end; a++, b++)
1221                 if (toupper(*a) != toupper(*b))
1222                     break;
1223             if (b == needle_end)
1224                 return haystack;
1225         }
1226         haystack++;
1227     }
1228     return NULL;
1229 }
1230 
1231 // 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)1232 void ImStrTrimBlanks(char* buf)
1233 {
1234     char* p = buf;
1235     while (p[0] == ' ' || p[0] == '\t')     // Leading blanks
1236         p++;
1237     char* p_start = p;
1238     while (*p != 0)                         // Find end of string
1239         p++;
1240     while (p > p_start && (p[-1] == ' ' || p[-1] == '\t'))  // Trailing blanks
1241         p--;
1242     if (p_start != buf)                     // Copy memory if we had leading blanks
1243         memmove(buf, p_start, p - p_start);
1244     buf[p - p_start] = 0;                   // Zero terminate
1245 }
1246 
1247 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1248 // 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.
1249 // B) When buf==NULL vsnprintf() will return the output size.
1250 #ifndef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1251 
1252 //#define IMGUI_USE_STB_SPRINTF
1253 #ifdef IMGUI_USE_STB_SPRINTF
1254 #define STB_SPRINTF_IMPLEMENTATION
1255 #include "imstb_sprintf.h"
1256 #endif
1257 
1258 #if defined(_MSC_VER) && !defined(vsnprintf)
1259 #define vsnprintf _vsnprintf
1260 #endif
1261 
ImFormatString(char * buf,size_t buf_size,const char * fmt,...)1262 int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1263 {
1264     va_list args;
1265     va_start(args, fmt);
1266 #ifdef IMGUI_USE_STB_SPRINTF
1267     int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1268 #else
1269     int w = vsnprintf(buf, buf_size, fmt, args);
1270 #endif
1271     va_end(args);
1272     if (buf == NULL)
1273         return w;
1274     if (w == -1 || w >= (int)buf_size)
1275         w = (int)buf_size - 1;
1276     buf[w] = 0;
1277     return w;
1278 }
1279 
ImFormatStringV(char * buf,size_t buf_size,const char * fmt,va_list args)1280 int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1281 {
1282 #ifdef IMGUI_USE_STB_SPRINTF
1283     int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1284 #else
1285     int w = vsnprintf(buf, buf_size, fmt, args);
1286 #endif
1287     if (buf == NULL)
1288         return w;
1289     if (w == -1 || w >= (int)buf_size)
1290         w = (int)buf_size - 1;
1291     buf[w] = 0;
1292     return w;
1293 }
1294 #endif // #ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1295 
1296 // CRC32 needs a 1KB lookup table (not cache friendly)
1297 // Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
1298 // - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
1299 static const ImU32 GCrc32LookupTable[256] =
1300 {
1301     0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
1302     0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
1303     0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
1304     0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
1305     0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
1306     0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
1307     0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
1308     0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
1309     0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
1310     0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
1311     0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
1312     0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
1313     0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
1314     0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
1315     0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
1316     0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
1317 };
1318 
1319 // Known size hash
1320 // It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
1321 // 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)1322 ImU32 ImHashData(const void* data_p, size_t data_size, ImU32 seed)
1323 {
1324     ImU32 crc = ~seed;
1325     const unsigned char* data = (const unsigned char*)data_p;
1326     const ImU32* crc32_lut = GCrc32LookupTable;
1327     while (data_size-- != 0)
1328         crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
1329     return ~crc;
1330 }
1331 
1332 // Zero-terminated string hash, with support for ### to reset back to seed value
1333 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1334 // Because this syntax is rarely used we are optimizing for the common case.
1335 // - If we reach ### in the string we discard the hash so far and reset to the seed.
1336 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
1337 // 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)1338 ImU32 ImHashStr(const char* data_p, size_t data_size, ImU32 seed)
1339 {
1340     seed = ~seed;
1341     ImU32 crc = seed;
1342     const unsigned char* data = (const unsigned char*)data_p;
1343     const ImU32* crc32_lut = GCrc32LookupTable;
1344     if (data_size != 0)
1345     {
1346         while (data_size-- != 0)
1347         {
1348             unsigned char c = *data++;
1349             if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')
1350                 crc = seed;
1351             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1352         }
1353     }
1354     else
1355     {
1356         while (unsigned char c = *data++)
1357         {
1358             if (c == '#' && data[0] == '#' && data[1] == '#')
1359                 crc = seed;
1360             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1361         }
1362     }
1363     return ~crc;
1364 }
1365 
ImFileOpen(const char * filename,const char * mode)1366 FILE* ImFileOpen(const char* filename, const char* mode)
1367 {
1368 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__)
1369     // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. Converting both strings from UTF-8 to wchar format (using a single allocation, because we can)
1370     const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1;
1371     const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1;
1372     ImVector<ImWchar> buf;
1373     buf.resize(filename_wsize + mode_wsize);
1374     ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL);
1375     ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL);
1376     return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]);
1377 #else
1378     return fopen(filename, mode);
1379 #endif
1380 }
1381 
1382 // Load file content into memory
1383 // Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree()
ImFileLoadToMemory(const char * filename,const char * file_open_mode,size_t * out_file_size,int padding_bytes)1384 void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, size_t* out_file_size, int padding_bytes)
1385 {
1386     IM_ASSERT(filename && file_open_mode);
1387     if (out_file_size)
1388         *out_file_size = 0;
1389 
1390     FILE* f;
1391     if ((f = ImFileOpen(filename, file_open_mode)) == NULL)
1392         return NULL;
1393 
1394     long file_size_signed;
1395     if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET))
1396     {
1397         fclose(f);
1398         return NULL;
1399     }
1400 
1401     size_t file_size = (size_t)file_size_signed;
1402     void* file_data = IM_ALLOC(file_size + padding_bytes);
1403     if (file_data == NULL)
1404     {
1405         fclose(f);
1406         return NULL;
1407     }
1408     if (fread(file_data, 1, file_size, f) != file_size)
1409     {
1410         fclose(f);
1411         IM_FREE(file_data);
1412         return NULL;
1413     }
1414     if (padding_bytes > 0)
1415         memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
1416 
1417     fclose(f);
1418     if (out_file_size)
1419         *out_file_size = file_size;
1420 
1421     return file_data;
1422 }
1423 
1424 //-----------------------------------------------------------------------------
1425 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
1426 //-----------------------------------------------------------------------------
1427 
1428 // Convert UTF-8 to 32-bits character, process single character input.
1429 // Based on stb_from_utf8() from github.com/nothings/stb/
1430 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)1431 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1432 {
1433     unsigned int c = (unsigned int)-1;
1434     const unsigned char* str = (const unsigned char*)in_text;
1435     if (!(*str & 0x80))
1436     {
1437         c = (unsigned int)(*str++);
1438         *out_char = c;
1439         return 1;
1440     }
1441     if ((*str & 0xe0) == 0xc0)
1442     {
1443         *out_char = 0xFFFD; // will be invalid but not end of string
1444         if (in_text_end && in_text_end - (const char*)str < 2) return 1;
1445         if (*str < 0xc2) return 2;
1446         c = (unsigned int)((*str++ & 0x1f) << 6);
1447         if ((*str & 0xc0) != 0x80) return 2;
1448         c += (*str++ & 0x3f);
1449         *out_char = c;
1450         return 2;
1451     }
1452     if ((*str & 0xf0) == 0xe0)
1453     {
1454         *out_char = 0xFFFD; // will be invalid but not end of string
1455         if (in_text_end && in_text_end - (const char*)str < 3) return 1;
1456         if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3;
1457         if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below
1458         c = (unsigned int)((*str++ & 0x0f) << 12);
1459         if ((*str & 0xc0) != 0x80) return 3;
1460         c += (unsigned int)((*str++ & 0x3f) << 6);
1461         if ((*str & 0xc0) != 0x80) return 3;
1462         c += (*str++ & 0x3f);
1463         *out_char = c;
1464         return 3;
1465     }
1466     if ((*str & 0xf8) == 0xf0)
1467     {
1468         *out_char = 0xFFFD; // will be invalid but not end of string
1469         if (in_text_end && in_text_end - (const char*)str < 4) return 1;
1470         if (*str > 0xf4) return 4;
1471         if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4;
1472         if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below
1473         c = (unsigned int)((*str++ & 0x07) << 18);
1474         if ((*str & 0xc0) != 0x80) return 4;
1475         c += (unsigned int)((*str++ & 0x3f) << 12);
1476         if ((*str & 0xc0) != 0x80) return 4;
1477         c += (unsigned int)((*str++ & 0x3f) << 6);
1478         if ((*str & 0xc0) != 0x80) return 4;
1479         c += (*str++ & 0x3f);
1480         // utf-8 encodings of values used in surrogate pairs are invalid
1481         if ((c & 0xFFFFF800) == 0xD800) return 4;
1482         *out_char = c;
1483         return 4;
1484     }
1485     *out_char = 0;
1486     return 0;
1487 }
1488 
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1489 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1490 {
1491     ImWchar* buf_out = buf;
1492     ImWchar* buf_end = buf + buf_size;
1493     while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1494     {
1495         unsigned int c;
1496         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1497         if (c == 0)
1498             break;
1499         if (c < 0x10000)    // FIXME: Losing characters that don't fit in 2 bytes
1500             *buf_out++ = (ImWchar)c;
1501     }
1502     *buf_out = 0;
1503     if (in_text_remaining)
1504         *in_text_remaining = in_text;
1505     return (int)(buf_out - buf);
1506 }
1507 
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1508 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1509 {
1510     int char_count = 0;
1511     while ((!in_text_end || in_text < in_text_end) && *in_text)
1512     {
1513         unsigned int c;
1514         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1515         if (c == 0)
1516             break;
1517         if (c < 0x10000)
1518             char_count++;
1519     }
1520     return char_count;
1521 }
1522 
1523 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8(char * buf,int buf_size,unsigned int c)1524 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1525 {
1526     if (c < 0x80)
1527     {
1528         buf[0] = (char)c;
1529         return 1;
1530     }
1531     if (c < 0x800)
1532     {
1533         if (buf_size < 2) return 0;
1534         buf[0] = (char)(0xc0 + (c >> 6));
1535         buf[1] = (char)(0x80 + (c & 0x3f));
1536         return 2;
1537     }
1538     if (c >= 0xdc00 && c < 0xe000)
1539     {
1540         return 0;
1541     }
1542     if (c >= 0xd800 && c < 0xdc00)
1543     {
1544         if (buf_size < 4) return 0;
1545         buf[0] = (char)(0xf0 + (c >> 18));
1546         buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1547         buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1548         buf[3] = (char)(0x80 + ((c ) & 0x3f));
1549         return 4;
1550     }
1551     //else if (c < 0x10000)
1552     {
1553         if (buf_size < 3) return 0;
1554         buf[0] = (char)(0xe0 + (c >> 12));
1555         buf[1] = (char)(0x80 + ((c>> 6) & 0x3f));
1556         buf[2] = (char)(0x80 + ((c ) & 0x3f));
1557         return 3;
1558     }
1559 }
1560 
1561 // Not optimal but we very rarely use this function.
ImTextCountUtf8BytesFromChar(const char * in_text,const char * in_text_end)1562 int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
1563 {
1564     unsigned int dummy = 0;
1565     return ImTextCharFromUtf8(&dummy, in_text, in_text_end);
1566 }
1567 
ImTextCountUtf8BytesFromChar(unsigned int c)1568 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1569 {
1570     if (c < 0x80) return 1;
1571     if (c < 0x800) return 2;
1572     if (c >= 0xdc00 && c < 0xe000) return 0;
1573     if (c >= 0xd800 && c < 0xdc00) return 4;
1574     return 3;
1575 }
1576 
ImTextStrToUtf8(char * buf,int buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1577 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1578 {
1579     char* buf_out = buf;
1580     const char* buf_end = buf + buf_size;
1581     while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1582     {
1583         unsigned int c = (unsigned int)(*in_text++);
1584         if (c < 0x80)
1585             *buf_out++ = (char)c;
1586         else
1587             buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c);
1588     }
1589     *buf_out = 0;
1590     return (int)(buf_out - buf);
1591 }
1592 
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1593 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1594 {
1595     int bytes_count = 0;
1596     while ((!in_text_end || in_text < in_text_end) && *in_text)
1597     {
1598         unsigned int c = (unsigned int)(*in_text++);
1599         if (c < 0x80)
1600             bytes_count++;
1601         else
1602             bytes_count += ImTextCountUtf8BytesFromChar(c);
1603     }
1604     return bytes_count;
1605 }
1606 
1607 //-----------------------------------------------------------------------------
1608 // [SECTION] MISC HELPERS/UTILTIES (Color functions)
1609 // Note: The Convert functions are early design which are not consistent with other API.
1610 //-----------------------------------------------------------------------------
1611 
ColorConvertU32ToFloat4(ImU32 in)1612 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1613 {
1614     float s = 1.0f/255.0f;
1615     return ImVec4(
1616         ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1617         ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1618         ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1619         ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1620 }
1621 
ColorConvertFloat4ToU32(const ImVec4 & in)1622 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1623 {
1624     ImU32 out;
1625     out  = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1626     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1627     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1628     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1629     return out;
1630 }
1631 
1632 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1633 // 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)1634 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1635 {
1636     float K = 0.f;
1637     if (g < b)
1638     {
1639         ImSwap(g, b);
1640         K = -1.f;
1641     }
1642     if (r < g)
1643     {
1644         ImSwap(r, g);
1645         K = -2.f / 6.f - K;
1646     }
1647 
1648     const float chroma = r - (g < b ? g : b);
1649     out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
1650     out_s = chroma / (r + 1e-20f);
1651     out_v = r;
1652 }
1653 
1654 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1655 // 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)1656 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1657 {
1658     if (s == 0.0f)
1659     {
1660         // gray
1661         out_r = out_g = out_b = v;
1662         return;
1663     }
1664 
1665     h = ImFmod(h, 1.0f) / (60.0f/360.0f);
1666     int   i = (int)h;
1667     float f = h - (float)i;
1668     float p = v * (1.0f - s);
1669     float q = v * (1.0f - s * f);
1670     float t = v * (1.0f - s * (1.0f - f));
1671 
1672     switch (i)
1673     {
1674     case 0: out_r = v; out_g = t; out_b = p; break;
1675     case 1: out_r = q; out_g = v; out_b = p; break;
1676     case 2: out_r = p; out_g = v; out_b = t; break;
1677     case 3: out_r = p; out_g = q; out_b = v; break;
1678     case 4: out_r = t; out_g = p; out_b = v; break;
1679     case 5: default: out_r = v; out_g = p; out_b = q; break;
1680     }
1681 }
1682 
GetColorU32(ImGuiCol idx,float alpha_mul)1683 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
1684 {
1685     ImGuiStyle& style = GImGui->Style;
1686     ImVec4 c = style.Colors[idx];
1687     c.w *= style.Alpha * alpha_mul;
1688     return ColorConvertFloat4ToU32(c);
1689 }
1690 
GetColorU32(const ImVec4 & col)1691 ImU32 ImGui::GetColorU32(const ImVec4& col)
1692 {
1693     ImGuiStyle& style = GImGui->Style;
1694     ImVec4 c = col;
1695     c.w *= style.Alpha;
1696     return ColorConvertFloat4ToU32(c);
1697 }
1698 
GetStyleColorVec4(ImGuiCol idx)1699 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
1700 {
1701     ImGuiStyle& style = GImGui->Style;
1702     return style.Colors[idx];
1703 }
1704 
GetColorU32(ImU32 col)1705 ImU32 ImGui::GetColorU32(ImU32 col)
1706 {
1707     float style_alpha = GImGui->Style.Alpha;
1708     if (style_alpha >= 1.0f)
1709         return col;
1710     ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
1711     a = (ImU32)(a * style_alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
1712     return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
1713 }
1714 
1715 //-----------------------------------------------------------------------------
1716 // [SECTION] ImGuiStorage
1717 // Helper: Key->value storage
1718 //-----------------------------------------------------------------------------
1719 
1720 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair> & data,ImGuiID key)1721 static ImGuiStorage::ImGuiStoragePair* LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair>& data, ImGuiID key)
1722 {
1723     ImGuiStorage::ImGuiStoragePair* first = data.Data;
1724     ImGuiStorage::ImGuiStoragePair* last = data.Data + data.Size;
1725     size_t count = (size_t)(last - first);
1726     while (count > 0)
1727     {
1728         size_t count2 = count >> 1;
1729         ImGuiStorage::ImGuiStoragePair* mid = first + count2;
1730         if (mid->key < key)
1731         {
1732             first = ++mid;
1733             count -= count2 + 1;
1734         }
1735         else
1736         {
1737             count = count2;
1738         }
1739     }
1740     return first;
1741 }
1742 
1743 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
BuildSortByKey()1744 void ImGuiStorage::BuildSortByKey()
1745 {
1746     struct StaticFunc
1747     {
1748         static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)
1749         {
1750             // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1751             if (((const ImGuiStoragePair*)lhs)->key > ((const ImGuiStoragePair*)rhs)->key) return +1;
1752             if (((const ImGuiStoragePair*)lhs)->key < ((const ImGuiStoragePair*)rhs)->key) return -1;
1753             return 0;
1754         }
1755     };
1756     if (Data.Size > 1)
1757         ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairCompareByID);
1758 }
1759 
GetInt(ImGuiID key,int default_val) const1760 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1761 {
1762     ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1763     if (it == Data.end() || it->key != key)
1764         return default_val;
1765     return it->val_i;
1766 }
1767 
GetBool(ImGuiID key,bool default_val) const1768 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1769 {
1770     return GetInt(key, default_val ? 1 : 0) != 0;
1771 }
1772 
GetFloat(ImGuiID key,float default_val) const1773 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1774 {
1775     ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1776     if (it == Data.end() || it->key != key)
1777         return default_val;
1778     return it->val_f;
1779 }
1780 
GetVoidPtr(ImGuiID key) const1781 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1782 {
1783     ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1784     if (it == Data.end() || it->key != key)
1785         return NULL;
1786     return it->val_p;
1787 }
1788 
1789 // 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)1790 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1791 {
1792     ImGuiStoragePair* it = LowerBound(Data, key);
1793     if (it == Data.end() || it->key != key)
1794         it = Data.insert(it, ImGuiStoragePair(key, default_val));
1795     return &it->val_i;
1796 }
1797 
GetBoolRef(ImGuiID key,bool default_val)1798 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1799 {
1800     return (bool*)GetIntRef(key, default_val ? 1 : 0);
1801 }
1802 
GetFloatRef(ImGuiID key,float default_val)1803 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1804 {
1805     ImGuiStoragePair* it = LowerBound(Data, key);
1806     if (it == Data.end() || it->key != key)
1807         it = Data.insert(it, ImGuiStoragePair(key, default_val));
1808     return &it->val_f;
1809 }
1810 
GetVoidPtrRef(ImGuiID key,void * default_val)1811 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1812 {
1813     ImGuiStoragePair* it = LowerBound(Data, key);
1814     if (it == Data.end() || it->key != key)
1815         it = Data.insert(it, ImGuiStoragePair(key, default_val));
1816     return &it->val_p;
1817 }
1818 
1819 // 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)1820 void ImGuiStorage::SetInt(ImGuiID key, int val)
1821 {
1822     ImGuiStoragePair* it = LowerBound(Data, key);
1823     if (it == Data.end() || it->key != key)
1824     {
1825         Data.insert(it, ImGuiStoragePair(key, val));
1826         return;
1827     }
1828     it->val_i = val;
1829 }
1830 
SetBool(ImGuiID key,bool val)1831 void ImGuiStorage::SetBool(ImGuiID key, bool val)
1832 {
1833     SetInt(key, val ? 1 : 0);
1834 }
1835 
SetFloat(ImGuiID key,float val)1836 void ImGuiStorage::SetFloat(ImGuiID key, float val)
1837 {
1838     ImGuiStoragePair* it = LowerBound(Data, key);
1839     if (it == Data.end() || it->key != key)
1840     {
1841         Data.insert(it, ImGuiStoragePair(key, val));
1842         return;
1843     }
1844     it->val_f = val;
1845 }
1846 
SetVoidPtr(ImGuiID key,void * val)1847 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1848 {
1849     ImGuiStoragePair* it = LowerBound(Data, key);
1850     if (it == Data.end() || it->key != key)
1851     {
1852         Data.insert(it, ImGuiStoragePair(key, val));
1853         return;
1854     }
1855     it->val_p = val;
1856 }
1857 
SetAllInt(int v)1858 void ImGuiStorage::SetAllInt(int v)
1859 {
1860     for (int i = 0; i < Data.Size; i++)
1861         Data[i].val_i = v;
1862 }
1863 
1864 //-----------------------------------------------------------------------------
1865 // [SECTION] ImGuiTextFilter
1866 //-----------------------------------------------------------------------------
1867 
1868 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)1869 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
1870 {
1871     if (default_filter)
1872     {
1873         ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
1874         Build();
1875     }
1876     else
1877     {
1878         InputBuf[0] = 0;
1879         CountGrep = 0;
1880     }
1881 }
1882 
Draw(const char * label,float width)1883 bool ImGuiTextFilter::Draw(const char* label, float width)
1884 {
1885     if (width != 0.0f)
1886         ImGui::SetNextItemWidth(width);
1887     bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
1888     if (value_changed)
1889         Build();
1890     return value_changed;
1891 }
1892 
split(char separator,ImVector<ImGuiTextRange> * out) const1893 void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const
1894 {
1895     out->resize(0);
1896     const char* wb = b;
1897     const char* we = wb;
1898     while (we < e)
1899     {
1900         if (*we == separator)
1901         {
1902             out->push_back(ImGuiTextRange(wb, we));
1903             wb = we + 1;
1904         }
1905         we++;
1906     }
1907     if (wb != we)
1908         out->push_back(ImGuiTextRange(wb, we));
1909 }
1910 
Build()1911 void ImGuiTextFilter::Build()
1912 {
1913     Filters.resize(0);
1914     ImGuiTextRange input_range(InputBuf, InputBuf+strlen(InputBuf));
1915     input_range.split(',', &Filters);
1916 
1917     CountGrep = 0;
1918     for (int i = 0; i != Filters.Size; i++)
1919     {
1920         ImGuiTextRange& f = Filters[i];
1921         while (f.b < f.e && ImCharIsBlankA(f.b[0]))
1922             f.b++;
1923         while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
1924             f.e--;
1925         if (f.empty())
1926             continue;
1927         if (Filters[i].b[0] != '-')
1928             CountGrep += 1;
1929     }
1930 }
1931 
PassFilter(const char * text,const char * text_end) const1932 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
1933 {
1934     if (Filters.empty())
1935         return true;
1936 
1937     if (text == NULL)
1938         text = "";
1939 
1940     for (int i = 0; i != Filters.Size; i++)
1941     {
1942         const ImGuiTextRange& f = Filters[i];
1943         if (f.empty())
1944             continue;
1945         if (f.b[0] == '-')
1946         {
1947             // Subtract
1948             if (ImStristr(text, text_end, f.b + 1, f.e) != NULL)
1949                 return false;
1950         }
1951         else
1952         {
1953             // Grep
1954             if (ImStristr(text, text_end, f.b, f.e) != NULL)
1955                 return true;
1956         }
1957     }
1958 
1959     // Implicit * grep
1960     if (CountGrep == 0)
1961         return true;
1962 
1963     return false;
1964 }
1965 
1966 //-----------------------------------------------------------------------------
1967 // [SECTION] ImGuiTextBuffer
1968 //-----------------------------------------------------------------------------
1969 
1970 // On some platform vsnprintf() takes va_list by reference and modifies it.
1971 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
1972 #ifndef va_copy
1973 #if defined(__GNUC__) || defined(__clang__)
1974 #define va_copy(dest, src) __builtin_va_copy(dest, src)
1975 #else
1976 #define va_copy(dest, src) (dest = src)
1977 #endif
1978 #endif
1979 
1980 char ImGuiTextBuffer::EmptyString[1] = { 0 };
1981 
append(const char * str,const char * str_end)1982 void ImGuiTextBuffer::append(const char* str, const char* str_end)
1983 {
1984     int len = str_end ? (int)(str_end - str) : (int)strlen(str);
1985 
1986     // Add zero-terminator the first time
1987     const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
1988     const int needed_sz = write_off + len;
1989     if (write_off + len >= Buf.Capacity)
1990     {
1991         int new_capacity = Buf.Capacity * 2;
1992         Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
1993     }
1994 
1995     Buf.resize(needed_sz);
1996     memcpy(&Buf[write_off - 1], str, (size_t)len);
1997     Buf[write_off - 1 + len] = 0;
1998 }
1999 
appendf(const char * fmt,...)2000 void ImGuiTextBuffer::appendf(const char* fmt, ...)
2001 {
2002     va_list args;
2003     va_start(args, fmt);
2004     appendfv(fmt, args);
2005     va_end(args);
2006 }
2007 
2008 // Helper: Text buffer for logging/accumulating text
appendfv(const char * fmt,va_list args)2009 void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
2010 {
2011     va_list args_copy;
2012     va_copy(args_copy, args);
2013 
2014     int len = ImFormatStringV(NULL, 0, fmt, args);         // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
2015     if (len <= 0)
2016     {
2017         va_end(args_copy);
2018         return;
2019     }
2020 
2021     // Add zero-terminator the first time
2022     const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2023     const int needed_sz = write_off + len;
2024     if (write_off + len >= Buf.Capacity)
2025     {
2026         int new_capacity = Buf.Capacity * 2;
2027         Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2028     }
2029 
2030     Buf.resize(needed_sz);
2031     ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
2032     va_end(args_copy);
2033 }
2034 
2035 //-----------------------------------------------------------------------------
2036 // [SECTION] ImGuiListClipper
2037 // This is currently not as flexible/powerful as it should be and really confusing/spaghetti, mostly because we changed
2038 // the API mid-way through development and support two ways to using the clipper, needs some rework (see TODO)
2039 //-----------------------------------------------------------------------------
2040 
2041 // Helper to calculate coarse clipping of large list of evenly sized items.
2042 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
2043 // 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)2044 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
2045 {
2046     ImGuiContext& g = *GImGui;
2047     ImGuiWindow* window = g.CurrentWindow;
2048     if (g.LogEnabled)
2049     {
2050         // If logging is active, do not perform any clipping
2051         *out_items_display_start = 0;
2052         *out_items_display_end = items_count;
2053         return;
2054     }
2055     if (window->SkipItems)
2056     {
2057         *out_items_display_start = *out_items_display_end = 0;
2058         return;
2059     }
2060 
2061     // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect
2062     ImRect unclipped_rect = window->ClipRect;
2063     if (g.NavMoveRequest)
2064         unclipped_rect.Add(g.NavScoringRectScreen);
2065 
2066     const ImVec2 pos = window->DC.CursorPos;
2067     int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
2068     int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
2069 
2070     // When performing a navigation request, ensure we have one item extra in the direction we are moving to
2071     if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up)
2072         start--;
2073     if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down)
2074         end++;
2075 
2076     start = ImClamp(start, 0, items_count);
2077     end = ImClamp(end + 1, start, items_count);
2078     *out_items_display_start = start;
2079     *out_items_display_end = end;
2080 }
2081 
SetCursorPosYAndSetupDummyPrevLine(float pos_y,float line_height)2082 static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height)
2083 {
2084     // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
2085     // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
2086     // The clipper should probably have a 4th step to display the last item in a regular manner.
2087     ImGuiContext& g = *GImGui;
2088     ImGuiWindow* window = g.CurrentWindow;
2089     window->DC.CursorPos.y = pos_y;
2090     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y);
2091     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.
2092     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.
2093     if (ImGuiColumns* columns = window->DC.CurrentColumns)
2094         columns->LineMinY = window->DC.CursorPos.y;                         // Setting this so that cell Y position are set properly
2095 }
2096 
2097 // Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
2098 // Use case B: Begin() called from constructor with items_height>0
2099 // 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)2100 void ImGuiListClipper::Begin(int count, float items_height)
2101 {
2102     ImGuiContext& g = *GImGui;
2103     ImGuiWindow* window = g.CurrentWindow;
2104 
2105     StartPosY = window->DC.CursorPos.y;
2106     ItemsHeight = items_height;
2107     ItemsCount = count;
2108     StepNo = 0;
2109     DisplayEnd = DisplayStart = -1;
2110     if (ItemsHeight > 0.0f)
2111     {
2112         ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display
2113         if (DisplayStart > 0)
2114             SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor
2115         StepNo = 2;
2116     }
2117 }
2118 
End()2119 void ImGuiListClipper::End()
2120 {
2121     if (ItemsCount < 0)
2122         return;
2123     // 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.
2124     if (ItemsCount < INT_MAX)
2125         SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor
2126     ItemsCount = -1;
2127     StepNo = 3;
2128 }
2129 
Step()2130 bool ImGuiListClipper::Step()
2131 {
2132     ImGuiContext& g = *GImGui;
2133     ImGuiWindow* window = g.CurrentWindow;
2134 
2135     if (ItemsCount == 0 || window->SkipItems)
2136     {
2137         ItemsCount = -1;
2138         return false;
2139     }
2140     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.
2141     {
2142         DisplayStart = 0;
2143         DisplayEnd = 1;
2144         StartPosY = window->DC.CursorPos.y;
2145         StepNo = 1;
2146         return true;
2147     }
2148     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.
2149     {
2150         if (ItemsCount == 1) { ItemsCount = -1; return false; }
2151         float items_height = window->DC.CursorPos.y - StartPosY;
2152         IM_ASSERT(items_height > 0.0f);   // If this triggers, it means Item 0 hasn't moved the cursor vertically
2153         Begin(ItemsCount - 1, items_height);
2154         DisplayStart++;
2155         DisplayEnd++;
2156         StepNo = 3;
2157         return true;
2158     }
2159     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.
2160     {
2161         IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0);
2162         StepNo = 3;
2163         return true;
2164     }
2165     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.
2166         End();
2167     return false;
2168 }
2169 
2170 //-----------------------------------------------------------------------------
2171 // [SECTION] RENDER HELPERS
2172 // Those (internal) functions are currently quite a legacy mess - their signature and behavior will change.
2173 // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: state.
2174 //-----------------------------------------------------------------------------
2175 
FindRenderedTextEnd(const char * text,const char * text_end)2176 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2177 {
2178     const char* text_display_end = text;
2179     if (!text_end)
2180         text_end = (const char*)-1;
2181 
2182     while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2183         text_display_end++;
2184     return text_display_end;
2185 }
2186 
2187 // Internal ImGui functions to render text
2188 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)2189 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2190 {
2191     ImGuiContext& g = *GImGui;
2192     ImGuiWindow* window = g.CurrentWindow;
2193 
2194     // Hide anything after a '##' string
2195     const char* text_display_end;
2196     if (hide_text_after_hash)
2197     {
2198         text_display_end = FindRenderedTextEnd(text, text_end);
2199     }
2200     else
2201     {
2202         if (!text_end)
2203             text_end = text + strlen(text); // FIXME-OPT
2204         text_display_end = text_end;
2205     }
2206 
2207     if (text != text_display_end)
2208     {
2209         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2210         if (g.LogEnabled)
2211             LogRenderedText(&pos, text, text_display_end);
2212     }
2213 }
2214 
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)2215 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2216 {
2217     ImGuiContext& g = *GImGui;
2218     ImGuiWindow* window = g.CurrentWindow;
2219 
2220     if (!text_end)
2221         text_end = text + strlen(text); // FIXME-OPT
2222 
2223     if (text != text_end)
2224     {
2225         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2226         if (g.LogEnabled)
2227             LogRenderedText(&pos, text, text_end);
2228     }
2229 }
2230 
2231 // Default clip_rect uses (pos_min,pos_max)
2232 // 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)2233 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)
2234 {
2235     // Perform CPU side clipping for single clipped element to avoid using scissor state
2236     ImVec2 pos = pos_min;
2237     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
2238 
2239     const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
2240     const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
2241     bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2242     if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
2243         need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2244 
2245     // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
2246     if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
2247     if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
2248 
2249     // Render
2250     if (need_clipping)
2251     {
2252         ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2253         draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2254     }
2255     else
2256     {
2257         draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2258     }
2259 }
2260 
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)2261 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)
2262 {
2263     // Hide anything after a '##' string
2264     const char* text_display_end = FindRenderedTextEnd(text, text_end);
2265     const int text_len = (int)(text_display_end - text);
2266     if (text_len == 0)
2267         return;
2268 
2269     ImGuiContext& g = *GImGui;
2270     ImGuiWindow* window = g.CurrentWindow;
2271     RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
2272     if (g.LogEnabled)
2273         LogRenderedText(&pos_min, text, text_display_end);
2274 }
2275 
2276 
2277 // Another overly complex function until we reorganize everything into a nice all-in-one helper.
2278 // 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.
2279 // 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)2280 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)
2281 {
2282     ImGuiContext& g = *GImGui;
2283     if (text_end_full == NULL)
2284         text_end_full = FindRenderedTextEnd(text);
2285     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);
2286 
2287     //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));
2288     //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));
2289     //draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255));
2290     // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
2291     if (text_size.x > pos_max.x - pos_min.x)
2292     {
2293         // Hello wo...
2294         // |       |   |
2295         // min   max   ellipsis_max
2296         //          <-> this is generally some padding value
2297 
2298         const ImFont* font = draw_list->_Data->Font;
2299         const float font_size = draw_list->_Data->FontSize;
2300         const char* text_end_ellipsis = NULL;
2301 
2302         ImWchar ellipsis_char = font->EllipsisChar;
2303         int ellipsis_char_count = 1;
2304         if (ellipsis_char == (ImWchar)-1)
2305         {
2306             ellipsis_char = (ImWchar)'.';
2307             ellipsis_char_count = 3;
2308         }
2309         const ImFontGlyph* glyph = font->FindGlyph(ellipsis_char);
2310 
2311         float ellipsis_glyph_width = glyph->X1;                 // Width of the glyph with no padding on either side
2312         float ellipsis_total_width = ellipsis_glyph_width;      // Full width of entire ellipsis
2313 
2314         if (ellipsis_char_count > 1)
2315         {
2316             // Full ellipsis size without free spacing after it.
2317             const float spacing_between_dots = 1.0f * (draw_list->_Data->FontSize / font->FontSize);
2318             ellipsis_glyph_width = glyph->X1 - glyph->X0 + spacing_between_dots;
2319             ellipsis_total_width = ellipsis_glyph_width * (float)ellipsis_char_count - spacing_between_dots;
2320         }
2321 
2322         // We can now claim the space between pos_max.x and ellipsis_max.x
2323         const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_total_width) - pos_min.x, 1.0f);
2324         float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
2325         if (text == text_end_ellipsis && text_end_ellipsis < text_end_full)
2326         {
2327             // Always display at least 1 character if there's no room for character + ellipsis
2328             text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full);
2329             text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x;
2330         }
2331         while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1]))
2332         {
2333             // 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)
2334             text_end_ellipsis--;
2335             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
2336         }
2337 
2338         // Render text, render ellipsis
2339         RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
2340         float ellipsis_x = pos_min.x + text_size_clipped_x;
2341         if (ellipsis_x + ellipsis_total_width <= ellipsis_max_x)
2342             for (int i = 0; i < ellipsis_char_count; i++)
2343             {
2344                 font->RenderChar(draw_list, font_size, ImVec2(ellipsis_x, pos_min.y), GetColorU32(ImGuiCol_Text), ellipsis_char);
2345                 ellipsis_x += ellipsis_glyph_width;
2346             }
2347     }
2348     else
2349     {
2350         RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));
2351     }
2352 
2353     if (g.LogEnabled)
2354         LogRenderedText(&pos_min, text, text_end_full);
2355 }
2356 
2357 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)2358 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2359 {
2360     ImGuiContext& g = *GImGui;
2361     ImGuiWindow* window = g.CurrentWindow;
2362     window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2363     const float border_size = g.Style.FrameBorderSize;
2364     if (border && border_size > 0.0f)
2365     {
2366         window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2367         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2368     }
2369 }
2370 
RenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding)2371 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
2372 {
2373     ImGuiContext& g = *GImGui;
2374     ImGuiWindow* window = g.CurrentWindow;
2375     const float border_size = g.Style.FrameBorderSize;
2376     if (border_size > 0.0f)
2377     {
2378         window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2379         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2380     }
2381 }
2382 
2383 // 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)2384 void ImGui::RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale)
2385 {
2386     const float h = draw_list->_Data->FontSize * 1.00f;
2387     float r = h * 0.40f * scale;
2388     ImVec2 center = pos + ImVec2(h * 0.50f, h * 0.50f * scale);
2389 
2390     ImVec2 a, b, c;
2391     switch (dir)
2392     {
2393     case ImGuiDir_Up:
2394     case ImGuiDir_Down:
2395         if (dir == ImGuiDir_Up) r = -r;
2396         a = ImVec2(+0.000f,+0.750f) * r;
2397         b = ImVec2(-0.866f,-0.750f) * r;
2398         c = ImVec2(+0.866f,-0.750f) * r;
2399         break;
2400     case ImGuiDir_Left:
2401     case ImGuiDir_Right:
2402         if (dir == ImGuiDir_Left) r = -r;
2403         a = ImVec2(+0.750f,+0.000f) * r;
2404         b = ImVec2(-0.750f,+0.866f) * r;
2405         c = ImVec2(-0.750f,-0.866f) * r;
2406         break;
2407     case ImGuiDir_None:
2408     case ImGuiDir_COUNT:
2409         IM_ASSERT(0);
2410         break;
2411     }
2412     draw_list->AddTriangleFilled(center + a, center + b, center + c, col);
2413 }
2414 
RenderBullet(ImDrawList * draw_list,ImVec2 pos,ImU32 col)2415 void ImGui::RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col)
2416 {
2417     draw_list->AddCircleFilled(pos, draw_list->_Data->FontSize * 0.20f, col, 8);
2418 }
2419 
RenderCheckMark(ImVec2 pos,ImU32 col,float sz)2420 void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz)
2421 {
2422     ImGuiContext& g = *GImGui;
2423     ImGuiWindow* window = g.CurrentWindow;
2424 
2425     float thickness = ImMax(sz / 5.0f, 1.0f);
2426     sz -= thickness*0.5f;
2427     pos += ImVec2(thickness*0.25f, thickness*0.25f);
2428 
2429     float third = sz / 3.0f;
2430     float bx = pos.x + third;
2431     float by = pos.y + sz - third*0.5f;
2432     window->DrawList->PathLineTo(ImVec2(bx - third, by - third));
2433     window->DrawList->PathLineTo(ImVec2(bx, by));
2434     window->DrawList->PathLineTo(ImVec2(bx + third*2, by - third*2));
2435     window->DrawList->PathStroke(col, false, thickness);
2436 }
2437 
RenderNavHighlight(const ImRect & bb,ImGuiID id,ImGuiNavHighlightFlags flags)2438 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
2439 {
2440     ImGuiContext& g = *GImGui;
2441     if (id != g.NavId)
2442         return;
2443     if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
2444         return;
2445     ImGuiWindow* window = g.CurrentWindow;
2446     if (window->DC.NavHideHighlightOneFrame)
2447         return;
2448 
2449     float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
2450     ImRect display_rect = bb;
2451     display_rect.ClipWith(window->ClipRect);
2452     if (flags & ImGuiNavHighlightFlags_TypeDefault)
2453     {
2454         const float THICKNESS = 2.0f;
2455         const float DISTANCE = 3.0f + THICKNESS * 0.5f;
2456         display_rect.Expand(ImVec2(DISTANCE,DISTANCE));
2457         bool fully_visible = window->ClipRect.Contains(display_rect);
2458         if (!fully_visible)
2459             window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
2460         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);
2461         if (!fully_visible)
2462             window->DrawList->PopClipRect();
2463     }
2464     if (flags & ImGuiNavHighlightFlags_TypeThin)
2465     {
2466         window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f);
2467     }
2468 }
2469 
2470 //-----------------------------------------------------------------------------
2471 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
2472 //-----------------------------------------------------------------------------
2473 
2474 // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
ImGuiWindow(ImGuiContext * context,const char * name)2475 ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
2476     : DrawListInst(&context->DrawListSharedData)
2477 {
2478     Name = ImStrdup(name);
2479     ID = ImHashStr(name);
2480     IDStack.push_back(ID);
2481     Flags = ImGuiWindowFlags_None;
2482     Pos = ImVec2(0.0f, 0.0f);
2483     Size = SizeFull = ImVec2(0.0f, 0.0f);
2484     ContentSize = ContentSizeExplicit = ImVec2(0.0f, 0.0f);
2485     WindowPadding = ImVec2(0.0f, 0.0f);
2486     WindowRounding = 0.0f;
2487     WindowBorderSize = 0.0f;
2488     NameBufLen = (int)strlen(name) + 1;
2489     MoveId = GetID("#MOVE");
2490     ChildId = 0;
2491     Scroll = ImVec2(0.0f, 0.0f);
2492     ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
2493     ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
2494     ScrollbarSizes = ImVec2(0.0f, 0.0f);
2495     ScrollbarX = ScrollbarY = false;
2496     Active = WasActive = false;
2497     WriteAccessed = false;
2498     Collapsed = false;
2499     WantCollapseToggle = false;
2500     SkipItems = false;
2501     Appearing = false;
2502     Hidden = false;
2503     HasCloseButton = false;
2504     ResizeBorderHeld = -1;
2505     BeginCount = 0;
2506     BeginOrderWithinParent = -1;
2507     BeginOrderWithinContext = -1;
2508     PopupId = 0;
2509     AutoFitFramesX = AutoFitFramesY = -1;
2510     AutoFitChildAxises = 0x00;
2511     AutoFitOnlyGrows = false;
2512     AutoPosLastDirection = ImGuiDir_None;
2513     HiddenFramesCanSkipItems = HiddenFramesCannotSkipItems = 0;
2514     SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
2515     SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
2516 
2517     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.
2518 
2519     LastFrameActive = -1;
2520     LastTimeActive = -1.0f;
2521     ItemWidthDefault = 0.0f;
2522     FontWindowScale = 1.0f;
2523     SettingsIdx = -1;
2524 
2525     DrawList = &DrawListInst;
2526     DrawList->_OwnerName = Name;
2527     ParentWindow = NULL;
2528     RootWindow = NULL;
2529     RootWindowForTitleBarHighlight = NULL;
2530     RootWindowForNav = NULL;
2531 
2532     NavLastIds[0] = NavLastIds[1] = 0;
2533     NavRectRel[0] = NavRectRel[1] = ImRect();
2534     NavLastChildNavWindow = NULL;
2535 
2536     MemoryCompacted = false;
2537     MemoryDrawListIdxCapacity = MemoryDrawListVtxCapacity = 0;
2538 }
2539 
~ImGuiWindow()2540 ImGuiWindow::~ImGuiWindow()
2541 {
2542     IM_ASSERT(DrawList == &DrawListInst);
2543     IM_DELETE(Name);
2544     for (int i = 0; i != ColumnsStorage.Size; i++)
2545         ColumnsStorage[i].~ImGuiColumns();
2546 }
2547 
GetID(const char * str,const char * str_end)2548 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
2549 {
2550     ImGuiID seed = IDStack.back();
2551     ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2552     ImGui::KeepAliveID(id);
2553     return id;
2554 }
2555 
GetID(const void * ptr)2556 ImGuiID ImGuiWindow::GetID(const void* ptr)
2557 {
2558     ImGuiID seed = IDStack.back();
2559     ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
2560     ImGui::KeepAliveID(id);
2561     return id;
2562 }
2563 
GetID(int n)2564 ImGuiID ImGuiWindow::GetID(int n)
2565 {
2566     ImGuiID seed = IDStack.back();
2567     ImGuiID id = ImHashData(&n, sizeof(n), seed);
2568     ImGui::KeepAliveID(id);
2569     return id;
2570 }
2571 
GetIDNoKeepAlive(const char * str,const char * str_end)2572 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
2573 {
2574     ImGuiID seed = IDStack.back();
2575     return ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2576 }
2577 
GetIDNoKeepAlive(const void * ptr)2578 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
2579 {
2580     ImGuiID seed = IDStack.back();
2581     return ImHashData(&ptr, sizeof(void*), seed);
2582 }
2583 
GetIDNoKeepAlive(int n)2584 ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n)
2585 {
2586     ImGuiID seed = IDStack.back();
2587     return ImHashData(&n, sizeof(n), seed);
2588 }
2589 
2590 // This is only used in rare/specific situations to manufacture an ID out of nowhere.
GetIDFromRectangle(const ImRect & r_abs)2591 ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
2592 {
2593     ImGuiID seed = IDStack.back();
2594     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) };
2595     ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
2596     ImGui::KeepAliveID(id);
2597     return id;
2598 }
2599 
SetCurrentWindow(ImGuiWindow * window)2600 static void SetCurrentWindow(ImGuiWindow* window)
2601 {
2602     ImGuiContext& g = *GImGui;
2603     g.CurrentWindow = window;
2604     if (window)
2605         g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
2606 }
2607 
2608 // Free up/compact internal window buffers, we can use this when a window becomes unused.
2609 // This is currently unused by the library, but you may call this yourself for easy GC.
2610 // Not freed:
2611 // - ImGuiWindow, ImGuiWindowSettings, Name
2612 // - StateStorage, ColumnsStorage (may hold useful data)
2613 // This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.
GcCompactTransientWindowBuffers(ImGuiWindow * window)2614 void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window)
2615 {
2616     window->MemoryCompacted = true;
2617     window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;
2618     window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;
2619     window->IDStack.clear();
2620     window->DrawList->ClearFreeMemory();
2621     window->DC.ChildWindows.clear();
2622     window->DC.ItemFlagsStack.clear();
2623     window->DC.ItemWidthStack.clear();
2624     window->DC.TextWrapPosStack.clear();
2625     window->DC.GroupStack.clear();
2626 }
2627 
GcAwakeTransientWindowBuffers(ImGuiWindow * window)2628 void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window)
2629 {
2630     // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.
2631     // The other buffers tends to amortize much faster.
2632     window->MemoryCompacted = false;
2633     window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity);
2634     window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity);
2635     window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0;
2636 }
2637 
SetNavID(ImGuiID id,int nav_layer)2638 void ImGui::SetNavID(ImGuiID id, int nav_layer)
2639 {
2640     ImGuiContext& g = *GImGui;
2641     IM_ASSERT(g.NavWindow);
2642     IM_ASSERT(nav_layer == 0 || nav_layer == 1);
2643     g.NavId = id;
2644     g.NavWindow->NavLastIds[nav_layer] = id;
2645 }
2646 
SetNavIDWithRectRel(ImGuiID id,int nav_layer,const ImRect & rect_rel)2647 void ImGui::SetNavIDWithRectRel(ImGuiID id, int nav_layer, const ImRect& rect_rel)
2648 {
2649     ImGuiContext& g = *GImGui;
2650     SetNavID(id, nav_layer);
2651     g.NavWindow->NavRectRel[nav_layer] = rect_rel;
2652     g.NavMousePosDirty = true;
2653     g.NavDisableHighlight = false;
2654     g.NavDisableMouseHover = true;
2655 }
2656 
SetActiveID(ImGuiID id,ImGuiWindow * window)2657 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
2658 {
2659     ImGuiContext& g = *GImGui;
2660     g.ActiveIdIsJustActivated = (g.ActiveId != id);
2661     if (g.ActiveIdIsJustActivated)
2662     {
2663         g.ActiveIdTimer = 0.0f;
2664         g.ActiveIdHasBeenPressedBefore = false;
2665         g.ActiveIdHasBeenEditedBefore = false;
2666         if (id != 0)
2667         {
2668             g.LastActiveId = id;
2669             g.LastActiveIdTimer = 0.0f;
2670         }
2671     }
2672     g.ActiveId = id;
2673     g.ActiveIdAllowOverlap = false;
2674     g.ActiveIdWindow = window;
2675     g.ActiveIdHasBeenEditedThisFrame = false;
2676     if (id)
2677     {
2678         g.ActiveIdIsAlive = id;
2679         g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
2680     }
2681 
2682     // Clear declaration of inputs claimed by the widget
2683     // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
2684     g.ActiveIdUsingNavDirMask = 0x00;
2685     g.ActiveIdUsingNavInputMask = 0x00;
2686     g.ActiveIdUsingKeyInputMask = 0x00;
2687 }
2688 
2689 // FIXME-NAV: The existence of SetNavID/SetNavIDWithRectRel/SetFocusID is incredibly messy and confusing and needs some explanation or refactoring.
SetFocusID(ImGuiID id,ImGuiWindow * window)2690 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
2691 {
2692     ImGuiContext& g = *GImGui;
2693     IM_ASSERT(id != 0);
2694 
2695     // Assume that SetFocusID() is called in the context where its NavLayer is the current layer, which is the case everywhere we call it.
2696     const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
2697     if (g.NavWindow != window)
2698         g.NavInitRequest = false;
2699     g.NavId = id;
2700     g.NavWindow = window;
2701     g.NavLayer = nav_layer;
2702     window->NavLastIds[nav_layer] = id;
2703     if (window->DC.LastItemId == id)
2704         window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos);
2705 
2706     if (g.ActiveIdSource == ImGuiInputSource_Nav)
2707         g.NavDisableMouseHover = true;
2708     else
2709         g.NavDisableHighlight = true;
2710 }
2711 
ClearActiveID()2712 void ImGui::ClearActiveID()
2713 {
2714     SetActiveID(0, NULL);
2715 }
2716 
SetHoveredID(ImGuiID id)2717 void ImGui::SetHoveredID(ImGuiID id)
2718 {
2719     ImGuiContext& g = *GImGui;
2720     g.HoveredId = id;
2721     g.HoveredIdAllowOverlap = false;
2722     if (id != 0 && g.HoveredIdPreviousFrame != id)
2723         g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
2724 }
2725 
GetHoveredID()2726 ImGuiID ImGui::GetHoveredID()
2727 {
2728     ImGuiContext& g = *GImGui;
2729     return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
2730 }
2731 
KeepAliveID(ImGuiID id)2732 void ImGui::KeepAliveID(ImGuiID id)
2733 {
2734     ImGuiContext& g = *GImGui;
2735     if (g.ActiveId == id)
2736         g.ActiveIdIsAlive = id;
2737     if (g.ActiveIdPreviousFrame == id)
2738         g.ActiveIdPreviousFrameIsAlive = true;
2739 }
2740 
MarkItemEdited(ImGuiID id)2741 void ImGui::MarkItemEdited(ImGuiID id)
2742 {
2743     // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
2744     // 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.
2745     ImGuiContext& g = *GImGui;
2746     IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive);
2747     IM_UNUSED(id); // Avoid unused variable warnings when asserts are compiled out.
2748     //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
2749     g.ActiveIdHasBeenEditedThisFrame = true;
2750     g.ActiveIdHasBeenEditedBefore = true;
2751     g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
2752 }
2753 
IsWindowContentHoverable(ImGuiWindow * window,ImGuiHoveredFlags flags)2754 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
2755 {
2756     // An active popup disable hovering on other windows (apart from its own children)
2757     // FIXME-OPT: This could be cached/stored within the window.
2758     ImGuiContext& g = *GImGui;
2759     if (g.NavWindow)
2760         if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
2761             if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
2762             {
2763                 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
2764                 // NB: The order of those two tests is important because Modal windows are also Popups.
2765                 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
2766                     return false;
2767                 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
2768                     return false;
2769             }
2770 
2771     return true;
2772 }
2773 
2774 // Advance cursor given item size for layout.
ItemSize(const ImVec2 & size,float text_baseline_y)2775 void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
2776 {
2777     ImGuiContext& g = *GImGui;
2778     ImGuiWindow* window = g.CurrentWindow;
2779     if (window->SkipItems)
2780         return;
2781 
2782     // We increase the height in this function to accommodate for baseline offset.
2783     // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
2784     // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
2785     const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
2786     const float line_height = ImMax(window->DC.CurrLineSize.y, size.y + offset_to_match_baseline_y);
2787 
2788     // Always align ourselves on pixel boundaries
2789     //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]
2790     window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
2791     window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y;
2792     window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);    // Next line
2793     window->DC.CursorPos.y = IM_FLOOR(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y);        // Next line
2794     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
2795     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
2796     //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
2797 
2798     window->DC.PrevLineSize.y = line_height;
2799     window->DC.CurrLineSize.y = 0.0f;
2800     window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
2801     window->DC.CurrLineTextBaseOffset = 0.0f;
2802 
2803     // Horizontal layout mode
2804     if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
2805         SameLine();
2806 }
2807 
ItemSize(const ImRect & bb,float text_baseline_y)2808 void ImGui::ItemSize(const ImRect& bb, float text_baseline_y)
2809 {
2810     ItemSize(bb.GetSize(), text_baseline_y);
2811 }
2812 
2813 // Declare item bounding box for clipping and interaction.
2814 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
2815 // 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)2816 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg)
2817 {
2818     ImGuiContext& g = *GImGui;
2819     ImGuiWindow* window = g.CurrentWindow;
2820 
2821     if (id != 0)
2822     {
2823         // Navigation processing runs prior to clipping early-out
2824         //  (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
2825         //  (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
2826         //      unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
2827         //      thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
2828         //      We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
2829         //      to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
2830         // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
2831         // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
2832         window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask;
2833         if (g.NavId == id || g.NavAnyRequest)
2834             if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
2835                 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
2836                     NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
2837 
2838         // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd()
2839 #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
2840         if (id == g.DebugItemPickerBreakID)
2841         {
2842             IM_DEBUG_BREAK();
2843             g.DebugItemPickerBreakID = 0;
2844         }
2845 #endif
2846     }
2847 
2848     window->DC.LastItemId = id;
2849     window->DC.LastItemRect = bb;
2850     window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None;
2851     g.NextItemData.Flags = ImGuiNextItemDataFlags_None;
2852 
2853 #ifdef IMGUI_ENABLE_TEST_ENGINE
2854     if (id != 0)
2855         IMGUI_TEST_ENGINE_ITEM_ADD(nav_bb_arg ? *nav_bb_arg : bb, id);
2856 #endif
2857 
2858     // Clipping test
2859     const bool is_clipped = IsClippedEx(bb, id, false);
2860     if (is_clipped)
2861         return false;
2862     //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
2863 
2864     // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
2865     if (IsMouseHoveringRect(bb.Min, bb.Max))
2866         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect;
2867     return true;
2868 }
2869 
2870 // This is roughly matching the behavior of internal-facing ItemHoverable()
2871 // - 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()
2872 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
IsItemHovered(ImGuiHoveredFlags flags)2873 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
2874 {
2875     ImGuiContext& g = *GImGui;
2876     ImGuiWindow* window = g.CurrentWindow;
2877     if (g.NavDisableMouseHover && !g.NavDisableHighlight)
2878         return IsItemFocused();
2879 
2880     // Test for bounding box overlap, as updated as ItemAdd()
2881     if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
2882         return false;
2883     IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0);   // Flags not supported by this function
2884 
2885     // Test if we are hovering the right window (our window could be behind another window)
2886     // [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.
2887     // 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.
2888     //if (g.HoveredWindow != window)
2889     //    return false;
2890     if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
2891         return false;
2892 
2893     // Test if another item is active (e.g. being dragged)
2894     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
2895         if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
2896             return false;
2897 
2898     // Test if interactions on this window are blocked by an active popup or modal.
2899     // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
2900     if (!IsWindowContentHoverable(window, flags))
2901         return false;
2902 
2903     // Test if the item is disabled
2904     if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
2905         return false;
2906 
2907     // Special handling for the dummy item after Begin() which represent the title bar or tab.
2908     // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
2909     if (window->DC.LastItemId == window->MoveId && window->WriteAccessed)
2910         return false;
2911     return true;
2912 }
2913 
2914 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
ItemHoverable(const ImRect & bb,ImGuiID id)2915 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
2916 {
2917     ImGuiContext& g = *GImGui;
2918     if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
2919         return false;
2920 
2921     ImGuiWindow* window = g.CurrentWindow;
2922     if (g.HoveredWindow != window)
2923         return false;
2924     if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
2925         return false;
2926     if (!IsMouseHoveringRect(bb.Min, bb.Max))
2927         return false;
2928     if (g.NavDisableMouseHover || !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
2929         return false;
2930     if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
2931         return false;
2932 
2933     SetHoveredID(id);
2934 
2935     // [DEBUG] Item Picker tool!
2936     // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making
2937     // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered
2938     // items if we perform the test in ItemAdd(), but that would incur a small runtime cost.
2939     // #define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX in imconfig.h if you want this check to also be performed in ItemAdd().
2940     if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
2941         GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
2942     if (g.DebugItemPickerBreakID == id)
2943         IM_DEBUG_BREAK();
2944 
2945     return true;
2946 }
2947 
IsClippedEx(const ImRect & bb,ImGuiID id,bool clip_even_when_logged)2948 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
2949 {
2950     ImGuiContext& g = *GImGui;
2951     ImGuiWindow* window = g.CurrentWindow;
2952     if (!bb.Overlaps(window->ClipRect))
2953         if (id == 0 || id != g.ActiveId)
2954             if (clip_even_when_logged || !g.LogEnabled)
2955                 return true;
2956     return false;
2957 }
2958 
2959 // Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out.
FocusableItemRegister(ImGuiWindow * window,ImGuiID id)2960 bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id)
2961 {
2962     ImGuiContext& g = *GImGui;
2963 
2964     // Increment counters
2965     const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0;
2966     window->DC.FocusCounterAll++;
2967     if (is_tab_stop)
2968         window->DC.FocusCounterTab++;
2969 
2970     // Process TAB/Shift-TAB to tab *OUT* of the currently focused item.
2971     // (Note that we can always TAB out of a widget that doesn't allow tabbing in)
2972     if (g.ActiveId == id && g.FocusTabPressed && !IsActiveIdUsingKey(ImGuiKey_Tab) && g.FocusRequestNextWindow == NULL)
2973     {
2974         g.FocusRequestNextWindow = window;
2975         g.FocusRequestNextCounterTab = window->DC.FocusCounterTab + (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.
2976     }
2977 
2978     // Handle focus requests
2979     if (g.FocusRequestCurrWindow == window)
2980     {
2981         if (window->DC.FocusCounterAll == g.FocusRequestCurrCounterAll)
2982             return true;
2983         if (is_tab_stop && window->DC.FocusCounterTab == g.FocusRequestCurrCounterTab)
2984         {
2985             g.NavJustTabbedId = id;
2986             return true;
2987         }
2988 
2989         // If another item is about to be focused, we clear our own active id
2990         if (g.ActiveId == id)
2991             ClearActiveID();
2992     }
2993 
2994     return false;
2995 }
2996 
FocusableItemUnregister(ImGuiWindow * window)2997 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
2998 {
2999     window->DC.FocusCounterAll--;
3000     window->DC.FocusCounterTab--;
3001 }
3002 
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)3003 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
3004 {
3005     if (wrap_pos_x < 0.0f)
3006         return 0.0f;
3007 
3008     ImGuiWindow* window = GImGui->CurrentWindow;
3009     if (wrap_pos_x == 0.0f)
3010         wrap_pos_x = window->WorkRect.Max.x;
3011     else if (wrap_pos_x > 0.0f)
3012         wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
3013 
3014     return ImMax(wrap_pos_x - pos.x, 1.0f);
3015 }
3016 
3017 // IM_ALLOC() == ImGui::MemAlloc()
MemAlloc(size_t size)3018 void* ImGui::MemAlloc(size_t size)
3019 {
3020     if (ImGuiContext* ctx = GImGui)
3021         ctx->IO.MetricsActiveAllocations++;
3022     return GImAllocatorAllocFunc(size, GImAllocatorUserData);
3023 }
3024 
3025 // IM_FREE() == ImGui::MemFree()
MemFree(void * ptr)3026 void ImGui::MemFree(void* ptr)
3027 {
3028     if (ptr)
3029         if (ImGuiContext* ctx = GImGui)
3030             ctx->IO.MetricsActiveAllocations--;
3031     return GImAllocatorFreeFunc(ptr, GImAllocatorUserData);
3032 }
3033 
GetClipboardText()3034 const char* ImGui::GetClipboardText()
3035 {
3036     ImGuiContext& g = *GImGui;
3037     return g.IO.GetClipboardTextFn ? g.IO.GetClipboardTextFn(g.IO.ClipboardUserData) : "";
3038 }
3039 
SetClipboardText(const char * text)3040 void ImGui::SetClipboardText(const char* text)
3041 {
3042     ImGuiContext& g = *GImGui;
3043     if (g.IO.SetClipboardTextFn)
3044         g.IO.SetClipboardTextFn(g.IO.ClipboardUserData, text);
3045 }
3046 
GetVersion()3047 const char* ImGui::GetVersion()
3048 {
3049     return IMGUI_VERSION;
3050 }
3051 
3052 // Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself
3053 // 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()3054 ImGuiContext* ImGui::GetCurrentContext()
3055 {
3056     return GImGui;
3057 }
3058 
SetCurrentContext(ImGuiContext * ctx)3059 void ImGui::SetCurrentContext(ImGuiContext* ctx)
3060 {
3061 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
3062     IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
3063 #else
3064     GImGui = ctx;
3065 #endif
3066 }
3067 
3068 // Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui.
3069 // Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
3070 // If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. your user code
3071 // may see different structures than what imgui.cpp sees, which is problematic.
3072 // 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)3073 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)
3074 {
3075     bool error = false;
3076     if (strcmp(version, IMGUI_VERSION)!=0) { error = true; IM_ASSERT(strcmp(version,IMGUI_VERSION)==0 && "Mismatched version string!");  }
3077     if (sz_io    != sizeof(ImGuiIO))       { error = true; IM_ASSERT(sz_io    == sizeof(ImGuiIO)      && "Mismatched struct layout!"); }
3078     if (sz_style != sizeof(ImGuiStyle))    { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle)   && "Mismatched struct layout!"); }
3079     if (sz_vec2  != sizeof(ImVec2))        { error = true; IM_ASSERT(sz_vec2  == sizeof(ImVec2)       && "Mismatched struct layout!"); }
3080     if (sz_vec4  != sizeof(ImVec4))        { error = true; IM_ASSERT(sz_vec4  == sizeof(ImVec4)       && "Mismatched struct layout!"); }
3081     if (sz_vert  != sizeof(ImDrawVert))    { error = true; IM_ASSERT(sz_vert  == sizeof(ImDrawVert)   && "Mismatched struct layout!"); }
3082     if (sz_idx   != sizeof(ImDrawIdx))     { error = true; IM_ASSERT(sz_idx   == sizeof(ImDrawIdx)    && "Mismatched struct layout!"); }
3083     return !error;
3084 }
3085 
SetAllocatorFunctions(void * (* alloc_func)(size_t sz,void * user_data),void (* free_func)(void * ptr,void * user_data),void * user_data)3086 void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data)
3087 {
3088     GImAllocatorAllocFunc = alloc_func;
3089     GImAllocatorFreeFunc = free_func;
3090     GImAllocatorUserData = user_data;
3091 }
3092 
CreateContext(ImFontAtlas * shared_font_atlas)3093 ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
3094 {
3095     ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
3096     if (GImGui == NULL)
3097         SetCurrentContext(ctx);
3098     Initialize(ctx);
3099     return ctx;
3100 }
3101 
DestroyContext(ImGuiContext * ctx)3102 void ImGui::DestroyContext(ImGuiContext* ctx)
3103 {
3104     if (ctx == NULL)
3105         ctx = GImGui;
3106     Shutdown(ctx);
3107     if (GImGui == ctx)
3108         SetCurrentContext(NULL);
3109     IM_DELETE(ctx);
3110 }
3111 
GetIO()3112 ImGuiIO& ImGui::GetIO()
3113 {
3114     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3115     return GImGui->IO;
3116 }
3117 
GetStyle()3118 ImGuiStyle& ImGui::GetStyle()
3119 {
3120     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3121     return GImGui->Style;
3122 }
3123 
3124 // Same value as passed to the old io.RenderDrawListsFn function. Valid after Render() and until the next call to NewFrame()
GetDrawData()3125 ImDrawData* ImGui::GetDrawData()
3126 {
3127     ImGuiContext& g = *GImGui;
3128     return g.DrawData.Valid ? &g.DrawData : NULL;
3129 }
3130 
GetTime()3131 double ImGui::GetTime()
3132 {
3133     return GImGui->Time;
3134 }
3135 
GetFrameCount()3136 int ImGui::GetFrameCount()
3137 {
3138     return GImGui->FrameCount;
3139 }
3140 
GetBackgroundDrawList()3141 ImDrawList* ImGui::GetBackgroundDrawList()
3142 {
3143     return &GImGui->BackgroundDrawList;
3144 }
3145 
GetForegroundDrawList()3146 ImDrawList* ImGui::GetForegroundDrawList()
3147 {
3148     return &GImGui->ForegroundDrawList;
3149 }
3150 
GetDrawListSharedData()3151 ImDrawListSharedData* ImGui::GetDrawListSharedData()
3152 {
3153     return &GImGui->DrawListSharedData;
3154 }
3155 
StartMouseMovingWindow(ImGuiWindow * window)3156 void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
3157 {
3158     // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
3159     // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
3160     // This is because we want ActiveId to be set even when the window is not permitted to move.
3161     ImGuiContext& g = *GImGui;
3162     FocusWindow(window);
3163     SetActiveID(window->MoveId, window);
3164     g.NavDisableHighlight = true;
3165     g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos;
3166 
3167     bool can_move_window = true;
3168     if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
3169         can_move_window = false;
3170     if (can_move_window)
3171         g.MovingWindow = window;
3172 }
3173 
3174 // Handle mouse moving window
3175 // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
UpdateMouseMovingWindowNewFrame()3176 void ImGui::UpdateMouseMovingWindowNewFrame()
3177 {
3178     ImGuiContext& g = *GImGui;
3179     if (g.MovingWindow != NULL)
3180     {
3181         // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
3182         // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
3183         KeepAliveID(g.ActiveId);
3184         IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
3185         ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
3186         if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
3187         {
3188             ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
3189             if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
3190             {
3191                 MarkIniSettingsDirty(moving_window);
3192                 SetWindowPos(moving_window, pos, ImGuiCond_Always);
3193             }
3194             FocusWindow(g.MovingWindow);
3195         }
3196         else
3197         {
3198             ClearActiveID();
3199             g.MovingWindow = NULL;
3200         }
3201     }
3202     else
3203     {
3204         // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
3205         if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
3206         {
3207             KeepAliveID(g.ActiveId);
3208             if (!g.IO.MouseDown[0])
3209                 ClearActiveID();
3210         }
3211     }
3212 }
3213 
3214 // Initiate moving window, handle left-click and right-click focus
UpdateMouseMovingWindowEndFrame()3215 void ImGui::UpdateMouseMovingWindowEndFrame()
3216 {
3217     // Initiate moving window
3218     ImGuiContext& g = *GImGui;
3219     if (g.ActiveId != 0 || g.HoveredId != 0)
3220         return;
3221 
3222     // Unless we just made a window/popup appear
3223     if (g.NavWindow && g.NavWindow->Appearing)
3224         return;
3225 
3226     // Click to focus window and start moving (after we're done with all our widgets)
3227     if (g.IO.MouseClicked[0])
3228     {
3229         if (g.HoveredRootWindow != NULL)
3230         {
3231             StartMouseMovingWindow(g.HoveredWindow);
3232             if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoTitleBar))
3233                 if (!g.HoveredRootWindow->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
3234                     g.MovingWindow = NULL;
3235         }
3236         else if (g.NavWindow != NULL && GetTopMostPopupModal() == NULL)
3237         {
3238             // Clicking on void disable focus
3239             FocusWindow(NULL);
3240         }
3241     }
3242 
3243     // With right mouse button we close popups without changing focus based on where the mouse is aimed
3244     // Instead, focus will be restored to the window under the bottom-most closed popup.
3245     // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
3246     if (g.IO.MouseClicked[1])
3247     {
3248         // Find the top-most window between HoveredWindow and the top-most Modal Window.
3249         // This is where we can trim the popup stack.
3250         ImGuiWindow* modal = GetTopMostPopupModal();
3251         bool hovered_window_above_modal = false;
3252         if (modal == NULL)
3253             hovered_window_above_modal = true;
3254         for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--)
3255         {
3256             ImGuiWindow* window = g.Windows[i];
3257             if (window == modal)
3258                 break;
3259             if (window == g.HoveredWindow)
3260                 hovered_window_above_modal = true;
3261         }
3262         ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true);
3263     }
3264 }
3265 
IsWindowActiveAndVisible(ImGuiWindow * window)3266 static bool IsWindowActiveAndVisible(ImGuiWindow* window)
3267 {
3268     return (window->Active) && (!window->Hidden);
3269 }
3270 
UpdateMouseInputs()3271 static void ImGui::UpdateMouseInputs()
3272 {
3273     ImGuiContext& g = *GImGui;
3274 
3275     // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
3276     if (IsMousePosValid(&g.IO.MousePos))
3277         g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos);
3278 
3279     // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
3280     if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
3281         g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
3282     else
3283         g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
3284     if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
3285         g.NavDisableMouseHover = false;
3286 
3287     g.IO.MousePosPrev = g.IO.MousePos;
3288     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3289     {
3290         g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
3291         g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
3292         g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
3293         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;
3294         g.IO.MouseDoubleClicked[i] = false;
3295         if (g.IO.MouseClicked[i])
3296         {
3297             if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime)
3298             {
3299                 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3300                 if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
3301                     g.IO.MouseDoubleClicked[i] = true;
3302                 g.IO.MouseClickedTime[i] = -FLT_MAX;    // so the third click isn't turned into a double-click
3303             }
3304             else
3305             {
3306                 g.IO.MouseClickedTime[i] = g.Time;
3307             }
3308             g.IO.MouseClickedPos[i] = g.IO.MousePos;
3309             g.IO.MouseDownWasDoubleClick[i] = g.IO.MouseDoubleClicked[i];
3310             g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
3311             g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
3312         }
3313         else if (g.IO.MouseDown[i])
3314         {
3315             // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
3316             ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3317             g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
3318             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);
3319             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);
3320         }
3321         if (!g.IO.MouseDown[i] && !g.IO.MouseReleased[i])
3322             g.IO.MouseDownWasDoubleClick[i] = false;
3323         if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
3324             g.NavDisableMouseHover = false;
3325     }
3326 }
3327 
StartLockWheelingWindow(ImGuiWindow * window)3328 static void StartLockWheelingWindow(ImGuiWindow* window)
3329 {
3330     ImGuiContext& g = *GImGui;
3331     if (g.WheelingWindow == window)
3332         return;
3333     g.WheelingWindow = window;
3334     g.WheelingWindowRefMousePos = g.IO.MousePos;
3335     g.WheelingWindowTimer = WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER;
3336 }
3337 
UpdateMouseWheel()3338 void ImGui::UpdateMouseWheel()
3339 {
3340     ImGuiContext& g = *GImGui;
3341 
3342     // Reset the locked window if we move the mouse or after the timer elapses
3343     if (g.WheelingWindow != NULL)
3344     {
3345         g.WheelingWindowTimer -= g.IO.DeltaTime;
3346         if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
3347             g.WheelingWindowTimer = 0.0f;
3348         if (g.WheelingWindowTimer <= 0.0f)
3349         {
3350             g.WheelingWindow = NULL;
3351             g.WheelingWindowTimer = 0.0f;
3352         }
3353     }
3354 
3355     if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f)
3356         return;
3357 
3358     ImGuiWindow* window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
3359     if (!window || window->Collapsed)
3360         return;
3361 
3362     // Zoom / Scale window
3363     // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
3364     if (g.IO.MouseWheel != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
3365     {
3366         StartLockWheelingWindow(window);
3367         const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
3368         const float scale = new_font_scale / window->FontWindowScale;
3369         window->FontWindowScale = new_font_scale;
3370         if (!(window->Flags & ImGuiWindowFlags_ChildWindow))
3371         {
3372             const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
3373             SetWindowPos(window, window->Pos + offset, 0);
3374             window->Size = ImFloor(window->Size * scale);
3375             window->SizeFull = ImFloor(window->SizeFull * scale);
3376         }
3377         return;
3378     }
3379 
3380     // Mouse wheel scrolling
3381     // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent
3382 
3383     // Vertical Mouse Wheel scrolling
3384     const float wheel_y = (g.IO.MouseWheel != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f;
3385     if (wheel_y != 0.0f && !g.IO.KeyCtrl)
3386     {
3387         StartLockWheelingWindow(window);
3388         while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3389             window = window->ParentWindow;
3390         if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3391         {
3392             float max_step = window->InnerRect.GetHeight() * 0.67f;
3393             float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step));
3394             SetScrollY(window, window->Scroll.y - wheel_y * scroll_step);
3395         }
3396     }
3397 
3398     // Horizontal Mouse Wheel scrolling, or Vertical Mouse Wheel w/ Shift held
3399     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;
3400     if (wheel_x != 0.0f && !g.IO.KeyCtrl)
3401     {
3402         StartLockWheelingWindow(window);
3403         while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.x == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3404             window = window->ParentWindow;
3405         if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3406         {
3407             float max_step = window->InnerRect.GetWidth() * 0.67f;
3408             float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step));
3409             SetScrollX(window, window->Scroll.x - wheel_x * scroll_step);
3410         }
3411     }
3412 }
3413 
3414 // 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()3415 void ImGui::UpdateHoveredWindowAndCaptureFlags()
3416 {
3417     ImGuiContext& g = *GImGui;
3418 
3419     // Find the window hovered by mouse:
3420     // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3421     // - 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.
3422     // - 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.
3423     FindHoveredWindow();
3424 
3425     // Modal windows prevents cursor from hovering behind them.
3426     ImGuiWindow* modal_window = GetTopMostPopupModal();
3427     if (modal_window)
3428         if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window))
3429             g.HoveredRootWindow = g.HoveredWindow = NULL;
3430 
3431     // Disabled mouse?
3432     if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
3433         g.HoveredWindow = g.HoveredRootWindow = NULL;
3434 
3435     // 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.
3436     int mouse_earliest_button_down = -1;
3437     bool mouse_any_down = false;
3438     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3439     {
3440         if (g.IO.MouseClicked[i])
3441             g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty());
3442         mouse_any_down |= g.IO.MouseDown[i];
3443         if (g.IO.MouseDown[i])
3444             if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
3445                 mouse_earliest_button_down = i;
3446     }
3447     const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
3448 
3449     // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
3450     // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
3451     const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
3452     if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
3453         g.HoveredWindow = g.HoveredRootWindow = NULL;
3454 
3455     // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app)
3456     if (g.WantCaptureMouseNextFrame != -1)
3457         g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
3458     else
3459         g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty());
3460 
3461     // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app)
3462     if (g.WantCaptureKeyboardNextFrame != -1)
3463         g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
3464     else
3465         g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
3466     if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
3467         g.IO.WantCaptureKeyboard = true;
3468 
3469     // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
3470     g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
3471 }
3472 
NewFrameSanityChecks()3473 static void NewFrameSanityChecks()
3474 {
3475     ImGuiContext& g = *GImGui;
3476 
3477     // Check user data
3478     // (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)
3479     IM_ASSERT(g.Initialized);
3480     IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0)              && "Need a positive DeltaTime!");
3481     IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount)  && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
3482     IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f  && "Invalid DisplaySize value!");
3483     IM_ASSERT(g.IO.Fonts->Fonts.Size > 0                                && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3484     IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded()                          && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3485     IM_ASSERT(g.Style.CurveTessellationTol > 0.0f                       && "Invalid style setting!");
3486     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)!");
3487     IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting.");
3488     IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
3489     for (int n = 0; n < ImGuiKey_COUNT; n++)
3490         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)");
3491 
3492     // 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)
3493     if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
3494         IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
3495 
3496     // Perform simple check: the beta io.ConfigWindowsResizeFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
3497     if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
3498         g.IO.ConfigWindowsResizeFromEdges = false;
3499 }
3500 
NewFrame()3501 void ImGui::NewFrame()
3502 {
3503     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3504     ImGuiContext& g = *GImGui;
3505 
3506 #ifdef IMGUI_ENABLE_TEST_ENGINE
3507     ImGuiTestEngineHook_PreNewFrame(&g);
3508 #endif
3509 
3510     // Check and assert for various common IO and Configuration mistakes
3511     NewFrameSanityChecks();
3512 
3513     // Load settings on first frame (if not explicitly loaded manually before)
3514     if (!g.SettingsLoaded)
3515     {
3516         IM_ASSERT(g.SettingsWindows.empty());
3517         if (g.IO.IniFilename)
3518             LoadIniSettingsFromDisk(g.IO.IniFilename);
3519         g.SettingsLoaded = true;
3520     }
3521 
3522     // Save settings (with a delay after the last modification, so we don't spam disk too much)
3523     if (g.SettingsDirtyTimer > 0.0f)
3524     {
3525         g.SettingsDirtyTimer -= g.IO.DeltaTime;
3526         if (g.SettingsDirtyTimer <= 0.0f)
3527         {
3528             if (g.IO.IniFilename != NULL)
3529                 SaveIniSettingsToDisk(g.IO.IniFilename);
3530             else
3531                 g.IO.WantSaveIniSettings = true;  // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
3532             g.SettingsDirtyTimer = 0.0f;
3533         }
3534     }
3535 
3536     g.Time += g.IO.DeltaTime;
3537     g.FrameScopeActive = true;
3538     g.FrameCount += 1;
3539     g.TooltipOverrideCount = 0;
3540     g.WindowsActiveCount = 0;
3541 
3542     // Setup current font and draw list shared data
3543     g.IO.Fonts->Locked = true;
3544     SetCurrentFont(GetDefaultFont());
3545     IM_ASSERT(g.Font->IsLoaded());
3546     g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
3547     g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
3548     g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
3549     if (g.Style.AntiAliasedLines)
3550         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
3551     if (g.Style.AntiAliasedFill)
3552         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
3553     if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
3554         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
3555 
3556     g.BackgroundDrawList.Clear();
3557     g.BackgroundDrawList.PushTextureID(g.IO.Fonts->TexID);
3558     g.BackgroundDrawList.PushClipRectFullScreen();
3559 
3560     g.ForegroundDrawList.Clear();
3561     g.ForegroundDrawList.PushTextureID(g.IO.Fonts->TexID);
3562     g.ForegroundDrawList.PushClipRectFullScreen();
3563 
3564     // Mark rendering data as invalid to prevent user who may have a handle on it to use it.
3565     g.DrawData.Clear();
3566 
3567     // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
3568     if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
3569         KeepAliveID(g.DragDropPayload.SourceId);
3570 
3571     // Clear reference to active widget if the widget isn't alive anymore
3572     if (!g.HoveredIdPreviousFrame)
3573         g.HoveredIdTimer = 0.0f;
3574     if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
3575         g.HoveredIdNotActiveTimer = 0.0f;
3576     if (g.HoveredId)
3577         g.HoveredIdTimer += g.IO.DeltaTime;
3578     if (g.HoveredId && g.ActiveId != g.HoveredId)
3579         g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
3580     g.HoveredIdPreviousFrame = g.HoveredId;
3581     g.HoveredId = 0;
3582     g.HoveredIdAllowOverlap = false;
3583     if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
3584         ClearActiveID();
3585     if (g.ActiveId)
3586         g.ActiveIdTimer += g.IO.DeltaTime;
3587     g.LastActiveIdTimer += g.IO.DeltaTime;
3588     g.ActiveIdPreviousFrame = g.ActiveId;
3589     g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
3590     g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore;
3591     g.ActiveIdIsAlive = 0;
3592     g.ActiveIdHasBeenEditedThisFrame = false;
3593     g.ActiveIdPreviousFrameIsAlive = false;
3594     g.ActiveIdIsJustActivated = false;
3595     if (g.TempInputTextId != 0 && g.ActiveId != g.TempInputTextId)
3596         g.TempInputTextId = 0;
3597     if (g.ActiveId == 0)
3598     {
3599         g.ActiveIdUsingNavDirMask = g.ActiveIdUsingNavInputMask = 0;
3600         g.ActiveIdUsingKeyInputMask = 0;
3601     }
3602 
3603     // Drag and drop
3604     g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
3605     g.DragDropAcceptIdCurr = 0;
3606     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
3607     g.DragDropWithinSourceOrTarget = false;
3608 
3609     // Update keyboard input state
3610     memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
3611     for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
3612         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;
3613 
3614     // Update gamepad/keyboard directional navigation
3615     NavUpdate();
3616 
3617     // Update mouse input state
3618     UpdateMouseInputs();
3619 
3620     // Calculate frame-rate for the user, as a purely luxurious feature
3621     g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
3622     g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
3623     g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
3624     g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX;
3625 
3626     // Find hovered window
3627     // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
3628     UpdateHoveredWindowAndCaptureFlags();
3629 
3630     // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
3631     UpdateMouseMovingWindowNewFrame();
3632 
3633     // Background darkening/whitening
3634     if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
3635         g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
3636     else
3637         g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
3638 
3639     g.MouseCursor = ImGuiMouseCursor_Arrow;
3640     g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
3641     g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
3642 
3643     // Mouse wheel scrolling, scale
3644     UpdateMouseWheel();
3645 
3646     // Pressing TAB activate widget focus
3647     g.FocusTabPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab));
3648     if (g.ActiveId == 0 && g.FocusTabPressed)
3649     {
3650         // Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also
3651         // manipulate the Next fields even, even though they will be turned into Curr fields by the code below.
3652         g.FocusRequestNextWindow = g.NavWindow;
3653         g.FocusRequestNextCounterAll = INT_MAX;
3654         if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
3655             g.FocusRequestNextCounterTab = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1);
3656         else
3657             g.FocusRequestNextCounterTab = g.IO.KeyShift ? -1 : 0;
3658     }
3659 
3660     // Turn queued focus request into current one
3661     g.FocusRequestCurrWindow = NULL;
3662     g.FocusRequestCurrCounterAll = g.FocusRequestCurrCounterTab = INT_MAX;
3663     if (g.FocusRequestNextWindow != NULL)
3664     {
3665         ImGuiWindow* window = g.FocusRequestNextWindow;
3666         g.FocusRequestCurrWindow = window;
3667         if (g.FocusRequestNextCounterAll != INT_MAX && window->DC.FocusCounterAll != -1)
3668             g.FocusRequestCurrCounterAll = ImModPositive(g.FocusRequestNextCounterAll, window->DC.FocusCounterAll + 1);
3669         if (g.FocusRequestNextCounterTab != INT_MAX && window->DC.FocusCounterTab != -1)
3670             g.FocusRequestCurrCounterTab = ImModPositive(g.FocusRequestNextCounterTab, window->DC.FocusCounterTab + 1);
3671         g.FocusRequestNextWindow = NULL;
3672         g.FocusRequestNextCounterAll = g.FocusRequestNextCounterTab = INT_MAX;
3673     }
3674 
3675     g.NavIdTabCounter = INT_MAX;
3676 
3677     // Mark all windows as not visible and compact unused memory.
3678     IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size);
3679     const float memory_compact_start_time = (g.IO.ConfigWindowsMemoryCompactTimer >= 0.0f) ? (float)g.Time - g.IO.ConfigWindowsMemoryCompactTimer : FLT_MAX;
3680     for (int i = 0; i != g.Windows.Size; i++)
3681     {
3682         ImGuiWindow* window = g.Windows[i];
3683         window->WasActive = window->Active;
3684         window->BeginCount = 0;
3685         window->Active = false;
3686         window->WriteAccessed = false;
3687 
3688         // Garbage collect (this is totally functional but we may need decide if the side-effects are desirable)
3689         if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
3690             GcCompactTransientWindowBuffers(window);
3691     }
3692 
3693     // Closing the focused window restore focus to the first active root window in descending z-order
3694     if (g.NavWindow && !g.NavWindow->WasActive)
3695         FocusTopMostWindowUnderOne(NULL, NULL);
3696 
3697     // No window should be open at the beginning of the frame.
3698     // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
3699     g.CurrentWindowStack.resize(0);
3700     g.BeginPopupStack.resize(0);
3701     ClosePopupsOverWindow(g.NavWindow, false);
3702 
3703     // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
3704     UpdateDebugToolItemPicker();
3705 
3706     // Create implicit/fallback window - which we will only render it if the user has added something to it.
3707     // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
3708     // This fallback is particularly important as it avoid ImGui:: calls from crashing.
3709     SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver);
3710     Begin("Debug##Default");
3711     g.FrameScopePushedImplicitWindow = true;
3712 
3713 #ifdef IMGUI_ENABLE_TEST_ENGINE
3714     ImGuiTestEngineHook_PostNewFrame(&g);
3715 #endif
3716 }
3717 
3718 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
UpdateDebugToolItemPicker()3719 void ImGui::UpdateDebugToolItemPicker()
3720 {
3721     ImGuiContext& g = *GImGui;
3722     g.DebugItemPickerBreakID = 0;
3723     if (g.DebugItemPickerActive)
3724     {
3725         const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
3726         ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
3727         if (ImGui::IsKeyPressedMap(ImGuiKey_Escape))
3728             g.DebugItemPickerActive = false;
3729         if (ImGui::IsMouseClicked(0) && hovered_id)
3730         {
3731             g.DebugItemPickerBreakID = hovered_id;
3732             g.DebugItemPickerActive = false;
3733         }
3734         ImGui::SetNextWindowBgAlpha(0.60f);
3735         ImGui::BeginTooltip();
3736         ImGui::Text("HoveredId: 0x%08X", hovered_id);
3737         ImGui::Text("Press ESC to abort picking.");
3738         ImGui::TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!");
3739         ImGui::EndTooltip();
3740     }
3741 }
3742 
Initialize(ImGuiContext * context)3743 void ImGui::Initialize(ImGuiContext* context)
3744 {
3745     ImGuiContext& g = *context;
3746     IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
3747 
3748     // Add .ini handle for ImGuiWindow type
3749     ImGuiSettingsHandler ini_handler;
3750     ini_handler.TypeName = "Window";
3751     ini_handler.TypeHash = ImHashStr("Window");
3752     ini_handler.ReadOpenFn = SettingsHandlerWindow_ReadOpen;
3753     ini_handler.ReadLineFn = SettingsHandlerWindow_ReadLine;
3754     ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll;
3755     g.SettingsHandlers.push_back(ini_handler);
3756 
3757     g.Initialized = true;
3758 }
3759 
3760 // This function is merely here to free heap allocations.
Shutdown(ImGuiContext * context)3761 void ImGui::Shutdown(ImGuiContext* context)
3762 {
3763     // 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)
3764     ImGuiContext& g = *context;
3765     if (g.IO.Fonts && g.FontAtlasOwnedByContext)
3766     {
3767         g.IO.Fonts->Locked = false;
3768         IM_DELETE(g.IO.Fonts);
3769     }
3770     g.IO.Fonts = NULL;
3771 
3772     // Cleanup of other data are conditional on actually having initialized Dear ImGui.
3773     if (!g.Initialized)
3774         return;
3775 
3776     // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
3777     if (g.SettingsLoaded && g.IO.IniFilename != NULL)
3778     {
3779         ImGuiContext* backup_context = GImGui;
3780         SetCurrentContext(context);
3781         SaveIniSettingsToDisk(g.IO.IniFilename);
3782         SetCurrentContext(backup_context);
3783     }
3784 
3785     // Clear everything else
3786     for (int i = 0; i < g.Windows.Size; i++)
3787         IM_DELETE(g.Windows[i]);
3788     g.Windows.clear();
3789     g.WindowsFocusOrder.clear();
3790     g.WindowsSortBuffer.clear();
3791     g.CurrentWindow = NULL;
3792     g.CurrentWindowStack.clear();
3793     g.WindowsById.Clear();
3794     g.NavWindow = NULL;
3795     g.HoveredWindow = g.HoveredRootWindow = NULL;
3796     g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
3797     g.MovingWindow = NULL;
3798     g.ColorModifiers.clear();
3799     g.StyleModifiers.clear();
3800     g.FontStack.clear();
3801     g.OpenPopupStack.clear();
3802     g.BeginPopupStack.clear();
3803     g.DrawDataBuilder.ClearFreeMemory();
3804     g.BackgroundDrawList.ClearFreeMemory();
3805     g.ForegroundDrawList.ClearFreeMemory();
3806 
3807     g.TabBars.Clear();
3808     g.CurrentTabBarStack.clear();
3809     g.ShrinkWidthBuffer.clear();
3810 
3811     g.PrivateClipboard.clear();
3812     g.InputTextState.ClearFreeMemory();
3813 
3814     g.SettingsWindowsNames.clear();
3815     g.SettingsWindows.clear();
3816     g.SettingsHandlers.clear();
3817 
3818     if (g.LogFile && g.LogFile != stdout)
3819     {
3820         fclose(g.LogFile);
3821         g.LogFile = NULL;
3822     }
3823     g.LogBuffer.clear();
3824 
3825     g.Initialized = false;
3826 }
3827 
3828 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)3829 static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
3830 {
3831     const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
3832     const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
3833     if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
3834         return d;
3835     if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
3836         return d;
3837     return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
3838 }
3839 
AddWindowToSortBuffer(ImVector<ImGuiWindow * > * out_sorted_windows,ImGuiWindow * window)3840 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
3841 {
3842     out_sorted_windows->push_back(window);
3843     if (window->Active)
3844     {
3845         int count = window->DC.ChildWindows.Size;
3846         if (count > 1)
3847             ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
3848         for (int i = 0; i < count; i++)
3849         {
3850             ImGuiWindow* child = window->DC.ChildWindows[i];
3851             if (child->Active)
3852                 AddWindowToSortBuffer(out_sorted_windows, child);
3853         }
3854     }
3855 }
3856 
AddDrawListToDrawData(ImVector<ImDrawList * > * out_list,ImDrawList * draw_list)3857 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
3858 {
3859     if (draw_list->CmdBuffer.empty())
3860         return;
3861 
3862     // Remove trailing command if unused
3863     ImDrawCmd& last_cmd = draw_list->CmdBuffer.back();
3864     if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL)
3865     {
3866         draw_list->CmdBuffer.pop_back();
3867         if (draw_list->CmdBuffer.empty())
3868             return;
3869     }
3870 
3871     // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
3872     // May trigger for you if you are using PrimXXX functions incorrectly.
3873     IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
3874     IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
3875     if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset))
3876         IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
3877 
3878     // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
3879     // If this assert triggers because you are drawing lots of stuff manually:
3880     // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds.
3881     //   Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics window to inspect draw list contents.
3882     // - If you want large meshes with more than 64K vertices, you can either:
3883     //   (A) Handle the ImDrawCmd::VtxOffset value in your renderer back-end, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'.
3884     //       Most example back-ends already support this from 1.71. Pre-1.71 back-ends won't.
3885     //       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.
3886     //   (B) Or handle 32-bits indices in your renderer back-end, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h.
3887     //       Most example back-ends already support this. For example, the OpenGL example code detect index size at compile-time:
3888     //         glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
3889     //       Your own engine or render API may use different parameters or function calls to specify index sizes.
3890     //       2 and 4 bytes indices are generally supported by most graphics API.
3891     // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching
3892     //   the 64K limit to split your draw commands in multiple draw lists.
3893     if (sizeof(ImDrawIdx) == 2)
3894         IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
3895 
3896     out_list->push_back(draw_list);
3897 }
3898 
AddWindowToDrawData(ImVector<ImDrawList * > * out_render_list,ImGuiWindow * window)3899 static void AddWindowToDrawData(ImVector<ImDrawList*>* out_render_list, ImGuiWindow* window)
3900 {
3901     ImGuiContext& g = *GImGui;
3902     g.IO.MetricsRenderWindows++;
3903     AddDrawListToDrawData(out_render_list, window->DrawList);
3904     for (int i = 0; i < window->DC.ChildWindows.Size; i++)
3905     {
3906         ImGuiWindow* child = window->DC.ChildWindows[i];
3907         if (IsWindowActiveAndVisible(child)) // clipped children may have been marked not active
3908             AddWindowToDrawData(out_render_list, child);
3909     }
3910 }
3911 
3912 // Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)
AddRootWindowToDrawData(ImGuiWindow * window)3913 static void AddRootWindowToDrawData(ImGuiWindow* window)
3914 {
3915     ImGuiContext& g = *GImGui;
3916     if (window->Flags & ImGuiWindowFlags_Tooltip)
3917         AddWindowToDrawData(&g.DrawDataBuilder.Layers[1], window);
3918     else
3919         AddWindowToDrawData(&g.DrawDataBuilder.Layers[0], window);
3920 }
3921 
FlattenIntoSingleLayer()3922 void ImDrawDataBuilder::FlattenIntoSingleLayer()
3923 {
3924     int n = Layers[0].Size;
3925     int size = n;
3926     for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
3927         size += Layers[i].Size;
3928     Layers[0].resize(size);
3929     for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
3930     {
3931         ImVector<ImDrawList*>& layer = Layers[layer_n];
3932         if (layer.empty())
3933             continue;
3934         memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
3935         n += layer.Size;
3936         layer.resize(0);
3937     }
3938 }
3939 
SetupDrawData(ImVector<ImDrawList * > * draw_lists,ImDrawData * draw_data)3940 static void SetupDrawData(ImVector<ImDrawList*>* draw_lists, ImDrawData* draw_data)
3941 {
3942     ImGuiIO& io = ImGui::GetIO();
3943     draw_data->Valid = true;
3944     draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
3945     draw_data->CmdListsCount = draw_lists->Size;
3946     draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
3947     draw_data->DisplayPos = ImVec2(0.0f, 0.0f);
3948     draw_data->DisplaySize = io.DisplaySize;
3949     draw_data->FramebufferScale = io.DisplayFramebufferScale;
3950     for (int n = 0; n < draw_lists->Size; n++)
3951     {
3952         draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size;
3953         draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size;
3954     }
3955 }
3956 
3957 // 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)3958 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
3959 {
3960     ImGuiWindow* window = GetCurrentWindow();
3961     window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
3962     window->ClipRect = window->DrawList->_ClipRectStack.back();
3963 }
3964 
PopClipRect()3965 void ImGui::PopClipRect()
3966 {
3967     ImGuiWindow* window = GetCurrentWindow();
3968     window->DrawList->PopClipRect();
3969     window->ClipRect = window->DrawList->_ClipRectStack.back();
3970 }
3971 
3972 // 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()3973 void ImGui::EndFrame()
3974 {
3975     ImGuiContext& g = *GImGui;
3976     IM_ASSERT(g.Initialized);
3977     if (g.FrameCountEnded == g.FrameCount)          // Don't process EndFrame() multiple times.
3978         return;
3979     IM_ASSERT(g.FrameScopeActive && "Forgot to call ImGui::NewFrame()?");
3980 
3981     // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
3982     if (g.IO.ImeSetInputScreenPosFn && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f))
3983     {
3984         g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y);
3985         g.PlatformImeLastPos = g.PlatformImePos;
3986     }
3987 
3988     // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you
3989     // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API).
3990     if (g.CurrentWindowStack.Size != 1)
3991     {
3992         if (g.CurrentWindowStack.Size > 1)
3993         {
3994             IM_ASSERT(g.CurrentWindowStack.Size == 1 && "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?");
3995             while (g.CurrentWindowStack.Size > 1) // FIXME-ERRORHANDLING
3996                 End();
3997         }
3998         else
3999         {
4000             IM_ASSERT(g.CurrentWindowStack.Size == 1 && "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?");
4001         }
4002     }
4003 
4004     // Hide implicit/fallback "Debug" window if it hasn't been used
4005     g.FrameScopePushedImplicitWindow = false;
4006     if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
4007         g.CurrentWindow->Active = false;
4008     End();
4009 
4010     // Show CTRL+TAB list window
4011     if (g.NavWindowingTarget != NULL)
4012         NavUpdateWindowingOverlay();
4013 
4014     // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
4015     if (g.DragDropActive)
4016     {
4017         bool is_delivered = g.DragDropPayload.Delivery;
4018         bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton));
4019         if (is_delivered || is_elapsed)
4020             ClearDragDrop();
4021     }
4022 
4023     // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.
4024     if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount)
4025     {
4026         g.DragDropWithinSourceOrTarget = true;
4027         SetTooltip("...");
4028         g.DragDropWithinSourceOrTarget = false;
4029     }
4030 
4031     // End frame
4032     g.FrameScopeActive = false;
4033     g.FrameCountEnded = g.FrameCount;
4034 
4035     // Initiate moving window + handle left-click and right-click focus
4036     UpdateMouseMovingWindowEndFrame();
4037 
4038     // Sort the window list so that all child windows are after their parent
4039     // We cannot do that on FocusWindow() because childs may not exist yet
4040     g.WindowsSortBuffer.resize(0);
4041     g.WindowsSortBuffer.reserve(g.Windows.Size);
4042     for (int i = 0; i != g.Windows.Size; i++)
4043     {
4044         ImGuiWindow* window = g.Windows[i];
4045         if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow))       // if a child is active its parent will add it
4046             continue;
4047         AddWindowToSortBuffer(&g.WindowsSortBuffer, window);
4048     }
4049 
4050     // 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.
4051     IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size);
4052     g.Windows.swap(g.WindowsSortBuffer);
4053     g.IO.MetricsActiveWindows = g.WindowsActiveCount;
4054 
4055     // Unlock font atlas
4056     g.IO.Fonts->Locked = false;
4057 
4058     // Clear Input data for next frame
4059     g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
4060     g.IO.InputQueueCharacters.resize(0);
4061     memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
4062 }
4063 
Render()4064 void ImGui::Render()
4065 {
4066     ImGuiContext& g = *GImGui;
4067     IM_ASSERT(g.Initialized);
4068 
4069     if (g.FrameCountEnded != g.FrameCount)
4070         EndFrame();
4071     g.FrameCountRendered = g.FrameCount;
4072 
4073     // Gather ImDrawList to render (for each active window)
4074     g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsRenderWindows = 0;
4075     g.DrawDataBuilder.Clear();
4076     if (!g.BackgroundDrawList.VtxBuffer.empty())
4077         AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.BackgroundDrawList);
4078 
4079     ImGuiWindow* windows_to_render_top_most[2];
4080     windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
4081     windows_to_render_top_most[1] = g.NavWindowingTarget ? g.NavWindowingList : NULL;
4082     for (int n = 0; n != g.Windows.Size; n++)
4083     {
4084         ImGuiWindow* window = g.Windows[n];
4085         if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])
4086             AddRootWindowToDrawData(window);
4087     }
4088     for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++)
4089         if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window
4090             AddRootWindowToDrawData(windows_to_render_top_most[n]);
4091     g.DrawDataBuilder.FlattenIntoSingleLayer();
4092 
4093     // Draw software mouse cursor if requested
4094     if (g.IO.MouseDrawCursor)
4095         RenderMouseCursor(&g.ForegroundDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
4096 
4097     if (!g.ForegroundDrawList.VtxBuffer.empty())
4098         AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.ForegroundDrawList);
4099 
4100     // Setup ImDrawData structure for end-user
4101     SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData);
4102     g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount;
4103     g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount;
4104 
4105     // (Legacy) Call the Render callback function. The current prefer way is to let the user retrieve GetDrawData() and call the render function themselves.
4106 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
4107     if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)
4108         g.IO.RenderDrawListsFn(&g.DrawData);
4109 #endif
4110 }
4111 
4112 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
4113 // 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)4114 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
4115 {
4116     ImGuiContext& g = *GImGui;
4117 
4118     const char* text_display_end;
4119     if (hide_text_after_double_hash)
4120         text_display_end = FindRenderedTextEnd(text, text_end);      // Hide anything after a '##' string
4121     else
4122         text_display_end = text_end;
4123 
4124     ImFont* font = g.Font;
4125     const float font_size = g.FontSize;
4126     if (text == text_display_end)
4127         return ImVec2(0.0f, font_size);
4128     ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
4129 
4130     // Round
4131     text_size.x = IM_FLOOR(text_size.x + 0.95f);
4132 
4133     return text_size;
4134 }
4135 
4136 // Find window given position, search front-to-back
4137 // FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programatically
4138 // with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
4139 // called, aka before the next Begin(). Moving window isn't affected.
FindHoveredWindow()4140 static void FindHoveredWindow()
4141 {
4142     ImGuiContext& g = *GImGui;
4143 
4144     ImGuiWindow* hovered_window = NULL;
4145     if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
4146         hovered_window = g.MovingWindow;
4147 
4148     ImVec2 padding_regular = g.Style.TouchExtraPadding;
4149     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;
4150     for (int i = g.Windows.Size - 1; i >= 0; i--)
4151     {
4152         ImGuiWindow* window = g.Windows[i];
4153         if (!window->Active || window->Hidden)
4154             continue;
4155         if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
4156             continue;
4157 
4158         // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
4159         ImRect bb(window->OuterRectClipped);
4160         if (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize))
4161             bb.Expand(padding_regular);
4162         else
4163             bb.Expand(padding_for_resize_from_edges);
4164         if (!bb.Contains(g.IO.MousePos))
4165             continue;
4166 
4167         // Those seemingly unnecessary extra tests are because the code here is a little different in viewport/docking branches.
4168         if (hovered_window == NULL)
4169             hovered_window = window;
4170         if (hovered_window)
4171             break;
4172     }
4173 
4174     g.HoveredWindow = hovered_window;
4175     g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
4176 
4177 }
4178 
4179 // Test if mouse cursor is hovering given rectangle
4180 // NB- Rectangle is clipped by our current clip setting
4181 // 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)4182 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
4183 {
4184     ImGuiContext& g = *GImGui;
4185 
4186     // Clip
4187     ImRect rect_clipped(r_min, r_max);
4188     if (clip)
4189         rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
4190 
4191     // Expand for touch input
4192     const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
4193     if (!rect_for_touch.Contains(g.IO.MousePos))
4194         return false;
4195     return true;
4196 }
4197 
GetKeyIndex(ImGuiKey imgui_key)4198 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
4199 {
4200     IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
4201     ImGuiContext& g = *GImGui;
4202     return g.IO.KeyMap[imgui_key];
4203 }
4204 
4205 // Note that imgui doesn't know the semantic of each entry of io.KeysDown[]. Use your own indices/enums according to how your back-end/engine stored them into io.KeysDown[]!
IsKeyDown(int user_key_index)4206 bool ImGui::IsKeyDown(int user_key_index)
4207 {
4208     if (user_key_index < 0)
4209         return false;
4210     ImGuiContext& g = *GImGui;
4211     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4212     return g.IO.KeysDown[user_key_index];
4213 }
4214 
4215 // t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
4216 // t1 = current time (e.g.: g.Time)
4217 // An event is triggered at:
4218 //  t = 0.0f     t = repeat_delay,    t = repeat_delay + repeat_rate*N
CalcTypematicRepeatAmount(float t0,float t1,float repeat_delay,float repeat_rate)4219 int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)
4220 {
4221     if (t1 == 0.0f)
4222         return 1;
4223     if (t0 >= t1)
4224         return 0;
4225     if (repeat_rate <= 0.0f)
4226         return (t0 < repeat_delay) && (t1 >= repeat_delay);
4227     const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);
4228     const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);
4229     const int count = count_t1 - count_t0;
4230     return count;
4231 }
4232 
GetKeyPressedAmount(int key_index,float repeat_delay,float repeat_rate)4233 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
4234 {
4235     ImGuiContext& g = *GImGui;
4236     if (key_index < 0)
4237         return 0;
4238     IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4239     const float t = g.IO.KeysDownDuration[key_index];
4240     return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
4241 }
4242 
IsKeyPressed(int user_key_index,bool repeat)4243 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
4244 {
4245     ImGuiContext& g = *GImGui;
4246     if (user_key_index < 0)
4247         return false;
4248     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4249     const float t = g.IO.KeysDownDuration[user_key_index];
4250     if (t == 0.0f)
4251         return true;
4252     if (repeat && t > g.IO.KeyRepeatDelay)
4253         return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
4254     return false;
4255 }
4256 
IsKeyReleased(int user_key_index)4257 bool ImGui::IsKeyReleased(int user_key_index)
4258 {
4259     ImGuiContext& g = *GImGui;
4260     if (user_key_index < 0) return false;
4261     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4262     return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
4263 }
4264 
IsMouseDown(int button)4265 bool ImGui::IsMouseDown(int button)
4266 {
4267     ImGuiContext& g = *GImGui;
4268     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4269     return g.IO.MouseDown[button];
4270 }
4271 
IsAnyMouseDown()4272 bool ImGui::IsAnyMouseDown()
4273 {
4274     ImGuiContext& g = *GImGui;
4275     for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
4276         if (g.IO.MouseDown[n])
4277             return true;
4278     return false;
4279 }
4280 
IsMouseClicked(int button,bool repeat)4281 bool ImGui::IsMouseClicked(int button, bool repeat)
4282 {
4283     ImGuiContext& g = *GImGui;
4284     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4285     const float t = g.IO.MouseDownDuration[button];
4286     if (t == 0.0f)
4287         return true;
4288 
4289     if (repeat && t > g.IO.KeyRepeatDelay)
4290     {
4291         // 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.
4292         int amount = CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.50f);
4293         if (amount > 0)
4294             return true;
4295     }
4296 
4297     return false;
4298 }
4299 
IsMouseReleased(int button)4300 bool ImGui::IsMouseReleased(int button)
4301 {
4302     ImGuiContext& g = *GImGui;
4303     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4304     return g.IO.MouseReleased[button];
4305 }
4306 
IsMouseDoubleClicked(int button)4307 bool ImGui::IsMouseDoubleClicked(int button)
4308 {
4309     ImGuiContext& g = *GImGui;
4310     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4311     return g.IO.MouseDoubleClicked[button];
4312 }
4313 
4314 // [Internal] This doesn't test if the button is pressed
IsMouseDragPastThreshold(int button,float lock_threshold)4315 bool ImGui::IsMouseDragPastThreshold(int button, float lock_threshold)
4316 {
4317     ImGuiContext& g = *GImGui;
4318     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4319     if (lock_threshold < 0.0f)
4320         lock_threshold = g.IO.MouseDragThreshold;
4321     return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
4322 }
4323 
IsMouseDragging(int button,float lock_threshold)4324 bool ImGui::IsMouseDragging(int button, float lock_threshold)
4325 {
4326     ImGuiContext& g = *GImGui;
4327     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4328     if (!g.IO.MouseDown[button])
4329         return false;
4330     return IsMouseDragPastThreshold(button, lock_threshold);
4331 }
4332 
GetMousePos()4333 ImVec2 ImGui::GetMousePos()
4334 {
4335     return GImGui->IO.MousePos;
4336 }
4337 
4338 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()4339 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
4340 {
4341     ImGuiContext& g = *GImGui;
4342     if (g.BeginPopupStack.Size > 0)
4343         return g.OpenPopupStack[g.BeginPopupStack.Size-1].OpenMousePos;
4344     return g.IO.MousePos;
4345 }
4346 
4347 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
IsMousePosValid(const ImVec2 * mouse_pos)4348 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
4349 {
4350     // The assert is only to silence a false-positive in XCode Static Analysis.
4351     // 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).
4352     IM_ASSERT(GImGui != NULL);
4353     const float MOUSE_INVALID = -256000.0f;
4354     ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
4355     return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
4356 }
4357 
4358 // Return the delta from the initial clicking position while the mouse button is clicked or was just released.
4359 // This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
4360 // NB: This is only valid if IsMousePosValid(). Back-ends in theory should always keep mouse position valid when dragging even outside the client window.
GetMouseDragDelta(int button,float lock_threshold)4361 ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold)
4362 {
4363     ImGuiContext& g = *GImGui;
4364     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4365     if (lock_threshold < 0.0f)
4366         lock_threshold = g.IO.MouseDragThreshold;
4367     if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])
4368         if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
4369             if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))
4370                 return g.IO.MousePos - g.IO.MouseClickedPos[button];
4371     return ImVec2(0.0f, 0.0f);
4372 }
4373 
ResetMouseDragDelta(int button)4374 void ImGui::ResetMouseDragDelta(int button)
4375 {
4376     ImGuiContext& g = *GImGui;
4377     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4378     // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
4379     g.IO.MouseClickedPos[button] = g.IO.MousePos;
4380 }
4381 
GetMouseCursor()4382 ImGuiMouseCursor ImGui::GetMouseCursor()
4383 {
4384     return GImGui->MouseCursor;
4385 }
4386 
SetMouseCursor(ImGuiMouseCursor cursor_type)4387 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
4388 {
4389     GImGui->MouseCursor = cursor_type;
4390 }
4391 
CaptureKeyboardFromApp(bool capture)4392 void ImGui::CaptureKeyboardFromApp(bool capture)
4393 {
4394     GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
4395 }
4396 
CaptureMouseFromApp(bool capture)4397 void ImGui::CaptureMouseFromApp(bool capture)
4398 {
4399     GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
4400 }
4401 
IsItemActive()4402 bool ImGui::IsItemActive()
4403 {
4404     ImGuiContext& g = *GImGui;
4405     if (g.ActiveId)
4406     {
4407         ImGuiWindow* window = g.CurrentWindow;
4408         return g.ActiveId == window->DC.LastItemId;
4409     }
4410     return false;
4411 }
4412 
IsItemActivated()4413 bool ImGui::IsItemActivated()
4414 {
4415     ImGuiContext& g = *GImGui;
4416     if (g.ActiveId)
4417     {
4418         ImGuiWindow* window = g.CurrentWindow;
4419         if (g.ActiveId == window->DC.LastItemId && g.ActiveIdPreviousFrame != window->DC.LastItemId)
4420             return true;
4421     }
4422     return false;
4423 }
4424 
IsItemDeactivated()4425 bool ImGui::IsItemDeactivated()
4426 {
4427     ImGuiContext& g = *GImGui;
4428     ImGuiWindow* window = g.CurrentWindow;
4429     if (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDeactivated)
4430         return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Deactivated) != 0;
4431     return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId);
4432 }
4433 
IsItemDeactivatedAfterEdit()4434 bool ImGui::IsItemDeactivatedAfterEdit()
4435 {
4436     ImGuiContext& g = *GImGui;
4437     return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore));
4438 }
4439 
IsItemFocused()4440 bool ImGui::IsItemFocused()
4441 {
4442     ImGuiContext& g = *GImGui;
4443     ImGuiWindow* window = g.CurrentWindow;
4444 
4445     if (g.NavId == 0 || g.NavDisableHighlight || g.NavId != window->DC.LastItemId)
4446         return false;
4447     return true;
4448 }
4449 
IsItemClicked(int mouse_button)4450 bool ImGui::IsItemClicked(int mouse_button)
4451 {
4452     return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
4453 }
4454 
IsItemToggledSelection()4455 bool ImGui::IsItemToggledSelection()
4456 {
4457     ImGuiContext& g = *GImGui;
4458     return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;
4459 }
4460 
IsAnyItemHovered()4461 bool ImGui::IsAnyItemHovered()
4462 {
4463     ImGuiContext& g = *GImGui;
4464     return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
4465 }
4466 
IsAnyItemActive()4467 bool ImGui::IsAnyItemActive()
4468 {
4469     ImGuiContext& g = *GImGui;
4470     return g.ActiveId != 0;
4471 }
4472 
IsAnyItemFocused()4473 bool ImGui::IsAnyItemFocused()
4474 {
4475     ImGuiContext& g = *GImGui;
4476     return g.NavId != 0 && !g.NavDisableHighlight;
4477 }
4478 
IsItemVisible()4479 bool ImGui::IsItemVisible()
4480 {
4481     ImGuiWindow* window = GetCurrentWindowRead();
4482     return window->ClipRect.Overlaps(window->DC.LastItemRect);
4483 }
4484 
IsItemEdited()4485 bool ImGui::IsItemEdited()
4486 {
4487     ImGuiWindow* window = GetCurrentWindowRead();
4488     return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0;
4489 }
4490 
4491 // 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()4492 void ImGui::SetItemAllowOverlap()
4493 {
4494     ImGuiContext& g = *GImGui;
4495     if (g.HoveredId == g.CurrentWindow->DC.LastItemId)
4496         g.HoveredIdAllowOverlap = true;
4497     if (g.ActiveId == g.CurrentWindow->DC.LastItemId)
4498         g.ActiveIdAllowOverlap = true;
4499 }
4500 
GetItemRectMin()4501 ImVec2 ImGui::GetItemRectMin()
4502 {
4503     ImGuiWindow* window = GetCurrentWindowRead();
4504     return window->DC.LastItemRect.Min;
4505 }
4506 
GetItemRectMax()4507 ImVec2 ImGui::GetItemRectMax()
4508 {
4509     ImGuiWindow* window = GetCurrentWindowRead();
4510     return window->DC.LastItemRect.Max;
4511 }
4512 
GetItemRectSize()4513 ImVec2 ImGui::GetItemRectSize()
4514 {
4515     ImGuiWindow* window = GetCurrentWindowRead();
4516     return window->DC.LastItemRect.GetSize();
4517 }
4518 
GetViewportRect()4519 static ImRect GetViewportRect()
4520 {
4521     ImGuiContext& g = *GImGui;
4522     return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
4523 }
4524 
BeginChildEx(const char * name,ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags flags)4525 static bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags)
4526 {
4527     ImGuiContext& g = *GImGui;
4528     ImGuiWindow* parent_window = g.CurrentWindow;
4529 
4530     flags |= ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
4531     flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove);  // Inherit the NoMove flag
4532 
4533     // Size
4534     const ImVec2 content_avail = GetContentRegionAvail();
4535     ImVec2 size = ImFloor(size_arg);
4536     const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
4537     if (size.x <= 0.0f)
4538         size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
4539     if (size.y <= 0.0f)
4540         size.y = ImMax(content_avail.y + size.y, 4.0f);
4541     SetNextWindowSize(size);
4542 
4543     // 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.
4544     char title[256];
4545     if (name)
4546         ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s_%08X", parent_window->Name, name, id);
4547     else
4548         ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id);
4549 
4550     const float backup_border_size = g.Style.ChildBorderSize;
4551     if (!border)
4552         g.Style.ChildBorderSize = 0.0f;
4553     bool ret = Begin(title, NULL, flags);
4554     g.Style.ChildBorderSize = backup_border_size;
4555 
4556     ImGuiWindow* child_window = g.CurrentWindow;
4557     child_window->ChildId = id;
4558     child_window->AutoFitChildAxises = (ImS8)auto_fit_axises;
4559 
4560     // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
4561     // While this is not really documented/defined, it seems that the expected thing to do.
4562     if (child_window->BeginCount == 1)
4563         parent_window->DC.CursorPos = child_window->Pos;
4564 
4565     // Process navigation-in immediately so NavInit can run on first frame
4566     if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll))
4567     {
4568         FocusWindow(child_window);
4569         NavInitWindow(child_window, false);
4570         SetActiveID(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item
4571         g.ActiveIdSource = ImGuiInputSource_Nav;
4572     }
4573     return ret;
4574 }
4575 
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4576 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4577 {
4578     ImGuiWindow* window = GetCurrentWindow();
4579     return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
4580 }
4581 
BeginChild(ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4582 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4583 {
4584     IM_ASSERT(id != 0);
4585     return BeginChildEx(NULL, id, size_arg, border, extra_flags);
4586 }
4587 
EndChild()4588 void ImGui::EndChild()
4589 {
4590     ImGuiContext& g = *GImGui;
4591     ImGuiWindow* window = g.CurrentWindow;
4592 
4593     IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow);   // Mismatched BeginChild()/EndChild() callss
4594     if (window->BeginCount > 1)
4595     {
4596         End();
4597     }
4598     else
4599     {
4600         ImVec2 sz = window->Size;
4601         if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
4602             sz.x = ImMax(4.0f, sz.x);
4603         if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
4604             sz.y = ImMax(4.0f, sz.y);
4605         End();
4606 
4607         ImGuiWindow* parent_window = g.CurrentWindow;
4608         ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
4609         ItemSize(sz);
4610         if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
4611         {
4612             ItemAdd(bb, window->ChildId);
4613             RenderNavHighlight(bb, window->ChildId);
4614 
4615             // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
4616             if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow)
4617                 RenderNavHighlight(ImRect(bb.Min - ImVec2(2,2), bb.Max + ImVec2(2,2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
4618         }
4619         else
4620         {
4621             // Not navigable into
4622             ItemAdd(bb, 0);
4623         }
4624     }
4625 }
4626 
4627 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)4628 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
4629 {
4630     ImGuiContext& g = *GImGui;
4631     const ImGuiStyle& style = g.Style;
4632     PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
4633     PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
4634     PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
4635     PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
4636     bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
4637     PopStyleVar(3);
4638     PopStyleColor();
4639     return ret;
4640 }
4641 
EndChildFrame()4642 void ImGui::EndChildFrame()
4643 {
4644     EndChild();
4645 }
4646 
4647 // Save and compare stack sizes on Begin()/End() to detect usage errors
CheckStacksSize(ImGuiWindow * window,bool write)4648 static void CheckStacksSize(ImGuiWindow* window, bool write)
4649 {
4650     // 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)
4651     ImGuiContext& g = *GImGui;
4652     short* p_backup = &window->DC.StackSizesBackup[0];
4653     { int current = window->IDStack.Size;       if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup == current && "PushID/PopID or TreeNode/TreePop Mismatch!");   p_backup++; }    // Too few or too many PopID()/TreePop()
4654     { int current = window->DC.GroupStack.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup == current && "BeginGroup/EndGroup Mismatch!");                p_backup++; }    // Too few or too many EndGroup()
4655     { int current = g.BeginPopupStack.Size;     if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup == current && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch"); p_backup++;}// Too few or too many EndMenu()/EndPopup()
4656     // 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.
4657     { int current = g.ColorModifiers.Size;      if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup >= current && "PushStyleColor/PopStyleColor Mismatch!");       p_backup++; }    // Too few or too many PopStyleColor()
4658     { int current = g.StyleModifiers.Size;      if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup >= current && "PushStyleVar/PopStyleVar Mismatch!");           p_backup++; }    // Too few or too many PopStyleVar()
4659     { int current = g.FontStack.Size;           if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup >= current && "PushFont/PopFont Mismatch!");                   p_backup++; }    // Too few or too many PopFont()
4660     IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
4661 }
4662 
SetWindowConditionAllowFlags(ImGuiWindow * window,ImGuiCond flags,bool enabled)4663 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
4664 {
4665     window->SetWindowPosAllowFlags       = enabled ? (window->SetWindowPosAllowFlags       | flags) : (window->SetWindowPosAllowFlags       & ~flags);
4666     window->SetWindowSizeAllowFlags      = enabled ? (window->SetWindowSizeAllowFlags      | flags) : (window->SetWindowSizeAllowFlags      & ~flags);
4667     window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
4668 }
4669 
FindWindowByID(ImGuiID id)4670 ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
4671 {
4672     ImGuiContext& g = *GImGui;
4673     return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
4674 }
4675 
FindWindowByName(const char * name)4676 ImGuiWindow* ImGui::FindWindowByName(const char* name)
4677 {
4678     ImGuiID id = ImHashStr(name);
4679     return FindWindowByID(id);
4680 }
4681 
CreateNewWindow(const char * name,ImVec2 size,ImGuiWindowFlags flags)4682 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
4683 {
4684     ImGuiContext& g = *GImGui;
4685     //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags);
4686 
4687     // Create window the first time
4688     ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
4689     window->Flags = flags;
4690     g.WindowsById.SetVoidPtr(window->ID, window);
4691 
4692     // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
4693     window->Pos = ImVec2(60, 60);
4694 
4695     // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
4696     if (!(flags & ImGuiWindowFlags_NoSavedSettings))
4697         if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
4698         {
4699             // Retrieve settings from .ini file
4700             window->SettingsIdx = g.SettingsWindows.index_from_ptr(settings);
4701             SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
4702             window->Pos = ImVec2(settings->Pos.x, settings->Pos.y);
4703             window->Collapsed = settings->Collapsed;
4704             if (settings->Size.x > 0 && settings->Size.y > 0)
4705                 size = ImVec2(settings->Size.x, settings->Size.y);
4706         }
4707     window->Size = window->SizeFull = ImFloor(size);
4708     window->DC.CursorStartPos = window->DC.CursorMaxPos = window->Pos; // So first call to CalcContentSize() doesn't return crazy values
4709 
4710     if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
4711     {
4712         window->AutoFitFramesX = window->AutoFitFramesY = 2;
4713         window->AutoFitOnlyGrows = false;
4714     }
4715     else
4716     {
4717         if (window->Size.x <= 0.0f)
4718             window->AutoFitFramesX = 2;
4719         if (window->Size.y <= 0.0f)
4720             window->AutoFitFramesY = 2;
4721         window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
4722     }
4723 
4724     g.WindowsFocusOrder.push_back(window);
4725     if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
4726         g.Windows.push_front(window); // Quite slow but rare and only once
4727     else
4728         g.Windows.push_back(window);
4729     return window;
4730 }
4731 
CalcWindowSizeAfterConstraint(ImGuiWindow * window,ImVec2 new_size)4732 static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size)
4733 {
4734     ImGuiContext& g = *GImGui;
4735     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)
4736     {
4737         // Using -1,-1 on either X/Y axis to preserve the current size.
4738         ImRect cr = g.NextWindowData.SizeConstraintRect;
4739         new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
4740         new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
4741         if (g.NextWindowData.SizeCallback)
4742         {
4743             ImGuiSizeCallbackData data;
4744             data.UserData = g.NextWindowData.SizeCallbackUserData;
4745             data.Pos = window->Pos;
4746             data.CurrentSize = window->SizeFull;
4747             data.DesiredSize = new_size;
4748             g.NextWindowData.SizeCallback(&data);
4749             new_size = data.DesiredSize;
4750         }
4751         new_size.x = IM_FLOOR(new_size.x);
4752         new_size.y = IM_FLOOR(new_size.y);
4753     }
4754 
4755     // Minimum size
4756     if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
4757     {
4758         new_size = ImMax(new_size, g.Style.WindowMinSize);
4759         new_size.y = ImMax(new_size.y, window->TitleBarHeight() + window->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows
4760     }
4761     return new_size;
4762 }
4763 
CalcWindowContentSize(ImGuiWindow * window)4764 static ImVec2 CalcWindowContentSize(ImGuiWindow* window)
4765 {
4766     if (window->Collapsed)
4767         if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
4768             return window->ContentSize;
4769     if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0)
4770         return window->ContentSize;
4771 
4772     ImVec2 sz;
4773     sz.x = IM_FLOOR((window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);
4774     sz.y = IM_FLOOR((window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);
4775     return sz;
4776 }
4777 
CalcWindowAutoFitSize(ImGuiWindow * window,const ImVec2 & size_contents)4778 static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents)
4779 {
4780     ImGuiContext& g = *GImGui;
4781     ImGuiStyle& style = g.Style;
4782     ImVec2 size_decorations = ImVec2(0.0f, window->TitleBarHeight() + window->MenuBarHeight());
4783     ImVec2 size_pad = window->WindowPadding * 2.0f;
4784     ImVec2 size_desired = size_contents + size_pad + size_decorations;
4785     if (window->Flags & ImGuiWindowFlags_Tooltip)
4786     {
4787         // Tooltip always resize
4788         return size_desired;
4789     }
4790     else
4791     {
4792         // Maximum window size is determined by the viewport size or monitor size
4793         const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0;
4794         const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0;
4795         ImVec2 size_min = style.WindowMinSize;
4796         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)
4797             size_min = ImMin(size_min, ImVec2(4.0f, 4.0f));
4798         ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, g.IO.DisplaySize - style.DisplaySafeAreaPadding * 2.0f));
4799 
4800         // When the window cannot fit all contents (either because of constraints, either because screen is too small),
4801         // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
4802         ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);
4803         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);
4804         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);
4805         if (will_have_scrollbar_x)
4806             size_auto_fit.y += style.ScrollbarSize;
4807         if (will_have_scrollbar_y)
4808             size_auto_fit.x += style.ScrollbarSize;
4809         return size_auto_fit;
4810     }
4811 }
4812 
CalcWindowExpectedSize(ImGuiWindow * window)4813 ImVec2 ImGui::CalcWindowExpectedSize(ImGuiWindow* window)
4814 {
4815     ImVec2 size_contents = CalcWindowContentSize(window);
4816     ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents);
4817     ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit);
4818     return size_final;
4819 }
4820 
GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)4821 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
4822 {
4823     if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
4824         return ImGuiCol_PopupBg;
4825     if (flags & ImGuiWindowFlags_ChildWindow)
4826         return ImGuiCol_ChildBg;
4827     return ImGuiCol_WindowBg;
4828 }
4829 
CalcResizePosSizeFromAnyCorner(ImGuiWindow * window,const ImVec2 & corner_target,const ImVec2 & corner_norm,ImVec2 * out_pos,ImVec2 * out_size)4830 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
4831 {
4832     ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm);                // Expected window upper-left
4833     ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
4834     ImVec2 size_expected = pos_max - pos_min;
4835     ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected);
4836     *out_pos = pos_min;
4837     if (corner_norm.x == 0.0f)
4838         out_pos->x -= (size_constrained.x - size_expected.x);
4839     if (corner_norm.y == 0.0f)
4840         out_pos->y -= (size_constrained.y - size_expected.y);
4841     *out_size = size_constrained;
4842 }
4843 
4844 struct ImGuiResizeGripDef
4845 {
4846     ImVec2  CornerPosN;
4847     ImVec2  InnerDir;
4848     int     AngleMin12, AngleMax12;
4849 };
4850 
4851 static const ImGuiResizeGripDef resize_grip_def[4] =
4852 {
4853     { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower right
4854     { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower left
4855     { ImVec2(0,0), ImVec2(+1,+1), 6, 9 }, // Upper left
4856     { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper right
4857 };
4858 
GetResizeBorderRect(ImGuiWindow * window,int border_n,float perp_padding,float thickness)4859 static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
4860 {
4861     ImRect rect = window->Rect();
4862     if (thickness == 0.0f) rect.Max -= ImVec2(1,1);
4863     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
4864     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
4865     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
4866     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
4867     IM_ASSERT(0);
4868     return ImRect();
4869 }
4870 
4871 // Handle resize for: Resize Grips, Borders, Gamepad
4872 // 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])4873 static bool ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4])
4874 {
4875     ImGuiContext& g = *GImGui;
4876     ImGuiWindowFlags flags = window->Flags;
4877 
4878     if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
4879         return false;
4880     if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window.
4881         return false;
4882 
4883     bool ret_auto_fit = false;
4884     const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0;
4885     const float grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
4886     const float grip_hover_inner_size = IM_FLOOR(grip_draw_size * 0.75f);
4887     const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS : 0.0f;
4888 
4889     ImVec2 pos_target(FLT_MAX, FLT_MAX);
4890     ImVec2 size_target(FLT_MAX, FLT_MAX);
4891 
4892     // Resize grips and borders are on layer 1
4893     window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
4894     window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu);
4895 
4896     // Manual resize grips
4897     PushID("#RESIZE");
4898     for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
4899     {
4900         const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
4901         const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
4902 
4903         // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
4904         ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size);
4905         if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
4906         if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
4907         bool hovered, held;
4908         ButtonBehavior(resize_rect, window->GetID((void*)(intptr_t)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
4909         //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
4910         if (hovered || held)
4911             g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
4912 
4913         if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
4914         {
4915             // Manual auto-fit when double-clicking
4916             size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit);
4917             ret_auto_fit = true;
4918             ClearActiveID();
4919         }
4920         else if (held)
4921         {
4922             // Resize from any of the four corners
4923             // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
4924             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
4925             CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPosN, &pos_target, &size_target);
4926         }
4927         if (resize_grip_n == 0 || held || hovered)
4928             resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
4929     }
4930     for (int border_n = 0; border_n < resize_border_count; border_n++)
4931     {
4932         bool hovered, held;
4933         ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS);
4934         ButtonBehavior(border_rect, window->GetID((void*)(intptr_t)(border_n + 4)), &hovered, &held, ImGuiButtonFlags_FlattenChildren);
4935         //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
4936         if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held)
4937         {
4938             g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
4939             if (held)
4940                 *border_held = border_n;
4941         }
4942         if (held)
4943         {
4944             ImVec2 border_target = window->Pos;
4945             ImVec2 border_posn;
4946             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
4947             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
4948             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
4949             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
4950             CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target);
4951         }
4952     }
4953     PopID();
4954 
4955     // Navigation resize (keyboard/gamepad)
4956     if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
4957     {
4958         ImVec2 nav_resize_delta;
4959         if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift)
4960             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
4961         if (g.NavInputSource == ImGuiInputSource_NavGamepad)
4962             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down);
4963         if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f)
4964         {
4965             const float NAV_RESIZE_SPEED = 600.0f;
4966             nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
4967             g.NavWindowingToggleLayer = false;
4968             g.NavDisableMouseHover = true;
4969             resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
4970             // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
4971             size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + nav_resize_delta);
4972         }
4973     }
4974 
4975     // Apply back modified position/size to window
4976     if (size_target.x != FLT_MAX)
4977     {
4978         window->SizeFull = size_target;
4979         MarkIniSettingsDirty(window);
4980     }
4981     if (pos_target.x != FLT_MAX)
4982     {
4983         window->Pos = ImFloor(pos_target);
4984         MarkIniSettingsDirty(window);
4985     }
4986 
4987     // Resize nav layer
4988     window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
4989     window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
4990 
4991     window->Size = window->SizeFull;
4992     return ret_auto_fit;
4993 }
4994 
ClampWindowRect(ImGuiWindow * window,const ImRect & rect,const ImVec2 & padding)4995 static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& rect, const ImVec2& padding)
4996 {
4997     ImGuiContext& g = *GImGui;
4998     ImVec2 size_for_clamping = (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) ? ImVec2(window->Size.x, window->TitleBarHeight()) : window->Size;
4999     window->Pos = ImMin(rect.Max - padding, ImMax(window->Pos + size_for_clamping, rect.Min + padding) - size_for_clamping);
5000 }
5001 
RenderWindowOuterBorders(ImGuiWindow * window)5002 static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
5003 {
5004     ImGuiContext& g = *GImGui;
5005     float rounding = window->WindowRounding;
5006     float border_size = window->WindowBorderSize;
5007     if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground))
5008         window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
5009 
5010     int border_held = window->ResizeBorderHeld;
5011     if (border_held != -1)
5012     {
5013         struct ImGuiResizeBorderDef
5014         {
5015             ImVec2 InnerDir;
5016             ImVec2 CornerPosN1, CornerPosN2;
5017             float  OuterAngle;
5018         };
5019         static const ImGuiResizeBorderDef resize_border_def[4] =
5020         {
5021             { ImVec2(0,+1), ImVec2(0,0), ImVec2(1,0), IM_PI*1.50f }, // Top
5022             { ImVec2(-1,0), ImVec2(1,0), ImVec2(1,1), IM_PI*0.00f }, // Right
5023             { ImVec2(0,-1), ImVec2(1,1), ImVec2(0,1), IM_PI*0.50f }, // Bottom
5024             { ImVec2(+1,0), ImVec2(0,1), ImVec2(0,0), IM_PI*1.00f }  // Left
5025         };
5026         const ImGuiResizeBorderDef& def = resize_border_def[border_held];
5027         ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f);
5028         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);
5029         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);
5030         window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), false, ImMax(2.0f, border_size)); // Thicker than usual
5031     }
5032     if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
5033     {
5034         float y = window->Pos.y + window->TitleBarHeight() - 1;
5035         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);
5036     }
5037 }
5038 
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)5039 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)
5040 {
5041     ImGuiContext& g = *GImGui;
5042     ImGuiStyle& style = g.Style;
5043     ImGuiWindowFlags flags = window->Flags;
5044 
5045     // Draw window + handle manual resize
5046     // 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.
5047     const float window_rounding = window->WindowRounding;
5048     const float window_border_size = window->WindowBorderSize;
5049     if (window->Collapsed)
5050     {
5051         // Title bar only
5052         float backup_border_size = style.FrameBorderSize;
5053         g.Style.FrameBorderSize = window->WindowBorderSize;
5054         ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
5055         RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
5056         g.Style.FrameBorderSize = backup_border_size;
5057     }
5058     else
5059     {
5060         // Window background
5061         if (!(flags & ImGuiWindowFlags_NoBackground))
5062         {
5063             ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
5064             float alpha = 1.0f;
5065             if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha)
5066                 alpha = g.NextWindowData.BgAlphaVal;
5067             if (alpha != 1.0f)
5068                 bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
5069             window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot);
5070         }
5071 
5072         // Title bar
5073         if (!(flags & ImGuiWindowFlags_NoTitleBar))
5074         {
5075             ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
5076             window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top);
5077         }
5078 
5079         // Menu bar
5080         if (flags & ImGuiWindowFlags_MenuBar)
5081         {
5082             ImRect menu_bar_rect = window->MenuBarRect();
5083             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.
5084             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);
5085             if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
5086                 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
5087         }
5088 
5089         // Scrollbars
5090         if (window->ScrollbarX)
5091             Scrollbar(ImGuiAxis_X);
5092         if (window->ScrollbarY)
5093             Scrollbar(ImGuiAxis_Y);
5094 
5095         // Render resize grips (after their input handling so we don't have a frame of latency)
5096         if (!(flags & ImGuiWindowFlags_NoResize))
5097         {
5098             for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5099             {
5100                 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5101                 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
5102                 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)));
5103                 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)));
5104                 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);
5105                 window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]);
5106             }
5107         }
5108 
5109         // Borders
5110         RenderWindowOuterBorders(window);
5111     }
5112 }
5113 
5114 // Render title text, collapse button, close button
RenderWindowTitleBarContents(ImGuiWindow * window,const ImRect & title_bar_rect,const char * name,bool * p_open)5115 void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)
5116 {
5117     ImGuiContext& g = *GImGui;
5118     ImGuiStyle& style = g.Style;
5119     ImGuiWindowFlags flags = window->Flags;
5120 
5121     const bool has_close_button = (p_open != NULL);
5122     const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None);
5123 
5124     // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)
5125     const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
5126     window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
5127     window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5128     window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu);
5129 
5130     // Layout buttons
5131     // FIXME: Would be nice to generalize the subtleties expressed here into reusable code.
5132     float pad_l = style.FramePadding.x;
5133     float pad_r = style.FramePadding.x;
5134     float button_sz = g.FontSize;
5135     ImVec2 close_button_pos;
5136     ImVec2 collapse_button_pos;
5137     if (has_close_button)
5138     {
5139         pad_r += button_sz;
5140         close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5141     }
5142     if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)
5143     {
5144         pad_r += button_sz;
5145         collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5146     }
5147     if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)
5148     {
5149         collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l - style.FramePadding.x, title_bar_rect.Min.y);
5150         pad_l += button_sz;
5151     }
5152 
5153     // Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
5154     if (has_collapse_button)
5155         if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos))
5156             window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function
5157 
5158     // Close button
5159     if (has_close_button)
5160         if (CloseButton(window->GetID("#CLOSE"), close_button_pos))
5161             *p_open = false;
5162 
5163     window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5164     window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
5165     window->DC.ItemFlags = item_flags_backup;
5166 
5167     // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
5168     // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
5169     const char* UNSAVED_DOCUMENT_MARKER = "*";
5170     const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f;
5171     const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
5172 
5173     // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
5174     // while uncentered title text will still reach edges correct.
5175     if (pad_l > style.FramePadding.x)
5176         pad_l += g.Style.ItemInnerSpacing.x;
5177     if (pad_r > style.FramePadding.x)
5178         pad_r += g.Style.ItemInnerSpacing.x;
5179     if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f)
5180     {
5181         float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center
5182         float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x);
5183         pad_l = ImMax(pad_l, pad_extend * centerness);
5184         pad_r = ImMax(pad_r, pad_extend * centerness);
5185     }
5186 
5187     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);
5188     ImRect clip_r(layout_r.Min.x, layout_r.Min.y, layout_r.Max.x + g.Style.ItemInnerSpacing.x, layout_r.Max.y);
5189     //if (g.IO.KeyCtrl) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
5190     RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r);
5191     if (flags & ImGuiWindowFlags_UnsavedDocument)
5192     {
5193         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);
5194         ImVec2 off = ImVec2(0.0f, IM_FLOOR(-g.FontSize * 0.25f));
5195         RenderTextClipped(marker_pos + off, layout_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_r);
5196     }
5197 }
5198 
UpdateWindowParentAndRootLinks(ImGuiWindow * window,ImGuiWindowFlags flags,ImGuiWindow * parent_window)5199 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
5200 {
5201     window->ParentWindow = parent_window;
5202     window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
5203     if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
5204         window->RootWindow = parent_window->RootWindow;
5205     if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
5206         window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
5207     while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
5208     {
5209         IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);
5210         window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
5211     }
5212 }
5213 
5214 // Push a new Dear ImGui window to add widgets to.
5215 // - 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.
5216 // - Begin/End can be called multiple times during the frame with the same window name to append content.
5217 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
5218 //   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.
5219 // - 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.
5220 // - 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)5221 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
5222 {
5223     ImGuiContext& g = *GImGui;
5224     const ImGuiStyle& style = g.Style;
5225     IM_ASSERT(name != NULL && name[0] != '\0');     // Window name required
5226     IM_ASSERT(g.FrameScopeActive);                  // Forgot to call ImGui::NewFrame()
5227     IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
5228 
5229     // Find or create
5230     ImGuiWindow* window = FindWindowByName(name);
5231     const bool window_just_created = (window == NULL);
5232     if (window_just_created)
5233     {
5234         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.
5235         window = CreateNewWindow(name, size_on_first_use, flags);
5236     }
5237 
5238     // Automatically disable manual moving/resizing when NoInputs is set
5239     if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
5240         flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
5241 
5242     if (flags & ImGuiWindowFlags_NavFlattened)
5243         IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
5244 
5245     const int current_frame = g.FrameCount;
5246     const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
5247 
5248     // Update the Appearing flag
5249     bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1);   // Not using !WasActive because the implicit "Debug" window would always toggle off->on
5250     const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);
5251     if (flags & ImGuiWindowFlags_Popup)
5252     {
5253         ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5254         window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
5255         window_just_activated_by_user |= (window != popup_ref.Window);
5256     }
5257     window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
5258     if (window->Appearing)
5259         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
5260 
5261     // Update Flags, LastFrameActive, BeginOrderXXX fields
5262     if (first_begin_of_the_frame)
5263     {
5264         window->Flags = (ImGuiWindowFlags)flags;
5265         window->LastFrameActive = current_frame;
5266         window->LastTimeActive = (float)g.Time;
5267         window->BeginOrderWithinParent = 0;
5268         window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
5269     }
5270     else
5271     {
5272         flags = window->Flags;
5273     }
5274 
5275     // 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
5276     ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
5277     ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
5278     IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
5279 
5280     // We allow window memory to be compacted so recreate the base stack when needed.
5281     if (window->IDStack.Size == 0)
5282         window->IDStack.push_back(window->ID);
5283 
5284     // Add to stack
5285     // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
5286     g.CurrentWindowStack.push_back(window);
5287     g.CurrentWindow = NULL;
5288     CheckStacksSize(window, true);
5289     if (flags & ImGuiWindowFlags_Popup)
5290     {
5291         ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5292         popup_ref.Window = window;
5293         g.BeginPopupStack.push_back(popup_ref);
5294         window->PopupId = popup_ref.PopupId;
5295     }
5296 
5297     if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow))
5298         window->NavLastIds[0] = 0;
5299 
5300     // Process SetNextWindow***() calls
5301     bool window_pos_set_by_api = false;
5302     bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
5303     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos)
5304     {
5305         window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
5306         if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
5307         {
5308             // May be processed on the next frame if this is our first frame and we are measuring size
5309             // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
5310             window->SetWindowPosVal = g.NextWindowData.PosVal;
5311             window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
5312             window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5313         }
5314         else
5315         {
5316             SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
5317         }
5318     }
5319     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)
5320     {
5321         window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
5322         window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
5323         SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
5324     }
5325     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize)
5326         window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;
5327     else if (first_begin_of_the_frame)
5328         window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);
5329     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed)
5330         SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
5331     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus)
5332         FocusWindow(window);
5333     if (window->Appearing)
5334         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
5335 
5336     // When reusing window again multiple times a frame, just append content (don't need to setup again)
5337     if (first_begin_of_the_frame)
5338     {
5339         // Initialize
5340         const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
5341         UpdateWindowParentAndRootLinks(window, flags, parent_window);
5342 
5343         window->Active = true;
5344         window->HasCloseButton = (p_open != NULL);
5345         window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX);
5346         window->IDStack.resize(1);
5347 
5348         // Restore buffer capacity when woken from a compacted state, to avoid
5349         if (window->MemoryCompacted)
5350             GcAwakeTransientWindowBuffers(window);
5351 
5352         // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
5353         // 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.
5354         bool window_title_visible_elsewhere = false;
5355         if (g.NavWindowingList != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0)   // Window titles visible when using CTRL+TAB
5356             window_title_visible_elsewhere = true;
5357         if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
5358         {
5359             size_t buf_len = (size_t)window->NameBufLen;
5360             window->Name = ImStrdupcpy(window->Name, &buf_len, name);
5361             window->NameBufLen = (int)buf_len;
5362         }
5363 
5364         // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
5365 
5366         // Update contents size from last frame for auto-fitting (or use explicit size)
5367         window->ContentSize = CalcWindowContentSize(window);
5368         if (window->HiddenFramesCanSkipItems > 0)
5369             window->HiddenFramesCanSkipItems--;
5370         if (window->HiddenFramesCannotSkipItems > 0)
5371             window->HiddenFramesCannotSkipItems--;
5372 
5373         // Hide new windows for one frame until they calculate their size
5374         if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
5375             window->HiddenFramesCannotSkipItems = 1;
5376 
5377         // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
5378         // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
5379         if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
5380         {
5381             window->HiddenFramesCannotSkipItems = 1;
5382             if (flags & ImGuiWindowFlags_AlwaysAutoResize)
5383             {
5384                 if (!window_size_x_set_by_api)
5385                     window->Size.x = window->SizeFull.x = 0.f;
5386                 if (!window_size_y_set_by_api)
5387                     window->Size.y = window->SizeFull.y = 0.f;
5388                 window->ContentSize = ImVec2(0.f, 0.f);
5389             }
5390         }
5391 
5392         // FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style)
5393         SetCurrentWindow(window);
5394 
5395         // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)
5396 
5397         if (flags & ImGuiWindowFlags_ChildWindow)
5398             window->WindowBorderSize = style.ChildBorderSize;
5399         else
5400             window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
5401         window->WindowPadding = style.WindowPadding;
5402         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
5403             window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
5404         window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
5405         window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
5406 
5407         // Collapse window by double-clicking on title bar
5408         // 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
5409         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
5410         {
5411             // 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.
5412             ImRect title_bar_rect = window->TitleBarRect();
5413             if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
5414                 window->WantCollapseToggle = true;
5415             if (window->WantCollapseToggle)
5416             {
5417                 window->Collapsed = !window->Collapsed;
5418                 MarkIniSettingsDirty(window);
5419                 FocusWindow(window);
5420             }
5421         }
5422         else
5423         {
5424             window->Collapsed = false;
5425         }
5426         window->WantCollapseToggle = false;
5427 
5428         // SIZE
5429 
5430         // Calculate auto-fit size, handle automatic resize
5431         const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSize);
5432         bool use_current_size_for_scrollbar_x = window_just_created;
5433         bool use_current_size_for_scrollbar_y = window_just_created;
5434         if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
5435         {
5436             // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
5437             if (!window_size_x_set_by_api)
5438             {
5439                 window->SizeFull.x = size_auto_fit.x;
5440                 use_current_size_for_scrollbar_x = true;
5441             }
5442             if (!window_size_y_set_by_api)
5443             {
5444                 window->SizeFull.y = size_auto_fit.y;
5445                 use_current_size_for_scrollbar_y = true;
5446             }
5447         }
5448         else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
5449         {
5450             // Auto-fit may only grow window during the first few frames
5451             // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
5452             if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
5453             {
5454                 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
5455                 use_current_size_for_scrollbar_x = true;
5456             }
5457             if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
5458             {
5459                 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
5460                 use_current_size_for_scrollbar_y = true;
5461             }
5462             if (!window->Collapsed)
5463                 MarkIniSettingsDirty(window);
5464         }
5465 
5466         // Apply minimum/maximum window size constraints and final size
5467         window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull);
5468         window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
5469 
5470         // Decoration size
5471         const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
5472 
5473         // POSITION
5474 
5475         // Popup latch its initial position, will position itself when it appears next frame
5476         if (window_just_activated_by_user)
5477         {
5478             window->AutoPosLastDirection = ImGuiDir_None;
5479             if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
5480                 window->Pos = g.BeginPopupStack.back().OpenPopupPos;
5481         }
5482 
5483         // Position child window
5484         if (flags & ImGuiWindowFlags_ChildWindow)
5485         {
5486             IM_ASSERT(parent_window && parent_window->Active);
5487             window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
5488             parent_window->DC.ChildWindows.push_back(window);
5489             if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
5490                 window->Pos = parent_window->DC.CursorPos;
5491         }
5492 
5493         const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0);
5494         if (window_pos_with_pivot)
5495             SetWindowPos(window, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering)
5496         else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
5497             window->Pos = FindBestWindowPosForPopup(window);
5498         else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
5499             window->Pos = FindBestWindowPosForPopup(window);
5500         else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
5501             window->Pos = FindBestWindowPosForPopup(window);
5502 
5503         // Clamp position/size so window stays visible within its viewport or monitor
5504 
5505         // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
5506         ImRect viewport_rect(GetViewportRect());
5507         if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5508         {
5509             if (g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f) // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
5510             {
5511                 ImVec2 clamp_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
5512                 ClampWindowRect(window, viewport_rect, clamp_padding);
5513             }
5514         }
5515         window->Pos = ImFloor(window->Pos);
5516 
5517         // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
5518         window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
5519 
5520         // Apply window focus (new and reactivated windows are moved to front)
5521         bool want_focus = false;
5522         if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
5523         {
5524             if (flags & ImGuiWindowFlags_Popup)
5525                 want_focus = true;
5526             else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0)
5527                 want_focus = true;
5528         }
5529 
5530         // Handle manual resize: Resize Grips, Borders, Gamepad
5531         int border_held = -1;
5532         ImU32 resize_grip_col[4] = {};
5533         const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // 4
5534         const float resize_grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
5535         if (!window->Collapsed)
5536             if (UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]))
5537                 use_current_size_for_scrollbar_x = use_current_size_for_scrollbar_y = true;
5538         window->ResizeBorderHeld = (signed char)border_held;
5539 
5540         // SCROLLBAR VISIBILITY
5541 
5542         // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).
5543         if (!window->Collapsed)
5544         {
5545             // When reading the current size we need to read it after size constraints have been applied.
5546             // When we use InnerRect here we are intentionally reading last frame size, same for ScrollbarSizes values before we set them again.
5547             ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - decoration_up_height);
5548             ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + window->ScrollbarSizes;
5549             ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;
5550             float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;
5551             float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;
5552             //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?
5553             window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
5554             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));
5555             if (window->ScrollbarX && !window->ScrollbarY)
5556                 window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar);
5557             window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
5558         }
5559 
5560         // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)
5561         // Update various regions. Variables they depends on should be set above in this function.
5562         // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.
5563 
5564         // Outer rectangle
5565         // Not affected by window border size. Used by:
5566         // - FindHoveredWindow() (w/ extra padding when border resize is enabled)
5567         // - Begin() initial clipping rect for drawing window background and borders.
5568         // - Begin() clipping whole child
5569         const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;
5570         const ImRect outer_rect = window->Rect();
5571         const ImRect title_bar_rect = window->TitleBarRect();
5572         window->OuterRectClipped = outer_rect;
5573         window->OuterRectClipped.ClipWith(host_rect);
5574 
5575         // Inner rectangle
5576         // Not affected by window border size. Used by:
5577         // - InnerClipRect
5578         // - ScrollToBringRectIntoView()
5579         // - NavUpdatePageUpPageDown()
5580         // - Scrollbar()
5581         window->InnerRect.Min.x = window->Pos.x;
5582         window->InnerRect.Min.y = window->Pos.y + decoration_up_height;
5583         window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x;
5584         window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y;
5585 
5586         // Inner clipping rectangle.
5587         // Will extend a little bit outside the normal work region.
5588         // This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
5589         // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
5590         // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
5591         // Affected by window/frame border size. Used by:
5592         // - Begin() initial clip rect
5593         float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
5594         window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
5595         window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size);
5596         window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
5597         window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize);
5598         window->InnerClipRect.ClipWithFull(host_rect);
5599 
5600         // Default item width. Make it proportional to window size if window manually resizes
5601         if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
5602             window->ItemWidthDefault = ImFloor(window->Size.x * 0.65f);
5603         else
5604             window->ItemWidthDefault = ImFloor(g.FontSize * 16.0f);
5605 
5606         // SCROLLING
5607 
5608         // Lock down maximum scrolling
5609         // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate
5610         // for right/bottom aligned items without creating a scrollbar.
5611         window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth());
5612         window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());
5613 
5614         // Apply scrolling
5615         window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window, true);
5616         window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
5617 
5618         // DRAWING
5619 
5620         // Setup draw list and outer clipping rectangle
5621         window->DrawList->Clear();
5622         window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
5623         PushClipRect(host_rect.Min, host_rect.Max, false);
5624 
5625         // Draw modal window background (darkens what is behind them, all viewports)
5626         const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0;
5627         const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow);
5628         if (dim_bg_for_modal || dim_bg_for_window_list)
5629         {
5630             const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
5631             window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col);
5632         }
5633 
5634         // Draw navigation selection/windowing rectangle background
5635         if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim)
5636         {
5637             ImRect bb = window->Rect();
5638             bb.Expand(g.FontSize);
5639             if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway
5640                 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
5641         }
5642 
5643         // Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call.
5644         // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
5645         // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child.
5646         // We also disabled this when we have dimming overlay behind this specific one child.
5647         // 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.
5648         bool render_decorations_in_parent = false;
5649         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
5650             if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0)
5651                 render_decorations_in_parent = true;
5652         if (render_decorations_in_parent)
5653             window->DrawList = parent_window->DrawList;
5654 
5655         // Handle title bar, scrollbar, resize grips and resize borders
5656         const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
5657         const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
5658         RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, resize_grip_count, resize_grip_col, resize_grip_draw_size);
5659 
5660         if (render_decorations_in_parent)
5661             window->DrawList = &window->DrawListInst;
5662 
5663         // Draw navigation selection/windowing rectangle border
5664         if (g.NavWindowingTargetAnim == window)
5665         {
5666             float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
5667             ImRect bb = window->Rect();
5668             bb.Expand(g.FontSize);
5669             if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward
5670             {
5671                 bb.Expand(-g.FontSize - 1.0f);
5672                 rounding = window->WindowRounding;
5673             }
5674             window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f);
5675         }
5676 
5677         // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
5678 
5679         // Work rectangle.
5680         // Affected by window padding and border size. Used by:
5681         // - Columns() for right-most edge
5682         // - TreeNode(), CollapsingHeader() for right-most edge
5683         // - BeginTabBar() for right-most edge
5684         const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);
5685         const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);
5686         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));
5687         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));
5688         window->WorkRect.Min.x = ImFloor(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));
5689         window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
5690         window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
5691         window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
5692 
5693         // [LEGACY] Content Region
5694         // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
5695         // Used by:
5696         // - Mouse wheel scrolling + many other things
5697         window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
5698         window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + decoration_up_height;
5699         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));
5700         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));
5701 
5702         // Setup drawing context
5703         // (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.)
5704         window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x;
5705         window->DC.GroupOffset.x = 0.0f;
5706         window->DC.ColumnsOffset.x = 0.0f;
5707         window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, decoration_up_height + window->WindowPadding.y - window->Scroll.y);
5708         window->DC.CursorPos = window->DC.CursorStartPos;
5709         window->DC.CursorPosPrevLine = window->DC.CursorPos;
5710         window->DC.CursorMaxPos = window->DC.CursorStartPos;
5711         window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
5712         window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
5713         window->DC.NavHideHighlightOneFrame = false;
5714         window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f);
5715         window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext;
5716         window->DC.NavLayerActiveMaskNext = 0x00;
5717         window->DC.MenuBarAppending = false;
5718         window->DC.ChildWindows.resize(0);
5719         window->DC.LayoutType = ImGuiLayoutType_Vertical;
5720         window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
5721         window->DC.FocusCounterAll = window->DC.FocusCounterTab = -1;
5722         window->DC.ItemFlags = parent_window ? parent_window->DC.ItemFlags : ImGuiItemFlags_Default_;
5723         window->DC.ItemWidth = window->ItemWidthDefault;
5724         window->DC.TextWrapPos = -1.0f; // disabled
5725         window->DC.ItemFlagsStack.resize(0);
5726         window->DC.ItemWidthStack.resize(0);
5727         window->DC.TextWrapPosStack.resize(0);
5728         window->DC.CurrentColumns = NULL;
5729         window->DC.TreeDepth = 0;
5730         window->DC.TreeMayJumpToParentOnPopMask = 0x00;
5731         window->DC.StateStorage = &window->StateStorage;
5732         window->DC.GroupStack.resize(0);
5733         window->MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user);
5734 
5735         if ((flags & ImGuiWindowFlags_ChildWindow) && (window->DC.ItemFlags != parent_window->DC.ItemFlags))
5736         {
5737             window->DC.ItemFlags = parent_window->DC.ItemFlags;
5738             window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
5739         }
5740 
5741         if (window->AutoFitFramesX > 0)
5742             window->AutoFitFramesX--;
5743         if (window->AutoFitFramesY > 0)
5744             window->AutoFitFramesY--;
5745 
5746         // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
5747         if (want_focus)
5748         {
5749             FocusWindow(window);
5750             NavInitWindow(window, false);
5751         }
5752 
5753         // Title bar
5754         if (!(flags & ImGuiWindowFlags_NoTitleBar))
5755             RenderWindowTitleBarContents(window, title_bar_rect, name, p_open);
5756 
5757         // Pressing CTRL+C while holding on a window copy its content to the clipboard
5758         // 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.
5759         // Maybe we can support CTRL+C on every element?
5760         /*
5761         if (g.ActiveId == move_id)
5762             if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
5763                 LogToClipboard();
5764         */
5765 
5766         // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
5767         // This is useful to allow creating context menus on title bar only, etc.
5768         window->DC.LastItemId = window->MoveId;
5769         window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;
5770         window->DC.LastItemRect = title_bar_rect;
5771 #ifdef IMGUI_ENABLE_TEST_ENGINE
5772         if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
5773             IMGUI_TEST_ENGINE_ITEM_ADD(window->DC.LastItemRect, window->DC.LastItemId);
5774 #endif
5775     }
5776     else
5777     {
5778         // Append
5779         SetCurrentWindow(window);
5780     }
5781 
5782     PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
5783 
5784     // 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)
5785     if (first_begin_of_the_frame)
5786         window->WriteAccessed = false;
5787 
5788     window->BeginCount++;
5789     g.NextWindowData.ClearFlags();
5790 
5791     if (flags & ImGuiWindowFlags_ChildWindow)
5792     {
5793         // Child window can be out of sight and have "negative" clip windows.
5794         // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
5795         IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
5796         if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5797             if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
5798                 window->HiddenFramesCanSkipItems = 1;
5799 
5800         // Hide along with parent or if parent is collapsed
5801         if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))
5802             window->HiddenFramesCanSkipItems = 1;
5803         if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0))
5804             window->HiddenFramesCannotSkipItems = 1;
5805     }
5806 
5807     // 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)
5808     if (style.Alpha <= 0.0f)
5809         window->HiddenFramesCanSkipItems = 1;
5810 
5811     // Update the Hidden flag
5812     window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0);
5813 
5814     // Update the SkipItems flag, used to early out of all items functions (no layout required)
5815     bool skip_items = false;
5816     if (window->Collapsed || !window->Active || window->Hidden)
5817         if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0)
5818             skip_items = true;
5819     window->SkipItems = skip_items;
5820 
5821     return !skip_items;
5822 }
5823 
End()5824 void ImGui::End()
5825 {
5826     ImGuiContext& g = *GImGui;
5827 
5828     if (g.CurrentWindowStack.Size <= 1 && g.FrameScopePushedImplicitWindow)
5829     {
5830         IM_ASSERT(g.CurrentWindowStack.Size > 1 && "Calling End() too many times!");
5831         return; // FIXME-ERRORHANDLING
5832     }
5833     IM_ASSERT(g.CurrentWindowStack.Size > 0);
5834 
5835     ImGuiWindow* window = g.CurrentWindow;
5836 
5837     if (window->DC.CurrentColumns)
5838         EndColumns();
5839     PopClipRect();   // Inner window clip rectangle
5840 
5841     // Stop logging
5842     if (!(window->Flags & ImGuiWindowFlags_ChildWindow))    // FIXME: add more options for scope of logging
5843         LogFinish();
5844 
5845     // Pop from window stack
5846     g.CurrentWindowStack.pop_back();
5847     if (window->Flags & ImGuiWindowFlags_Popup)
5848         g.BeginPopupStack.pop_back();
5849     CheckStacksSize(window, false);
5850     SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
5851 }
5852 
BringWindowToFocusFront(ImGuiWindow * window)5853 void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
5854 {
5855     ImGuiContext& g = *GImGui;
5856     if (g.WindowsFocusOrder.back() == window)
5857         return;
5858     for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the top-most window
5859         if (g.WindowsFocusOrder[i] == window)
5860         {
5861             memmove(&g.WindowsFocusOrder[i], &g.WindowsFocusOrder[i + 1], (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*));
5862             g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window;
5863             break;
5864         }
5865 }
5866 
BringWindowToDisplayFront(ImGuiWindow * window)5867 void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
5868 {
5869     ImGuiContext& g = *GImGui;
5870     ImGuiWindow* current_front_window = g.Windows.back();
5871     if (current_front_window == window || current_front_window->RootWindow == window)
5872         return;
5873     for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window
5874         if (g.Windows[i] == window)
5875         {
5876             memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
5877             g.Windows[g.Windows.Size - 1] = window;
5878             break;
5879         }
5880 }
5881 
BringWindowToDisplayBack(ImGuiWindow * window)5882 void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
5883 {
5884     ImGuiContext& g = *GImGui;
5885     if (g.Windows[0] == window)
5886         return;
5887     for (int i = 0; i < g.Windows.Size; i++)
5888         if (g.Windows[i] == window)
5889         {
5890             memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
5891             g.Windows[0] = window;
5892             break;
5893         }
5894 }
5895 
5896 // Moving window to front of display and set focus (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)5897 void ImGui::FocusWindow(ImGuiWindow* window)
5898 {
5899     ImGuiContext& g = *GImGui;
5900 
5901     if (g.NavWindow != window)
5902     {
5903         g.NavWindow = window;
5904         if (window && g.NavDisableMouseHover)
5905             g.NavMousePosDirty = true;
5906         g.NavInitRequest = false;
5907         g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
5908         g.NavIdIsAlive = false;
5909         g.NavLayer = ImGuiNavLayer_Main;
5910         //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL);
5911     }
5912 
5913     // Close popups if any
5914     ClosePopupsOverWindow(window, false);
5915 
5916     // Passing NULL allow to disable keyboard focus
5917     if (!window)
5918         return;
5919 
5920     // Move the root window to the top of the pile
5921     if (window->RootWindow)
5922         window = window->RootWindow;
5923 
5924     // Steal focus on active widgets
5925     if (window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement should be unnecessary. Need further testing before removing it..
5926         if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != window)
5927             ClearActiveID();
5928 
5929     // Bring to front
5930     BringWindowToFocusFront(window);
5931     if (!(window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus))
5932         BringWindowToDisplayFront(window);
5933 }
5934 
FocusTopMostWindowUnderOne(ImGuiWindow * under_this_window,ImGuiWindow * ignore_window)5935 void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window)
5936 {
5937     ImGuiContext& g = *GImGui;
5938 
5939     int start_idx = g.WindowsFocusOrder.Size - 1;
5940     if (under_this_window != NULL)
5941     {
5942         int under_this_window_idx = FindWindowFocusIndex(under_this_window);
5943         if (under_this_window_idx != -1)
5944             start_idx = under_this_window_idx - 1;
5945     }
5946     for (int i = start_idx; i >= 0; i--)
5947     {
5948         // 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.
5949         ImGuiWindow* window = g.WindowsFocusOrder[i];
5950         if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow))
5951             if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
5952             {
5953                 ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window);
5954                 FocusWindow(focus_window);
5955                 return;
5956             }
5957     }
5958     FocusWindow(NULL);
5959 }
5960 
SetNextItemWidth(float item_width)5961 void ImGui::SetNextItemWidth(float item_width)
5962 {
5963     ImGuiContext& g = *GImGui;
5964     g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasWidth;
5965     g.NextItemData.Width = item_width;
5966 }
5967 
PushItemWidth(float item_width)5968 void ImGui::PushItemWidth(float item_width)
5969 {
5970     ImGuiContext& g = *GImGui;
5971     ImGuiWindow* window = g.CurrentWindow;
5972     window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
5973     window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
5974     g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
5975 }
5976 
PushMultiItemsWidths(int components,float w_full)5977 void ImGui::PushMultiItemsWidths(int components, float w_full)
5978 {
5979     ImGuiContext& g = *GImGui;
5980     ImGuiWindow* window = g.CurrentWindow;
5981     const ImGuiStyle& style = g.Style;
5982     const float w_item_one  = ImMax(1.0f, IM_FLOOR((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
5983     const float w_item_last = ImMax(1.0f, IM_FLOOR(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
5984     window->DC.ItemWidthStack.push_back(w_item_last);
5985     for (int i = 0; i < components-1; i++)
5986         window->DC.ItemWidthStack.push_back(w_item_one);
5987     window->DC.ItemWidth = window->DC.ItemWidthStack.back();
5988     g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
5989 }
5990 
PopItemWidth()5991 void ImGui::PopItemWidth()
5992 {
5993     ImGuiWindow* window = GetCurrentWindow();
5994     window->DC.ItemWidthStack.pop_back();
5995     window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
5996 }
5997 
5998 // Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().
5999 // The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()
CalcItemWidth()6000 float ImGui::CalcItemWidth()
6001 {
6002     ImGuiContext& g = *GImGui;
6003     ImGuiWindow* window = g.CurrentWindow;
6004     float w;
6005     if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth)
6006         w = g.NextItemData.Width;
6007     else
6008         w = window->DC.ItemWidth;
6009     if (w < 0.0f)
6010     {
6011         float region_max_x = GetContentRegionMaxAbs().x;
6012         w = ImMax(1.0f, region_max_x - window->DC.CursorPos.x + w);
6013     }
6014     w = IM_FLOOR(w);
6015     return w;
6016 }
6017 
6018 // [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().
6019 // Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.
6020 // Note that only CalcItemWidth() is publicly exposed.
6021 // 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)6022 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)
6023 {
6024     ImGuiWindow* window = GImGui->CurrentWindow;
6025 
6026     ImVec2 region_max;
6027     if (size.x < 0.0f || size.y < 0.0f)
6028         region_max = GetContentRegionMaxAbs();
6029 
6030     if (size.x == 0.0f)
6031         size.x = default_w;
6032     else if (size.x < 0.0f)
6033         size.x = ImMax(4.0f, region_max.x - window->DC.CursorPos.x + size.x);
6034 
6035     if (size.y == 0.0f)
6036         size.y = default_h;
6037     else if (size.y < 0.0f)
6038         size.y = ImMax(4.0f, region_max.y - window->DC.CursorPos.y + size.y);
6039 
6040     return size;
6041 }
6042 
SetCurrentFont(ImFont * font)6043 void ImGui::SetCurrentFont(ImFont* font)
6044 {
6045     ImGuiContext& g = *GImGui;
6046     IM_ASSERT(font && font->IsLoaded());    // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
6047     IM_ASSERT(font->Scale > 0.0f);
6048     g.Font = font;
6049     g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale);
6050     g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
6051 
6052     ImFontAtlas* atlas = g.Font->ContainerAtlas;
6053     g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
6054     g.DrawListSharedData.Font = g.Font;
6055     g.DrawListSharedData.FontSize = g.FontSize;
6056 }
6057 
PushFont(ImFont * font)6058 void ImGui::PushFont(ImFont* font)
6059 {
6060     ImGuiContext& g = *GImGui;
6061     if (!font)
6062         font = GetDefaultFont();
6063     SetCurrentFont(font);
6064     g.FontStack.push_back(font);
6065     g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
6066 }
6067 
PopFont()6068 void  ImGui::PopFont()
6069 {
6070     ImGuiContext& g = *GImGui;
6071     g.CurrentWindow->DrawList->PopTextureID();
6072     g.FontStack.pop_back();
6073     SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
6074 }
6075 
PushItemFlag(ImGuiItemFlags option,bool enabled)6076 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
6077 {
6078     ImGuiWindow* window = GetCurrentWindow();
6079     if (enabled)
6080         window->DC.ItemFlags |= option;
6081     else
6082         window->DC.ItemFlags &= ~option;
6083     window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
6084 }
6085 
PopItemFlag()6086 void ImGui::PopItemFlag()
6087 {
6088     ImGuiWindow* window = GetCurrentWindow();
6089     window->DC.ItemFlagsStack.pop_back();
6090     window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back();
6091 }
6092 
6093 // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system.
PushAllowKeyboardFocus(bool allow_keyboard_focus)6094 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
6095 {
6096     PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus);
6097 }
6098 
PopAllowKeyboardFocus()6099 void ImGui::PopAllowKeyboardFocus()
6100 {
6101     PopItemFlag();
6102 }
6103 
PushButtonRepeat(bool repeat)6104 void ImGui::PushButtonRepeat(bool repeat)
6105 {
6106     PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
6107 }
6108 
PopButtonRepeat()6109 void ImGui::PopButtonRepeat()
6110 {
6111     PopItemFlag();
6112 }
6113 
PushTextWrapPos(float wrap_pos_x)6114 void ImGui::PushTextWrapPos(float wrap_pos_x)
6115 {
6116     ImGuiWindow* window = GetCurrentWindow();
6117     window->DC.TextWrapPos = wrap_pos_x;
6118     window->DC.TextWrapPosStack.push_back(wrap_pos_x);
6119 }
6120 
PopTextWrapPos()6121 void ImGui::PopTextWrapPos()
6122 {
6123     ImGuiWindow* window = GetCurrentWindow();
6124     window->DC.TextWrapPosStack.pop_back();
6125     window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
6126 }
6127 
6128 // 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)6129 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
6130 {
6131     ImGuiContext& g = *GImGui;
6132     ImGuiColorMod backup;
6133     backup.Col = idx;
6134     backup.BackupValue = g.Style.Colors[idx];
6135     g.ColorModifiers.push_back(backup);
6136     g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
6137 }
6138 
PushStyleColor(ImGuiCol idx,const ImVec4 & col)6139 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
6140 {
6141     ImGuiContext& g = *GImGui;
6142     ImGuiColorMod backup;
6143     backup.Col = idx;
6144     backup.BackupValue = g.Style.Colors[idx];
6145     g.ColorModifiers.push_back(backup);
6146     g.Style.Colors[idx] = col;
6147 }
6148 
PopStyleColor(int count)6149 void ImGui::PopStyleColor(int count)
6150 {
6151     ImGuiContext& g = *GImGui;
6152     while (count > 0)
6153     {
6154         ImGuiColorMod& backup = g.ColorModifiers.back();
6155         g.Style.Colors[backup.Col] = backup.BackupValue;
6156         g.ColorModifiers.pop_back();
6157         count--;
6158     }
6159 }
6160 
6161 struct ImGuiStyleVarInfo
6162 {
6163     ImGuiDataType   Type;
6164     ImU32           Count;
6165     ImU32           Offset;
GetVarPtrImGuiStyleVarInfo6166     void*           GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
6167 };
6168 
6169 static const ImGuiStyleVarInfo GStyleVarInfo[] =
6170 {
6171     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) },               // ImGuiStyleVar_Alpha
6172     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) },       // ImGuiStyleVar_WindowPadding
6173     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) },      // ImGuiStyleVar_WindowRounding
6174     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) },    // ImGuiStyleVar_WindowBorderSize
6175     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) },       // ImGuiStyleVar_WindowMinSize
6176     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) },    // ImGuiStyleVar_WindowTitleAlign
6177     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) },       // ImGuiStyleVar_ChildRounding
6178     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) },     // ImGuiStyleVar_ChildBorderSize
6179     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) },       // ImGuiStyleVar_PopupRounding
6180     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) },     // ImGuiStyleVar_PopupBorderSize
6181     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) },        // ImGuiStyleVar_FramePadding
6182     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) },       // ImGuiStyleVar_FrameRounding
6183     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) },     // ImGuiStyleVar_FrameBorderSize
6184     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) },         // ImGuiStyleVar_ItemSpacing
6185     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) },    // ImGuiStyleVar_ItemInnerSpacing
6186     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) },       // ImGuiStyleVar_IndentSpacing
6187     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) },       // ImGuiStyleVar_ScrollbarSize
6188     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) },   // ImGuiStyleVar_ScrollbarRounding
6189     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) },         // ImGuiStyleVar_GrabMinSize
6190     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) },        // ImGuiStyleVar_GrabRounding
6191     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) },         // ImGuiStyleVar_TabRounding
6192     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) },     // ImGuiStyleVar_ButtonTextAlign
6193     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
6194 };
6195 
GetStyleVarInfo(ImGuiStyleVar idx)6196 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
6197 {
6198     IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
6199     IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
6200     return &GStyleVarInfo[idx];
6201 }
6202 
PushStyleVar(ImGuiStyleVar idx,float val)6203 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
6204 {
6205     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
6206     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
6207     {
6208         ImGuiContext& g = *GImGui;
6209         float* pvar = (float*)var_info->GetVarPtr(&g.Style);
6210         g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
6211         *pvar = val;
6212         return;
6213     }
6214     IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!");
6215 }
6216 
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)6217 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
6218 {
6219     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
6220     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
6221     {
6222         ImGuiContext& g = *GImGui;
6223         ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
6224         g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
6225         *pvar = val;
6226         return;
6227     }
6228     IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!");
6229 }
6230 
PopStyleVar(int count)6231 void ImGui::PopStyleVar(int count)
6232 {
6233     ImGuiContext& g = *GImGui;
6234     while (count > 0)
6235     {
6236         // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
6237         ImGuiStyleMod& backup = g.StyleModifiers.back();
6238         const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
6239         void* data = info->GetVarPtr(&g.Style);
6240         if (info->Type == ImGuiDataType_Float && info->Count == 1)      { ((float*)data)[0] = backup.BackupFloat[0]; }
6241         else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
6242         g.StyleModifiers.pop_back();
6243         count--;
6244     }
6245 }
6246 
GetStyleColorName(ImGuiCol idx)6247 const char* ImGui::GetStyleColorName(ImGuiCol idx)
6248 {
6249     // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
6250     switch (idx)
6251     {
6252     case ImGuiCol_Text: return "Text";
6253     case ImGuiCol_TextDisabled: return "TextDisabled";
6254     case ImGuiCol_WindowBg: return "WindowBg";
6255     case ImGuiCol_ChildBg: return "ChildBg";
6256     case ImGuiCol_PopupBg: return "PopupBg";
6257     case ImGuiCol_Border: return "Border";
6258     case ImGuiCol_BorderShadow: return "BorderShadow";
6259     case ImGuiCol_FrameBg: return "FrameBg";
6260     case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
6261     case ImGuiCol_FrameBgActive: return "FrameBgActive";
6262     case ImGuiCol_TitleBg: return "TitleBg";
6263     case ImGuiCol_TitleBgActive: return "TitleBgActive";
6264     case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
6265     case ImGuiCol_MenuBarBg: return "MenuBarBg";
6266     case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
6267     case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
6268     case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
6269     case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
6270     case ImGuiCol_CheckMark: return "CheckMark";
6271     case ImGuiCol_SliderGrab: return "SliderGrab";
6272     case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
6273     case ImGuiCol_Button: return "Button";
6274     case ImGuiCol_ButtonHovered: return "ButtonHovered";
6275     case ImGuiCol_ButtonActive: return "ButtonActive";
6276     case ImGuiCol_Header: return "Header";
6277     case ImGuiCol_HeaderHovered: return "HeaderHovered";
6278     case ImGuiCol_HeaderActive: return "HeaderActive";
6279     case ImGuiCol_Separator: return "Separator";
6280     case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
6281     case ImGuiCol_SeparatorActive: return "SeparatorActive";
6282     case ImGuiCol_ResizeGrip: return "ResizeGrip";
6283     case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
6284     case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
6285     case ImGuiCol_Tab: return "Tab";
6286     case ImGuiCol_TabHovered: return "TabHovered";
6287     case ImGuiCol_TabActive: return "TabActive";
6288     case ImGuiCol_TabUnfocused: return "TabUnfocused";
6289     case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive";
6290     case ImGuiCol_PlotLines: return "PlotLines";
6291     case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
6292     case ImGuiCol_PlotHistogram: return "PlotHistogram";
6293     case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
6294     case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
6295     case ImGuiCol_DragDropTarget: return "DragDropTarget";
6296     case ImGuiCol_NavHighlight: return "NavHighlight";
6297     case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
6298     case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
6299     case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
6300     }
6301     IM_ASSERT(0);
6302     return "Unknown";
6303 }
6304 
IsWindowChildOf(ImGuiWindow * window,ImGuiWindow * potential_parent)6305 bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
6306 {
6307     if (window->RootWindow == potential_parent)
6308         return true;
6309     while (window != NULL)
6310     {
6311         if (window == potential_parent)
6312             return true;
6313         window = window->ParentWindow;
6314     }
6315     return false;
6316 }
6317 
IsWindowHovered(ImGuiHoveredFlags flags)6318 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
6319 {
6320     IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0);   // Flags not supported by this function
6321     ImGuiContext& g = *GImGui;
6322 
6323     if (flags & ImGuiHoveredFlags_AnyWindow)
6324     {
6325         if (g.HoveredWindow == NULL)
6326             return false;
6327     }
6328     else
6329     {
6330         switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
6331         {
6332         case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
6333             if (g.HoveredRootWindow != g.CurrentWindow->RootWindow)
6334                 return false;
6335             break;
6336         case ImGuiHoveredFlags_RootWindow:
6337             if (g.HoveredWindow != g.CurrentWindow->RootWindow)
6338                 return false;
6339             break;
6340         case ImGuiHoveredFlags_ChildWindows:
6341             if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow))
6342                 return false;
6343             break;
6344         default:
6345             if (g.HoveredWindow != g.CurrentWindow)
6346                 return false;
6347             break;
6348         }
6349     }
6350 
6351     if (!IsWindowContentHoverable(g.HoveredWindow, flags))
6352         return false;
6353     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
6354         if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
6355             return false;
6356     return true;
6357 }
6358 
IsWindowFocused(ImGuiFocusedFlags flags)6359 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
6360 {
6361     ImGuiContext& g = *GImGui;
6362 
6363     if (flags & ImGuiFocusedFlags_AnyWindow)
6364         return g.NavWindow != NULL;
6365 
6366     IM_ASSERT(g.CurrentWindow);     // Not inside a Begin()/End()
6367     switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
6368     {
6369     case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
6370         return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
6371     case ImGuiFocusedFlags_RootWindow:
6372         return g.NavWindow == g.CurrentWindow->RootWindow;
6373     case ImGuiFocusedFlags_ChildWindows:
6374         return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
6375     default:
6376         return g.NavWindow == g.CurrentWindow;
6377     }
6378 }
6379 
6380 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
6381 // Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmaticaly.
6382 // If you want a window to never be focused, you may use the e.g. NoInputs flag.
IsWindowNavFocusable(ImGuiWindow * window)6383 bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
6384 {
6385     return window->Active && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
6386 }
6387 
GetWindowWidth()6388 float ImGui::GetWindowWidth()
6389 {
6390     ImGuiWindow* window = GImGui->CurrentWindow;
6391     return window->Size.x;
6392 }
6393 
GetWindowHeight()6394 float ImGui::GetWindowHeight()
6395 {
6396     ImGuiWindow* window = GImGui->CurrentWindow;
6397     return window->Size.y;
6398 }
6399 
GetWindowPos()6400 ImVec2 ImGui::GetWindowPos()
6401 {
6402     ImGuiContext& g = *GImGui;
6403     ImGuiWindow* window = g.CurrentWindow;
6404     return window->Pos;
6405 }
6406 
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiCond cond)6407 void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
6408 {
6409     // Test condition (NB: bit 0 is always true) and clear flags for next time
6410     if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
6411         return;
6412 
6413     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6414     window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6415     window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
6416 
6417     // Set
6418     const ImVec2 old_pos = window->Pos;
6419     window->Pos = ImFloor(pos);
6420     ImVec2 offset = window->Pos - old_pos;
6421     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
6422     window->DC.CursorMaxPos += offset;      // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.
6423     window->DC.CursorStartPos += offset;
6424 }
6425 
SetWindowPos(const ImVec2 & pos,ImGuiCond cond)6426 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
6427 {
6428     ImGuiWindow* window = GetCurrentWindowRead();
6429     SetWindowPos(window, pos, cond);
6430 }
6431 
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiCond cond)6432 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
6433 {
6434     if (ImGuiWindow* window = FindWindowByName(name))
6435         SetWindowPos(window, pos, cond);
6436 }
6437 
GetWindowSize()6438 ImVec2 ImGui::GetWindowSize()
6439 {
6440     ImGuiWindow* window = GetCurrentWindowRead();
6441     return window->Size;
6442 }
6443 
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiCond cond)6444 void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
6445 {
6446     // Test condition (NB: bit 0 is always true) and clear flags for next time
6447     if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
6448         return;
6449 
6450     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6451     window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6452 
6453     // Set
6454     if (size.x > 0.0f)
6455     {
6456         window->AutoFitFramesX = 0;
6457         window->SizeFull.x = IM_FLOOR(size.x);
6458     }
6459     else
6460     {
6461         window->AutoFitFramesX = 2;
6462         window->AutoFitOnlyGrows = false;
6463     }
6464     if (size.y > 0.0f)
6465     {
6466         window->AutoFitFramesY = 0;
6467         window->SizeFull.y = IM_FLOOR(size.y);
6468     }
6469     else
6470     {
6471         window->AutoFitFramesY = 2;
6472         window->AutoFitOnlyGrows = false;
6473     }
6474 }
6475 
SetWindowSize(const ImVec2 & size,ImGuiCond cond)6476 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
6477 {
6478     SetWindowSize(GImGui->CurrentWindow, size, cond);
6479 }
6480 
SetWindowSize(const char * name,const ImVec2 & size,ImGuiCond cond)6481 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
6482 {
6483     if (ImGuiWindow* window = FindWindowByName(name))
6484         SetWindowSize(window, size, cond);
6485 }
6486 
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiCond cond)6487 void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
6488 {
6489     // Test condition (NB: bit 0 is always true) and clear flags for next time
6490     if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
6491         return;
6492     window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6493 
6494     // Set
6495     window->Collapsed = collapsed;
6496 }
6497 
SetWindowCollapsed(bool collapsed,ImGuiCond cond)6498 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
6499 {
6500     SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
6501 }
6502 
IsWindowCollapsed()6503 bool ImGui::IsWindowCollapsed()
6504 {
6505     ImGuiWindow* window = GetCurrentWindowRead();
6506     return window->Collapsed;
6507 }
6508 
IsWindowAppearing()6509 bool ImGui::IsWindowAppearing()
6510 {
6511     ImGuiWindow* window = GetCurrentWindowRead();
6512     return window->Appearing;
6513 }
6514 
SetWindowCollapsed(const char * name,bool collapsed,ImGuiCond cond)6515 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
6516 {
6517     if (ImGuiWindow* window = FindWindowByName(name))
6518         SetWindowCollapsed(window, collapsed, cond);
6519 }
6520 
SetWindowFocus()6521 void ImGui::SetWindowFocus()
6522 {
6523     FocusWindow(GImGui->CurrentWindow);
6524 }
6525 
SetWindowFocus(const char * name)6526 void ImGui::SetWindowFocus(const char* name)
6527 {
6528     if (name)
6529     {
6530         if (ImGuiWindow* window = FindWindowByName(name))
6531             FocusWindow(window);
6532     }
6533     else
6534     {
6535         FocusWindow(NULL);
6536     }
6537 }
6538 
SetNextWindowPos(const ImVec2 & pos,ImGuiCond cond,const ImVec2 & pivot)6539 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
6540 {
6541     ImGuiContext& g = *GImGui;
6542     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6543     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos;
6544     g.NextWindowData.PosVal = pos;
6545     g.NextWindowData.PosPivotVal = pivot;
6546     g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
6547 }
6548 
SetNextWindowSize(const ImVec2 & size,ImGuiCond cond)6549 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
6550 {
6551     ImGuiContext& g = *GImGui;
6552     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6553     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize;
6554     g.NextWindowData.SizeVal = size;
6555     g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
6556 }
6557 
SetNextWindowSizeConstraints(const ImVec2 & size_min,const ImVec2 & size_max,ImGuiSizeCallback custom_callback,void * custom_callback_user_data)6558 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
6559 {
6560     ImGuiContext& g = *GImGui;
6561     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint;
6562     g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
6563     g.NextWindowData.SizeCallback = custom_callback;
6564     g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
6565 }
6566 
6567 // Content size = inner scrollable rectangle, padded with WindowPadding.
6568 // SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
SetNextWindowContentSize(const ImVec2 & size)6569 void ImGui::SetNextWindowContentSize(const ImVec2& size)
6570 {
6571     ImGuiContext& g = *GImGui;
6572     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize;
6573     g.NextWindowData.ContentSizeVal = size;
6574 }
6575 
SetNextWindowCollapsed(bool collapsed,ImGuiCond cond)6576 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
6577 {
6578     ImGuiContext& g = *GImGui;
6579     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6580     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed;
6581     g.NextWindowData.CollapsedVal = collapsed;
6582     g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
6583 }
6584 
SetNextWindowFocus()6585 void ImGui::SetNextWindowFocus()
6586 {
6587     ImGuiContext& g = *GImGui;
6588     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus;
6589 }
6590 
SetNextWindowBgAlpha(float alpha)6591 void ImGui::SetNextWindowBgAlpha(float alpha)
6592 {
6593     ImGuiContext& g = *GImGui;
6594     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha;
6595     g.NextWindowData.BgAlphaVal = alpha;
6596 }
6597 
6598 // FIXME: This is in window space (not screen space!). We should try to obsolete all those functions.
GetContentRegionMax()6599 ImVec2 ImGui::GetContentRegionMax()
6600 {
6601     ImGuiContext& g = *GImGui;
6602     ImGuiWindow* window = g.CurrentWindow;
6603     ImVec2 mx = window->ContentRegionRect.Max - window->Pos;
6604     if (window->DC.CurrentColumns)
6605         mx.x = window->WorkRect.Max.x - window->Pos.x;
6606     return mx;
6607 }
6608 
6609 // [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features.
GetContentRegionMaxAbs()6610 ImVec2 ImGui::GetContentRegionMaxAbs()
6611 {
6612     ImGuiContext& g = *GImGui;
6613     ImGuiWindow* window = g.CurrentWindow;
6614     ImVec2 mx = window->ContentRegionRect.Max;
6615     if (window->DC.CurrentColumns)
6616         mx.x = window->WorkRect.Max.x;
6617     return mx;
6618 }
6619 
GetContentRegionAvail()6620 ImVec2 ImGui::GetContentRegionAvail()
6621 {
6622     ImGuiWindow* window = GImGui->CurrentWindow;
6623     return GetContentRegionMaxAbs() - window->DC.CursorPos;
6624 }
6625 
6626 // In window space (not screen space!)
GetWindowContentRegionMin()6627 ImVec2 ImGui::GetWindowContentRegionMin()
6628 {
6629     ImGuiWindow* window = GImGui->CurrentWindow;
6630     return window->ContentRegionRect.Min - window->Pos;
6631 }
6632 
GetWindowContentRegionMax()6633 ImVec2 ImGui::GetWindowContentRegionMax()
6634 {
6635     ImGuiWindow* window = GImGui->CurrentWindow;
6636     return window->ContentRegionRect.Max - window->Pos;
6637 }
6638 
GetWindowContentRegionWidth()6639 float ImGui::GetWindowContentRegionWidth()
6640 {
6641     ImGuiWindow* window = GImGui->CurrentWindow;
6642     return window->ContentRegionRect.GetWidth();
6643 }
6644 
GetTextLineHeight()6645 float ImGui::GetTextLineHeight()
6646 {
6647     ImGuiContext& g = *GImGui;
6648     return g.FontSize;
6649 }
6650 
GetTextLineHeightWithSpacing()6651 float ImGui::GetTextLineHeightWithSpacing()
6652 {
6653     ImGuiContext& g = *GImGui;
6654     return g.FontSize + g.Style.ItemSpacing.y;
6655 }
6656 
GetFrameHeight()6657 float ImGui::GetFrameHeight()
6658 {
6659     ImGuiContext& g = *GImGui;
6660     return g.FontSize + g.Style.FramePadding.y * 2.0f;
6661 }
6662 
GetFrameHeightWithSpacing()6663 float ImGui::GetFrameHeightWithSpacing()
6664 {
6665     ImGuiContext& g = *GImGui;
6666     return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
6667 }
6668 
GetWindowDrawList()6669 ImDrawList* ImGui::GetWindowDrawList()
6670 {
6671     ImGuiWindow* window = GetCurrentWindow();
6672     return window->DrawList;
6673 }
6674 
GetFont()6675 ImFont* ImGui::GetFont()
6676 {
6677     return GImGui->Font;
6678 }
6679 
GetFontSize()6680 float ImGui::GetFontSize()
6681 {
6682     return GImGui->FontSize;
6683 }
6684 
GetFontTexUvWhitePixel()6685 ImVec2 ImGui::GetFontTexUvWhitePixel()
6686 {
6687     return GImGui->DrawListSharedData.TexUvWhitePixel;
6688 }
6689 
SetWindowFontScale(float scale)6690 void ImGui::SetWindowFontScale(float scale)
6691 {
6692     ImGuiContext& g = *GImGui;
6693     ImGuiWindow* window = GetCurrentWindow();
6694     window->FontWindowScale = scale;
6695     g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
6696 }
6697 
6698 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
6699 // 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()6700 ImVec2 ImGui::GetCursorPos()
6701 {
6702     ImGuiWindow* window = GetCurrentWindowRead();
6703     return window->DC.CursorPos - window->Pos + window->Scroll;
6704 }
6705 
GetCursorPosX()6706 float ImGui::GetCursorPosX()
6707 {
6708     ImGuiWindow* window = GetCurrentWindowRead();
6709     return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
6710 }
6711 
GetCursorPosY()6712 float ImGui::GetCursorPosY()
6713 {
6714     ImGuiWindow* window = GetCurrentWindowRead();
6715     return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
6716 }
6717 
SetCursorPos(const ImVec2 & local_pos)6718 void ImGui::SetCursorPos(const ImVec2& local_pos)
6719 {
6720     ImGuiWindow* window = GetCurrentWindow();
6721     window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
6722     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
6723 }
6724 
SetCursorPosX(float x)6725 void ImGui::SetCursorPosX(float x)
6726 {
6727     ImGuiWindow* window = GetCurrentWindow();
6728     window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
6729     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
6730 }
6731 
SetCursorPosY(float y)6732 void ImGui::SetCursorPosY(float y)
6733 {
6734     ImGuiWindow* window = GetCurrentWindow();
6735     window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
6736     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
6737 }
6738 
GetCursorStartPos()6739 ImVec2 ImGui::GetCursorStartPos()
6740 {
6741     ImGuiWindow* window = GetCurrentWindowRead();
6742     return window->DC.CursorStartPos - window->Pos;
6743 }
6744 
GetCursorScreenPos()6745 ImVec2 ImGui::GetCursorScreenPos()
6746 {
6747     ImGuiWindow* window = GetCurrentWindowRead();
6748     return window->DC.CursorPos;
6749 }
6750 
SetCursorScreenPos(const ImVec2 & pos)6751 void ImGui::SetCursorScreenPos(const ImVec2& pos)
6752 {
6753     ImGuiWindow* window = GetCurrentWindow();
6754     window->DC.CursorPos = pos;
6755     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
6756 }
6757 
ActivateItem(ImGuiID id)6758 void ImGui::ActivateItem(ImGuiID id)
6759 {
6760     ImGuiContext& g = *GImGui;
6761     g.NavNextActivateId = id;
6762 }
6763 
SetKeyboardFocusHere(int offset)6764 void ImGui::SetKeyboardFocusHere(int offset)
6765 {
6766     IM_ASSERT(offset >= -1);    // -1 is allowed but not below
6767     ImGuiContext& g = *GImGui;
6768     ImGuiWindow* window = g.CurrentWindow;
6769     g.FocusRequestNextWindow = window;
6770     g.FocusRequestNextCounterAll = window->DC.FocusCounterAll + 1 + offset;
6771     g.FocusRequestNextCounterTab = INT_MAX;
6772 }
6773 
SetItemDefaultFocus()6774 void ImGui::SetItemDefaultFocus()
6775 {
6776     ImGuiContext& g = *GImGui;
6777     ImGuiWindow* window = g.CurrentWindow;
6778     if (!window->Appearing)
6779         return;
6780     if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent)
6781     {
6782         g.NavInitRequest = false;
6783         g.NavInitResultId = g.NavWindow->DC.LastItemId;
6784         g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos);
6785         NavUpdateAnyRequestFlag();
6786         if (!IsItemVisible())
6787             SetScrollHereY();
6788     }
6789 }
6790 
SetStateStorage(ImGuiStorage * tree)6791 void ImGui::SetStateStorage(ImGuiStorage* tree)
6792 {
6793     ImGuiWindow* window = GImGui->CurrentWindow;
6794     window->DC.StateStorage = tree ? tree : &window->StateStorage;
6795 }
6796 
GetStateStorage()6797 ImGuiStorage* ImGui::GetStateStorage()
6798 {
6799     ImGuiWindow* window = GImGui->CurrentWindow;
6800     return window->DC.StateStorage;
6801 }
6802 
PushID(const char * str_id)6803 void ImGui::PushID(const char* str_id)
6804 {
6805     ImGuiWindow* window = GImGui->CurrentWindow;
6806     window->IDStack.push_back(window->GetIDNoKeepAlive(str_id));
6807 }
6808 
PushID(const char * str_id_begin,const char * str_id_end)6809 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
6810 {
6811     ImGuiWindow* window = GImGui->CurrentWindow;
6812     window->IDStack.push_back(window->GetIDNoKeepAlive(str_id_begin, str_id_end));
6813 }
6814 
PushID(const void * ptr_id)6815 void ImGui::PushID(const void* ptr_id)
6816 {
6817     ImGuiWindow* window = GImGui->CurrentWindow;
6818     window->IDStack.push_back(window->GetIDNoKeepAlive(ptr_id));
6819 }
6820 
PushID(int int_id)6821 void ImGui::PushID(int int_id)
6822 {
6823     ImGuiWindow* window = GImGui->CurrentWindow;
6824     window->IDStack.push_back(window->GetIDNoKeepAlive(int_id));
6825 }
6826 
6827 // Push a given id value ignoring the ID stack as a seed.
PushOverrideID(ImGuiID id)6828 void ImGui::PushOverrideID(ImGuiID id)
6829 {
6830     ImGuiWindow* window = GImGui->CurrentWindow;
6831     window->IDStack.push_back(id);
6832 }
6833 
PopID()6834 void ImGui::PopID()
6835 {
6836     ImGuiWindow* window = GImGui->CurrentWindow;
6837     window->IDStack.pop_back();
6838 }
6839 
GetID(const char * str_id)6840 ImGuiID ImGui::GetID(const char* str_id)
6841 {
6842     ImGuiWindow* window = GImGui->CurrentWindow;
6843     return window->GetID(str_id);
6844 }
6845 
GetID(const char * str_id_begin,const char * str_id_end)6846 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
6847 {
6848     ImGuiWindow* window = GImGui->CurrentWindow;
6849     return window->GetID(str_id_begin, str_id_end);
6850 }
6851 
GetID(const void * ptr_id)6852 ImGuiID ImGui::GetID(const void* ptr_id)
6853 {
6854     ImGuiWindow* window = GImGui->CurrentWindow;
6855     return window->GetID(ptr_id);
6856 }
6857 
IsRectVisible(const ImVec2 & size)6858 bool ImGui::IsRectVisible(const ImVec2& size)
6859 {
6860     ImGuiWindow* window = GImGui->CurrentWindow;
6861     return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
6862 }
6863 
IsRectVisible(const ImVec2 & rect_min,const ImVec2 & rect_max)6864 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
6865 {
6866     ImGuiWindow* window = GImGui->CurrentWindow;
6867     return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
6868 }
6869 
6870 // 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()6871 void ImGui::BeginGroup()
6872 {
6873     ImGuiContext& g = *GImGui;
6874     ImGuiWindow* window = GetCurrentWindow();
6875 
6876     window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
6877     ImGuiGroupData& group_data = window->DC.GroupStack.back();
6878     group_data.BackupCursorPos = window->DC.CursorPos;
6879     group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
6880     group_data.BackupIndent = window->DC.Indent;
6881     group_data.BackupGroupOffset = window->DC.GroupOffset;
6882     group_data.BackupCurrLineSize = window->DC.CurrLineSize;
6883     group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;
6884     group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
6885     group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
6886     group_data.EmitItem = true;
6887 
6888     window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
6889     window->DC.Indent = window->DC.GroupOffset;
6890     window->DC.CursorMaxPos = window->DC.CursorPos;
6891     window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
6892     if (g.LogEnabled)
6893         g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return
6894 }
6895 
EndGroup()6896 void ImGui::EndGroup()
6897 {
6898     ImGuiContext& g = *GImGui;
6899     ImGuiWindow* window = GetCurrentWindow();
6900     IM_ASSERT(!window->DC.GroupStack.empty());  // Mismatched BeginGroup()/EndGroup() calls
6901 
6902     ImGuiGroupData& group_data = window->DC.GroupStack.back();
6903 
6904     ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos));
6905 
6906     window->DC.CursorPos = group_data.BackupCursorPos;
6907     window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
6908     window->DC.Indent = group_data.BackupIndent;
6909     window->DC.GroupOffset = group_data.BackupGroupOffset;
6910     window->DC.CurrLineSize = group_data.BackupCurrLineSize;
6911     window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;
6912     if (g.LogEnabled)
6913         g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return
6914 
6915     if (!group_data.EmitItem)
6916     {
6917         window->DC.GroupStack.pop_back();
6918         return;
6919     }
6920 
6921     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.
6922     ItemSize(group_bb.GetSize());
6923     ItemAdd(group_bb, 0);
6924 
6925     // 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.
6926     // 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.
6927     // Also if you grep for LastItemId you'll notice it is only used in that context.
6928     // (The tests not symmetrical because ActiveIdIsAlive is an ID itself, in order to be able to handle ActiveId being overwritten during the frame.)
6929     const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId;
6930     const bool group_contains_prev_active_id = !group_data.BackupActiveIdPreviousFrameIsAlive && g.ActiveIdPreviousFrameIsAlive;
6931     if (group_contains_curr_active_id)
6932         window->DC.LastItemId = g.ActiveId;
6933     else if (group_contains_prev_active_id)
6934         window->DC.LastItemId = g.ActiveIdPreviousFrame;
6935     window->DC.LastItemRect = group_bb;
6936 
6937     // Forward Edited flag
6938     if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame)
6939         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
6940 
6941     // Forward Deactivated flag
6942     window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDeactivated;
6943     if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame)
6944         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Deactivated;
6945 
6946     window->DC.GroupStack.pop_back();
6947     //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255));   // [Debug]
6948 }
6949 
6950 // Gets back to previous line and continue with horizontal layout
6951 //      offset_from_start_x == 0 : follow right after previous item
6952 //      offset_from_start_x != 0 : align to specified x position (relative to window/group left)
6953 //      spacing_w < 0            : use default spacing if pos_x == 0, no spacing if pos_x != 0
6954 //      spacing_w >= 0           : enforce spacing amount
SameLine(float offset_from_start_x,float spacing_w)6955 void ImGui::SameLine(float offset_from_start_x, float spacing_w)
6956 {
6957     ImGuiWindow* window = GetCurrentWindow();
6958     if (window->SkipItems)
6959         return;
6960 
6961     ImGuiContext& g = *GImGui;
6962     if (offset_from_start_x != 0.0f)
6963     {
6964         if (spacing_w < 0.0f) spacing_w = 0.0f;
6965         window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
6966         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
6967     }
6968     else
6969     {
6970         if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
6971         window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
6972         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
6973     }
6974     window->DC.CurrLineSize = window->DC.PrevLineSize;
6975     window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
6976 }
6977 
Indent(float indent_w)6978 void ImGui::Indent(float indent_w)
6979 {
6980     ImGuiContext& g = *GImGui;
6981     ImGuiWindow* window = GetCurrentWindow();
6982     window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
6983     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
6984 }
6985 
Unindent(float indent_w)6986 void ImGui::Unindent(float indent_w)
6987 {
6988     ImGuiContext& g = *GImGui;
6989     ImGuiWindow* window = GetCurrentWindow();
6990     window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
6991     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
6992 }
6993 
6994 
6995 //-----------------------------------------------------------------------------
6996 // [SECTION] SCROLLING
6997 //-----------------------------------------------------------------------------
6998 
CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow * window,bool snap_on_edges)6999 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges)
7000 {
7001     ImGuiContext& g = *GImGui;
7002     ImVec2 scroll = window->Scroll;
7003     if (window->ScrollTarget.x < FLT_MAX)
7004     {
7005         float cr_x = window->ScrollTargetCenterRatio.x;
7006         float target_x = window->ScrollTarget.x;
7007         if (snap_on_edges && cr_x <= 0.0f && target_x <= window->WindowPadding.x)
7008             target_x = 0.0f;
7009         else if (snap_on_edges && cr_x >= 1.0f && target_x >= window->ContentSize.x + window->WindowPadding.x + g.Style.ItemSpacing.x)
7010             target_x = window->ContentSize.x + window->WindowPadding.x * 2.0f;
7011         scroll.x = target_x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x);
7012     }
7013     if (window->ScrollTarget.y < FLT_MAX)
7014     {
7015         // '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.
7016         float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
7017         float cr_y = window->ScrollTargetCenterRatio.y;
7018         float target_y = window->ScrollTarget.y;
7019         if (snap_on_edges && cr_y <= 0.0f && target_y <= window->WindowPadding.y)
7020             target_y = 0.0f;
7021         if (snap_on_edges && cr_y >= 1.0f && target_y >= window->ContentSize.y + window->WindowPadding.y + g.Style.ItemSpacing.y)
7022             target_y = window->ContentSize.y + window->WindowPadding.y * 2.0f;
7023         scroll.y = target_y - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y - decoration_up_height);
7024     }
7025     scroll = ImMax(scroll, ImVec2(0.0f, 0.0f));
7026     if (!window->Collapsed && !window->SkipItems)
7027     {
7028         scroll.x = ImMin(scroll.x, window->ScrollMax.x);
7029         scroll.y = ImMin(scroll.y, window->ScrollMax.y);
7030     }
7031     return scroll;
7032 }
7033 
7034 // Scroll to keep newly navigated item fully into view
ScrollToBringRectIntoView(ImGuiWindow * window,const ImRect & item_rect)7035 ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect)
7036 {
7037     ImGuiContext& g = *GImGui;
7038     ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));
7039     //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
7040 
7041     ImVec2 delta_scroll;
7042     if (!window_rect.Contains(item_rect))
7043     {
7044         if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x)
7045             SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x + g.Style.ItemSpacing.x, 0.0f);
7046         else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x)
7047             SetScrollFromPosX(window, item_rect.Max.x - window->Pos.x + g.Style.ItemSpacing.x, 1.0f);
7048         if (item_rect.Min.y < window_rect.Min.y)
7049             SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y - g.Style.ItemSpacing.y, 0.0f);
7050         else if (item_rect.Max.y >= window_rect.Max.y)
7051             SetScrollFromPosY(window, item_rect.Max.y - window->Pos.y + g.Style.ItemSpacing.y, 1.0f);
7052 
7053         ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window, false);
7054         delta_scroll = next_scroll - window->Scroll;
7055     }
7056 
7057     // Also scroll parent window to keep us into view if necessary
7058     if (window->Flags & ImGuiWindowFlags_ChildWindow)
7059         delta_scroll += ScrollToBringRectIntoView(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll));
7060 
7061     return delta_scroll;
7062 }
7063 
GetScrollX()7064 float ImGui::GetScrollX()
7065 {
7066     ImGuiWindow* window = GImGui->CurrentWindow;
7067     return window->Scroll.x;
7068 }
7069 
GetScrollY()7070 float ImGui::GetScrollY()
7071 {
7072     ImGuiWindow* window = GImGui->CurrentWindow;
7073     return window->Scroll.y;
7074 }
7075 
GetScrollMaxX()7076 float ImGui::GetScrollMaxX()
7077 {
7078     ImGuiWindow* window = GImGui->CurrentWindow;
7079     return window->ScrollMax.x;
7080 }
7081 
GetScrollMaxY()7082 float ImGui::GetScrollMaxY()
7083 {
7084     ImGuiWindow* window = GImGui->CurrentWindow;
7085     return window->ScrollMax.y;
7086 }
7087 
SetScrollX(float scroll_x)7088 void ImGui::SetScrollX(float scroll_x)
7089 {
7090     ImGuiWindow* window = GetCurrentWindow();
7091     window->ScrollTarget.x = scroll_x;
7092     window->ScrollTargetCenterRatio.x = 0.0f;
7093 }
7094 
SetScrollY(float scroll_y)7095 void ImGui::SetScrollY(float scroll_y)
7096 {
7097     ImGuiWindow* window = GetCurrentWindow();
7098     window->ScrollTarget.y = scroll_y;
7099     window->ScrollTargetCenterRatio.y = 0.0f;
7100 }
7101 
SetScrollX(ImGuiWindow * window,float new_scroll_x)7102 void ImGui::SetScrollX(ImGuiWindow* window, float new_scroll_x)
7103 {
7104     window->ScrollTarget.x = new_scroll_x;
7105     window->ScrollTargetCenterRatio.x = 0.0f;
7106 }
7107 
SetScrollY(ImGuiWindow * window,float new_scroll_y)7108 void ImGui::SetScrollY(ImGuiWindow* window, float new_scroll_y)
7109 {
7110     window->ScrollTarget.y = new_scroll_y;
7111     window->ScrollTargetCenterRatio.y = 0.0f;
7112 }
7113 
7114 
SetScrollFromPosX(ImGuiWindow * window,float local_x,float center_x_ratio)7115 void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
7116 {
7117     // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
7118     IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);
7119     window->ScrollTarget.x = IM_FLOOR(local_x + window->Scroll.x);
7120     window->ScrollTargetCenterRatio.x = center_x_ratio;
7121 }
7122 
SetScrollFromPosY(ImGuiWindow * window,float local_y,float center_y_ratio)7123 void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
7124 {
7125     // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
7126     IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
7127     const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
7128     local_y -= decoration_up_height;
7129     window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y);
7130     window->ScrollTargetCenterRatio.y = center_y_ratio;
7131 }
7132 
SetScrollFromPosX(float local_x,float center_x_ratio)7133 void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)
7134 {
7135     ImGuiContext& g = *GImGui;
7136     SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio);
7137 }
7138 
SetScrollFromPosY(float local_y,float center_y_ratio)7139 void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
7140 {
7141     ImGuiContext& g = *GImGui;
7142     SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio);
7143 }
7144 
7145 // 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)7146 void ImGui::SetScrollHereX(float center_x_ratio)
7147 {
7148     ImGuiContext& g = *GImGui;
7149     ImGuiWindow* window = g.CurrentWindow;
7150     float target_x = window->DC.LastItemRect.Min.x - window->Pos.x; // Left of last item, in window space
7151     float last_item_width = window->DC.LastItemRect.GetWidth();
7152     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.
7153     SetScrollFromPosX(target_x, center_x_ratio);
7154 }
7155 
7156 // 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)7157 void ImGui::SetScrollHereY(float center_y_ratio)
7158 {
7159     ImGuiContext& g = *GImGui;
7160     ImGuiWindow* window = g.CurrentWindow;
7161     float target_y = window->DC.CursorPosPrevLine.y - window->Pos.y; // Top of last item, in window space
7162     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.
7163     SetScrollFromPosY(target_y, center_y_ratio);
7164 }
7165 
7166 //-----------------------------------------------------------------------------
7167 // [SECTION] TOOLTIPS
7168 //-----------------------------------------------------------------------------
7169 
BeginTooltip()7170 void ImGui::BeginTooltip()
7171 {
7172     ImGuiContext& g = *GImGui;
7173     if (g.DragDropWithinSourceOrTarget)
7174     {
7175         // 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)
7176         // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
7177         // 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.
7178         //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
7179         ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale);
7180         SetNextWindowPos(tooltip_pos);
7181         SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
7182         //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
7183         BeginTooltipEx(0, true);
7184     }
7185     else
7186     {
7187         BeginTooltipEx(0, false);
7188     }
7189 }
7190 
7191 // Not exposed publicly as BeginTooltip() because bool parameters are evil. Let's see if other needs arise first.
BeginTooltipEx(ImGuiWindowFlags extra_flags,bool override_previous_tooltip)7192 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip)
7193 {
7194     ImGuiContext& g = *GImGui;
7195     char window_name[16];
7196     ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
7197     if (override_previous_tooltip)
7198         if (ImGuiWindow* window = FindWindowByName(window_name))
7199             if (window->Active)
7200             {
7201                 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
7202                 window->Hidden = true;
7203                 window->HiddenFramesCanSkipItems = 1;
7204                 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
7205             }
7206     ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
7207     Begin(window_name, NULL, flags | extra_flags);
7208 }
7209 
EndTooltip()7210 void ImGui::EndTooltip()
7211 {
7212     IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip);   // Mismatched BeginTooltip()/EndTooltip() calls
7213     End();
7214 }
7215 
SetTooltipV(const char * fmt,va_list args)7216 void ImGui::SetTooltipV(const char* fmt, va_list args)
7217 {
7218     ImGuiContext& g = *GImGui;
7219     if (g.DragDropWithinSourceOrTarget)
7220         BeginTooltip();
7221     else
7222         BeginTooltipEx(0, true);
7223     TextV(fmt, args);
7224     EndTooltip();
7225 }
7226 
SetTooltip(const char * fmt,...)7227 void ImGui::SetTooltip(const char* fmt, ...)
7228 {
7229     va_list args;
7230     va_start(args, fmt);
7231     SetTooltipV(fmt, args);
7232     va_end(args);
7233 }
7234 
7235 //-----------------------------------------------------------------------------
7236 // [SECTION] POPUPS
7237 //-----------------------------------------------------------------------------
7238 
IsPopupOpen(ImGuiID id)7239 bool ImGui::IsPopupOpen(ImGuiID id)
7240 {
7241     ImGuiContext& g = *GImGui;
7242     return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
7243 }
7244 
IsPopupOpen(const char * str_id)7245 bool ImGui::IsPopupOpen(const char* str_id)
7246 {
7247     ImGuiContext& g = *GImGui;
7248     return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id);
7249 }
7250 
GetTopMostPopupModal()7251 ImGuiWindow* ImGui::GetTopMostPopupModal()
7252 {
7253     ImGuiContext& g = *GImGui;
7254     for (int n = g.OpenPopupStack.Size-1; n >= 0; n--)
7255         if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
7256             if (popup->Flags & ImGuiWindowFlags_Modal)
7257                 return popup;
7258     return NULL;
7259 }
7260 
OpenPopup(const char * str_id)7261 void ImGui::OpenPopup(const char* str_id)
7262 {
7263     ImGuiContext& g = *GImGui;
7264     OpenPopupEx(g.CurrentWindow->GetID(str_id));
7265 }
7266 
7267 // Mark popup as open (toggle toward open state).
7268 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
7269 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
7270 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
OpenPopupEx(ImGuiID id)7271 void ImGui::OpenPopupEx(ImGuiID id)
7272 {
7273     ImGuiContext& g = *GImGui;
7274     ImGuiWindow* parent_window = g.CurrentWindow;
7275     int current_stack_size = g.BeginPopupStack.Size;
7276     ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
7277     popup_ref.PopupId = id;
7278     popup_ref.Window = NULL;
7279     popup_ref.SourceWindow = g.NavWindow;
7280     popup_ref.OpenFrameCount = g.FrameCount;
7281     popup_ref.OpenParentId = parent_window->IDStack.back();
7282     popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
7283     popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
7284 
7285     //IMGUI_DEBUG_LOG("OpenPopupEx(0x%08X)\n", g.FrameCount, id);
7286     if (g.OpenPopupStack.Size < current_stack_size + 1)
7287     {
7288         g.OpenPopupStack.push_back(popup_ref);
7289     }
7290     else
7291     {
7292         // 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
7293         // 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
7294         // 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.
7295         if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
7296         {
7297             g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
7298         }
7299         else
7300         {
7301             // Close child popups if any, then flag popup for open/reopen
7302             g.OpenPopupStack.resize(current_stack_size + 1);
7303             g.OpenPopupStack[current_stack_size] = popup_ref;
7304         }
7305 
7306         // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
7307         // This is equivalent to what ClosePopupToLevel() does.
7308         //if (g.OpenPopupStack[current_stack_size].PopupId == id)
7309         //    FocusWindow(parent_window);
7310     }
7311 }
7312 
ClosePopupsOverWindow(ImGuiWindow * ref_window,bool restore_focus_to_window_under_popup)7313 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
7314 {
7315     ImGuiContext& g = *GImGui;
7316     if (g.OpenPopupStack.empty())
7317         return;
7318 
7319     // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
7320     // Don't close our own child popup windows.
7321     int popup_count_to_keep = 0;
7322     if (ref_window)
7323     {
7324         // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
7325         for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
7326         {
7327             ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];
7328             if (!popup.Window)
7329                 continue;
7330             IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
7331             if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
7332                 continue;
7333 
7334             // Trim the stack when popups are not direct descendant of the reference window (the reference window is often the NavWindow)
7335             bool popup_or_descendent_is_ref_window = false;
7336             for (int m = popup_count_to_keep; m < g.OpenPopupStack.Size && !popup_or_descendent_is_ref_window; m++)
7337                 if (ImGuiWindow* popup_window = g.OpenPopupStack[m].Window)
7338                     if (popup_window->RootWindow == ref_window->RootWindow)
7339                         popup_or_descendent_is_ref_window = true;
7340             if (!popup_or_descendent_is_ref_window)
7341                 break;
7342         }
7343     }
7344     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
7345     {
7346         //IMGUI_DEBUG_LOG("ClosePopupsOverWindow(%s) -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep);
7347         ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup);
7348     }
7349 }
7350 
ClosePopupToLevel(int remaining,bool restore_focus_to_window_under_popup)7351 void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
7352 {
7353     ImGuiContext& g = *GImGui;
7354     IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
7355     ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow;
7356     ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window;
7357     g.OpenPopupStack.resize(remaining);
7358 
7359     if (restore_focus_to_window_under_popup)
7360     {
7361         if (focus_window && !focus_window->WasActive && popup_window)
7362         {
7363             // Fallback
7364             FocusTopMostWindowUnderOne(popup_window, NULL);
7365         }
7366         else
7367         {
7368             if (g.NavLayer == 0 && focus_window)
7369                 focus_window = NavRestoreLastChildNavWindow(focus_window);
7370             FocusWindow(focus_window);
7371         }
7372     }
7373 }
7374 
7375 // Close the popup we have begin-ed into.
CloseCurrentPopup()7376 void ImGui::CloseCurrentPopup()
7377 {
7378     ImGuiContext& g = *GImGui;
7379     int popup_idx = g.BeginPopupStack.Size - 1;
7380     if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
7381         return;
7382 
7383     // Closing a menu closes its top-most parent popup (unless a modal)
7384     while (popup_idx > 0)
7385     {
7386         ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;
7387         ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
7388         bool close_parent = false;
7389         if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
7390             if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal))
7391                 close_parent = true;
7392         if (!close_parent)
7393             break;
7394         popup_idx--;
7395     }
7396     //IMGUI_DEBUG_LOG("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx);
7397     ClosePopupToLevel(popup_idx, true);
7398 
7399     // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
7400     // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
7401     // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
7402     if (ImGuiWindow* window = g.NavWindow)
7403         window->DC.NavHideHighlightOneFrame = true;
7404 }
7405 
BeginPopupEx(ImGuiID id,ImGuiWindowFlags extra_flags)7406 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags)
7407 {
7408     ImGuiContext& g = *GImGui;
7409     if (!IsPopupOpen(id))
7410     {
7411         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
7412         return false;
7413     }
7414 
7415     char name[20];
7416     if (extra_flags & ImGuiWindowFlags_ChildMenu)
7417         ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth
7418     else
7419         ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
7420 
7421     bool is_open = Begin(name, NULL, extra_flags | ImGuiWindowFlags_Popup);
7422     if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
7423         EndPopup();
7424 
7425     return is_open;
7426 }
7427 
BeginPopup(const char * str_id,ImGuiWindowFlags flags)7428 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
7429 {
7430     ImGuiContext& g = *GImGui;
7431     if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
7432     {
7433         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
7434         return false;
7435     }
7436     flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
7437     return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags);
7438 }
7439 
7440 // If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
7441 // 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)7442 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
7443 {
7444     ImGuiContext& g = *GImGui;
7445     ImGuiWindow* window = g.CurrentWindow;
7446     const ImGuiID id = window->GetID(name);
7447     if (!IsPopupOpen(id))
7448     {
7449         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
7450         return false;
7451     }
7452 
7453     // Center modal windows by default
7454     // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
7455     if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
7456         SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
7457 
7458     flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings;
7459     const bool is_open = Begin(name, p_open, flags);
7460     if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
7461     {
7462         EndPopup();
7463         if (is_open)
7464             ClosePopupToLevel(g.BeginPopupStack.Size, true);
7465         return false;
7466     }
7467     return is_open;
7468 }
7469 
EndPopup()7470 void ImGui::EndPopup()
7471 {
7472     ImGuiContext& g = *GImGui;
7473     IM_ASSERT(g.CurrentWindow->Flags & ImGuiWindowFlags_Popup);  // Mismatched BeginPopup()/EndPopup() calls
7474     IM_ASSERT(g.BeginPopupStack.Size > 0);
7475 
7476     // Make all menus and popups wrap around for now, may need to expose that policy.
7477     NavMoveRequestTryWrapping(g.CurrentWindow, ImGuiNavMoveFlags_LoopY);
7478 
7479     End();
7480 }
7481 
OpenPopupOnItemClick(const char * str_id,int mouse_button)7482 bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button)
7483 {
7484     ImGuiWindow* window = GImGui->CurrentWindow;
7485     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
7486     {
7487         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!
7488         IM_ASSERT(id != 0);                                                  // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
7489         OpenPopupEx(id);
7490         return true;
7491     }
7492     return false;
7493 }
7494 
7495 // This is a helper to handle the simplest case of associating one named popup to one given widget.
7496 // You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
7497 // You can pass a NULL str_id to use the identifier of the last item.
BeginPopupContextItem(const char * str_id,int mouse_button)7498 bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button)
7499 {
7500     ImGuiWindow* window = GImGui->CurrentWindow;
7501     if (window->SkipItems)
7502         return false;
7503     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!
7504     IM_ASSERT(id != 0);                                                  // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
7505     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
7506         OpenPopupEx(id);
7507     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
7508 }
7509 
BeginPopupContextWindow(const char * str_id,int mouse_button,bool also_over_items)7510 bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items)
7511 {
7512     if (!str_id)
7513         str_id = "window_context";
7514     ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
7515     if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
7516         if (also_over_items || !IsAnyItemHovered())
7517             OpenPopupEx(id);
7518     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
7519 }
7520 
BeginPopupContextVoid(const char * str_id,int mouse_button)7521 bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
7522 {
7523     if (!str_id)
7524         str_id = "void_context";
7525     ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
7526     if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
7527         OpenPopupEx(id);
7528     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
7529 }
7530 
7531 // 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.)
7532 // 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)7533 ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
7534 {
7535     ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
7536     //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
7537     //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
7538 
7539     // Combo Box policy (we want a connecting edge)
7540     if (policy == ImGuiPopupPositionPolicy_ComboBox)
7541     {
7542         const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
7543         for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
7544         {
7545             const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
7546             if (n != -1 && dir == *last_dir) // Already tried this direction?
7547                 continue;
7548             ImVec2 pos;
7549             if (dir == ImGuiDir_Down)  pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y);          // Below, Toward Right (default)
7550             if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
7551             if (dir == ImGuiDir_Left)  pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
7552             if (dir == ImGuiDir_Up)    pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
7553             if (!r_outer.Contains(ImRect(pos, pos + size)))
7554                 continue;
7555             *last_dir = dir;
7556             return pos;
7557         }
7558     }
7559 
7560     // Default popup policy
7561     const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
7562     for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
7563     {
7564         const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
7565         if (n != -1 && dir == *last_dir) // Already tried this direction?
7566             continue;
7567         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);
7568         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);
7569         if (avail_w < size.x || avail_h < size.y)
7570             continue;
7571         ImVec2 pos;
7572         pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
7573         pos.y = (dir == ImGuiDir_Up)   ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down)  ? r_avoid.Max.y : base_pos_clamped.y;
7574         *last_dir = dir;
7575         return pos;
7576     }
7577 
7578     // Fallback, try to keep within display
7579     *last_dir = ImGuiDir_None;
7580     ImVec2 pos = ref_pos;
7581     pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
7582     pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
7583     return pos;
7584 }
7585 
GetWindowAllowedExtentRect(ImGuiWindow * window)7586 ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window)
7587 {
7588     IM_UNUSED(window);
7589     ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding;
7590     ImRect r_screen = GetViewportRect();
7591     r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
7592     return r_screen;
7593 }
7594 
FindBestWindowPosForPopup(ImGuiWindow * window)7595 ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
7596 {
7597     ImGuiContext& g = *GImGui;
7598 
7599     ImRect r_outer = GetWindowAllowedExtentRect(window);
7600     if (window->Flags & ImGuiWindowFlags_ChildMenu)
7601     {
7602         // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
7603         // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
7604         IM_ASSERT(g.CurrentWindow == window);
7605         ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2];
7606         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).
7607         ImRect r_avoid;
7608         if (parent_window->DC.MenuBarAppending)
7609             r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight());
7610         else
7611             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);
7612         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
7613     }
7614     if (window->Flags & ImGuiWindowFlags_Popup)
7615     {
7616         ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
7617         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
7618     }
7619     if (window->Flags & ImGuiWindowFlags_Tooltip)
7620     {
7621         // Position tooltip (always follows mouse)
7622         float sc = g.Style.MouseCursorScale;
7623         ImVec2 ref_pos = NavCalcPreferredRefPos();
7624         ImRect r_avoid;
7625         if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
7626             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
7627         else
7628             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.
7629         ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
7630         if (window->AutoPosLastDirection == ImGuiDir_None)
7631             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.
7632         return pos;
7633     }
7634     IM_ASSERT(0);
7635     return window->Pos;
7636 }
7637 
7638 
7639 //-----------------------------------------------------------------------------
7640 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
7641 //-----------------------------------------------------------------------------
7642 
ImGetDirQuadrantFromDelta(float dx,float dy)7643 ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
7644 {
7645     if (ImFabs(dx) > ImFabs(dy))
7646         return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
7647     return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
7648 }
7649 
NavScoreItemDistInterval(float a0,float a1,float b0,float b1)7650 static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
7651 {
7652     if (a1 < b0)
7653         return a1 - b0;
7654     if (b1 < a0)
7655         return a0 - b1;
7656     return 0.0f;
7657 }
7658 
NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir,ImRect & r,const ImRect & clip_rect)7659 static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect)
7660 {
7661     if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
7662     {
7663         r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y);
7664         r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y);
7665     }
7666     else
7667     {
7668         r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x);
7669         r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x);
7670     }
7671 }
7672 
7673 // Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057
NavScoreItem(ImGuiNavMoveResult * result,ImRect cand)7674 static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
7675 {
7676     ImGuiContext& g = *GImGui;
7677     ImGuiWindow* window = g.CurrentWindow;
7678     if (g.NavLayer != window->DC.NavLayerCurrent)
7679         return false;
7680 
7681     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)
7682     g.NavScoringCount++;
7683 
7684     // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
7685     if (window->ParentWindow == g.NavWindow)
7686     {
7687         IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened);
7688         if (!window->ClipRect.Overlaps(cand))
7689             return false;
7690         cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
7691     }
7692 
7693     // 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)
7694     // For example, this ensure that items in one column are not reached when moving vertically from items in another column.
7695     NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect);
7696 
7697     // Compute distance between boxes
7698     // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
7699     float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
7700     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
7701     if (dby != 0.0f && dbx != 0.0f)
7702        dbx = (dbx/1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
7703     float dist_box = ImFabs(dbx) + ImFabs(dby);
7704 
7705     // 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)
7706     float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
7707     float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
7708     float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
7709 
7710     // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
7711     ImGuiDir quadrant;
7712     float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
7713     if (dbx != 0.0f || dby != 0.0f)
7714     {
7715         // For non-overlapping boxes, use distance between boxes
7716         dax = dbx;
7717         day = dby;
7718         dist_axial = dist_box;
7719         quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
7720     }
7721     else if (dcx != 0.0f || dcy != 0.0f)
7722     {
7723         // For overlapping boxes with different centers, use distance between centers
7724         dax = dcx;
7725         day = dcy;
7726         dist_axial = dist_center;
7727         quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
7728     }
7729     else
7730     {
7731         // 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)
7732         quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
7733     }
7734 
7735 #if IMGUI_DEBUG_NAV_SCORING
7736     char buf[128];
7737     if (IsMouseHoveringRect(cand.Min, cand.Max))
7738     {
7739         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]);
7740         ImDrawList* draw_list = GetForegroundDrawList(window);
7741         draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
7742         draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
7743         draw_list->AddRectFilled(cand.Max - ImVec2(4,4), cand.Max + CalcTextSize(buf) + ImVec2(4,4), IM_COL32(40,0,0,150));
7744         draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
7745     }
7746     else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
7747     {
7748         if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; }
7749         if (quadrant == g.NavMoveDir)
7750         {
7751             ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
7752             ImDrawList* draw_list = GetForegroundDrawList(window);
7753             draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
7754             draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
7755         }
7756     }
7757  #endif
7758 
7759     // Is it in the quadrant we're interesting in moving to?
7760     bool new_best = false;
7761     if (quadrant == g.NavMoveDir)
7762     {
7763         // Does it beat the current best candidate?
7764         if (dist_box < result->DistBox)
7765         {
7766             result->DistBox = dist_box;
7767             result->DistCenter = dist_center;
7768             return true;
7769         }
7770         if (dist_box == result->DistBox)
7771         {
7772             // Try using distance between center points to break ties
7773             if (dist_center < result->DistCenter)
7774             {
7775                 result->DistCenter = dist_center;
7776                 new_best = true;
7777             }
7778             else if (dist_center == result->DistCenter)
7779             {
7780                 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
7781                 // (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),
7782                 // 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.
7783                 if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
7784                     new_best = true;
7785             }
7786         }
7787     }
7788 
7789     // 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
7790     // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
7791     // 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.
7792     // 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.
7793     // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
7794     if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial)  // Check axial match
7795         if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
7796             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))
7797             {
7798                 result->DistAxial = dist_axial;
7799                 new_best = true;
7800             }
7801 
7802     return new_best;
7803 }
7804 
7805 // 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)7806 static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
7807 {
7808     ImGuiContext& g = *GImGui;
7809     //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.
7810     //    return;
7811 
7812     const ImGuiItemFlags item_flags = window->DC.ItemFlags;
7813     const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
7814 
7815     // Process Init Request
7816     if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
7817     {
7818         // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
7819         if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0)
7820         {
7821             g.NavInitResultId = id;
7822             g.NavInitResultRectRel = nav_bb_rel;
7823         }
7824         if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus))
7825         {
7826             g.NavInitRequest = false; // Found a match, clear request
7827             NavUpdateAnyRequestFlag();
7828         }
7829     }
7830 
7831     // Process Move Request (scoring for navigation)
7832     // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
7833     if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled|ImGuiItemFlags_NoNav)))
7834     {
7835         ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
7836 #if IMGUI_DEBUG_NAV_SCORING
7837         // [DEBUG] Score all items in NavWindow at all times
7838         if (!g.NavMoveRequest)
7839             g.NavMoveDir = g.NavMoveDirLast;
7840         bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest;
7841 #else
7842         bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb);
7843 #endif
7844         if (new_best)
7845         {
7846             result->ID = id;
7847             result->SelectScopeId = g.MultiSelectScopeId;
7848             result->Window = window;
7849             result->RectRel = nav_bb_rel;
7850         }
7851 
7852         const float VISIBLE_RATIO = 0.70f;
7853         if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
7854             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)
7855                 if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb))
7856                 {
7857                     result = &g.NavMoveResultLocalVisibleSet;
7858                     result->ID = id;
7859                     result->SelectScopeId = g.MultiSelectScopeId;
7860                     result->Window = window;
7861                     result->RectRel = nav_bb_rel;
7862                 }
7863     }
7864 
7865     // Update window-relative bounding box of navigated item
7866     if (g.NavId == id)
7867     {
7868         g.NavWindow = window;                                           // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
7869         g.NavLayer = window->DC.NavLayerCurrent;
7870         g.NavIdIsAlive = true;
7871         g.NavIdTabCounter = window->DC.FocusCounterTab;
7872         window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel;    // Store item bounding box (relative to window position)
7873     }
7874 }
7875 
NavMoveRequestButNoResultYet()7876 bool ImGui::NavMoveRequestButNoResultYet()
7877 {
7878     ImGuiContext& g = *GImGui;
7879     return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
7880 }
7881 
NavMoveRequestCancel()7882 void ImGui::NavMoveRequestCancel()
7883 {
7884     ImGuiContext& g = *GImGui;
7885     g.NavMoveRequest = false;
7886     NavUpdateAnyRequestFlag();
7887 }
7888 
NavMoveRequestForward(ImGuiDir move_dir,ImGuiDir clip_dir,const ImRect & bb_rel,ImGuiNavMoveFlags move_flags)7889 void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags)
7890 {
7891     ImGuiContext& g = *GImGui;
7892     IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None);
7893     NavMoveRequestCancel();
7894     g.NavMoveDir = move_dir;
7895     g.NavMoveClipDir = clip_dir;
7896     g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
7897     g.NavMoveRequestFlags = move_flags;
7898     g.NavWindow->NavRectRel[g.NavLayer] = bb_rel;
7899 }
7900 
NavMoveRequestTryWrapping(ImGuiWindow * window,ImGuiNavMoveFlags move_flags)7901 void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
7902 {
7903     ImGuiContext& g = *GImGui;
7904     if (g.NavWindow != window || !NavMoveRequestButNoResultYet() || g.NavMoveRequestForward != ImGuiNavForward_None || g.NavLayer != 0)
7905         return;
7906     IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
7907     ImRect bb_rel = window->NavRectRel[0];
7908 
7909     ImGuiDir clip_dir = g.NavMoveDir;
7910     if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
7911     {
7912         bb_rel.Min.x = bb_rel.Max.x = ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x;
7913         if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(-bb_rel.GetHeight()); clip_dir = ImGuiDir_Up; }
7914         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7915     }
7916     if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
7917     {
7918         bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
7919         if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(+bb_rel.GetHeight()); clip_dir = ImGuiDir_Down; }
7920         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7921     }
7922     if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
7923     {
7924         bb_rel.Min.y = bb_rel.Max.y = ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y;
7925         if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(-bb_rel.GetWidth()); clip_dir = ImGuiDir_Left; }
7926         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7927     }
7928     if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
7929     {
7930         bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
7931         if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(+bb_rel.GetWidth()); clip_dir = ImGuiDir_Right; }
7932         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7933     }
7934 }
7935 
7936 // FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
7937 // This way we could find the last focused window among our children. It would be much less confusing this way?
NavSaveLastChildNavWindowIntoParent(ImGuiWindow * nav_window)7938 static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window)
7939 {
7940     ImGuiWindow* parent_window = nav_window;
7941     while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
7942         parent_window = parent_window->ParentWindow;
7943     if (parent_window && parent_window != nav_window)
7944         parent_window->NavLastChildNavWindow = nav_window;
7945 }
7946 
7947 // Restore the last focused child.
7948 // Call when we are expected to land on the Main Layer (0) after FocusWindow()
NavRestoreLastChildNavWindow(ImGuiWindow * window)7949 static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
7950 {
7951     return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window;
7952 }
7953 
NavRestoreLayer(ImGuiNavLayer layer)7954 static void NavRestoreLayer(ImGuiNavLayer layer)
7955 {
7956     ImGuiContext& g = *GImGui;
7957     g.NavLayer = layer;
7958     if (layer == 0)
7959         g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow);
7960     if (layer == 0 && g.NavWindow->NavLastIds[0] != 0)
7961         ImGui::SetNavIDWithRectRel(g.NavWindow->NavLastIds[0], layer, g.NavWindow->NavRectRel[0]);
7962     else
7963         ImGui::NavInitWindow(g.NavWindow, true);
7964 }
7965 
NavUpdateAnyRequestFlag()7966 static inline void ImGui::NavUpdateAnyRequestFlag()
7967 {
7968     ImGuiContext& g = *GImGui;
7969     g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
7970     if (g.NavAnyRequest)
7971         IM_ASSERT(g.NavWindow != NULL);
7972 }
7973 
7974 // This needs to be called before we submit any widget (aka in or before Begin)
NavInitWindow(ImGuiWindow * window,bool force_reinit)7975 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
7976 {
7977     ImGuiContext& g = *GImGui;
7978     IM_ASSERT(window == g.NavWindow);
7979     bool init_for_nav = false;
7980     if (!(window->Flags & ImGuiWindowFlags_NoNavInputs))
7981         if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
7982             init_for_nav = true;
7983     //IMGUI_DEBUG_LOG("[Nav] NavInitWindow() init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer);
7984     if (init_for_nav)
7985     {
7986         SetNavID(0, g.NavLayer);
7987         g.NavInitRequest = true;
7988         g.NavInitRequestFromMove = false;
7989         g.NavInitResultId = 0;
7990         g.NavInitResultRectRel = ImRect();
7991         NavUpdateAnyRequestFlag();
7992     }
7993     else
7994     {
7995         g.NavId = window->NavLastIds[0];
7996     }
7997 }
7998 
NavCalcPreferredRefPos()7999 static ImVec2 ImGui::NavCalcPreferredRefPos()
8000 {
8001     ImGuiContext& g = *GImGui;
8002     if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow)
8003     {
8004         // Mouse (we need a fallback in case the mouse becomes invalid after being used)
8005         if (IsMousePosValid(&g.IO.MousePos))
8006             return g.IO.MousePos;
8007         return g.LastValidMousePos;
8008     }
8009     else
8010     {
8011         // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item.
8012         const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
8013         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()));
8014         ImRect visible_rect = GetViewportRect();
8015         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.
8016     }
8017 }
8018 
GetNavInputAmount(ImGuiNavInput n,ImGuiInputReadMode mode)8019 float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode)
8020 {
8021     ImGuiContext& g = *GImGui;
8022     if (mode == ImGuiInputReadMode_Down)
8023         return g.IO.NavInputs[n];                         // Instant, read analog input (0.0f..1.0f, as provided by user)
8024 
8025     const float t = g.IO.NavInputsDownDuration[n];
8026     if (t < 0.0f && mode == ImGuiInputReadMode_Released)  // Return 1.0f when just released, no repeat, ignore analog input.
8027         return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
8028     if (t < 0.0f)
8029         return 0.0f;
8030     if (mode == ImGuiInputReadMode_Pressed)               // Return 1.0f when just pressed, no repeat, ignore analog input.
8031         return (t == 0.0f) ? 1.0f : 0.0f;
8032     if (mode == ImGuiInputReadMode_Repeat)
8033         return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.80f);
8034     if (mode == ImGuiInputReadMode_RepeatSlow)
8035         return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 1.25f, g.IO.KeyRepeatRate * 2.00f);
8036     if (mode == ImGuiInputReadMode_RepeatFast)
8037         return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.30f);
8038     return 0.0f;
8039 }
8040 
GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources,ImGuiInputReadMode mode,float slow_factor,float fast_factor)8041 ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor)
8042 {
8043     ImVec2 delta(0.0f, 0.0f);
8044     if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
8045         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode)   - GetNavInputAmount(ImGuiNavInput_KeyLeft_,   mode), GetNavInputAmount(ImGuiNavInput_KeyDown_,   mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_,   mode));
8046     if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
8047         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode)   - GetNavInputAmount(ImGuiNavInput_DpadLeft,   mode), GetNavInputAmount(ImGuiNavInput_DpadDown,   mode) - GetNavInputAmount(ImGuiNavInput_DpadUp,   mode));
8048     if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
8049         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode));
8050     if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow))
8051         delta *= slow_factor;
8052     if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast))
8053         delta *= fast_factor;
8054     return delta;
8055 }
8056 
NavUpdate()8057 static void ImGui::NavUpdate()
8058 {
8059     ImGuiContext& g = *GImGui;
8060     g.IO.WantSetMousePos = false;
8061 #if 0
8062     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);
8063 #endif
8064 
8065     // Set input source as Gamepad when buttons are pressed before we map Keyboard (some features differs when used with Gamepad vs Keyboard)
8066     bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
8067     bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
8068     if (nav_gamepad_active)
8069         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)
8070             g.NavInputSource = ImGuiInputSource_NavGamepad;
8071 
8072     // Update Keyboard->Nav inputs mapping
8073     if (nav_keyboard_active)
8074     {
8075         #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)
8076         NAV_MAP_KEY(ImGuiKey_Space,     ImGuiNavInput_Activate );
8077         NAV_MAP_KEY(ImGuiKey_Enter,     ImGuiNavInput_Input    );
8078         NAV_MAP_KEY(ImGuiKey_Escape,    ImGuiNavInput_Cancel   );
8079         NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ );
8080         NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
8081         NAV_MAP_KEY(ImGuiKey_UpArrow,   ImGuiNavInput_KeyUp_   );
8082         NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
8083         if (g.IO.KeyCtrl)
8084             g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
8085         if (g.IO.KeyShift)
8086             g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
8087         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.
8088             g.IO.NavInputs[ImGuiNavInput_KeyMenu_]  = 1.0f;
8089         #undef NAV_MAP_KEY
8090     }
8091     memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration));
8092     for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++)
8093         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;
8094 
8095     // Process navigation init request (select first/default focus)
8096     // 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)
8097     if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove) && g.NavWindow)
8098     {
8099         // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
8100         //IMGUI_DEBUG_LOG("[Nav] Apply NavInitRequest result: 0x%08X Layer %d in \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name);
8101         if (g.NavInitRequestFromMove)
8102             SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, g.NavInitResultRectRel);
8103         else
8104             SetNavID(g.NavInitResultId, g.NavLayer);
8105         g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel;
8106     }
8107     g.NavInitRequest = false;
8108     g.NavInitRequestFromMove = false;
8109     g.NavInitResultId = 0;
8110     g.NavJustMovedToId = 0;
8111 
8112     // Process navigation move request
8113     if (g.NavMoveRequest)
8114         NavUpdateMoveResult();
8115 
8116     // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
8117     if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
8118     {
8119         IM_ASSERT(g.NavMoveRequest);
8120         if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
8121             g.NavDisableHighlight = false;
8122         g.NavMoveRequestForward = ImGuiNavForward_None;
8123     }
8124 
8125     // Apply application mouse position movement, after we had a chance to process move request result.
8126     if (g.NavMousePosDirty && g.NavIdIsAlive)
8127     {
8128         // Set mouse position given our knowledge of the navigated item position from last frame
8129         if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
8130         {
8131             if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
8132             {
8133                 g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos();
8134                 g.IO.WantSetMousePos = true;
8135             }
8136         }
8137         g.NavMousePosDirty = false;
8138     }
8139     g.NavIdIsAlive = false;
8140     g.NavJustTabbedId = 0;
8141     IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
8142 
8143     // 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
8144     if (g.NavWindow)
8145         NavSaveLastChildNavWindowIntoParent(g.NavWindow);
8146     if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0)
8147         g.NavWindow->NavLastChildNavWindow = NULL;
8148 
8149     // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
8150     NavUpdateWindowing();
8151 
8152     // Set output flags for user application
8153     g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
8154     g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL);
8155 
8156     // Process NavCancel input (to close a popup, get back to parent, clear focus)
8157     if (IsNavInputPressed(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
8158     {
8159         if (g.ActiveId != 0)
8160         {
8161             if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel))
8162                 ClearActiveID();
8163         }
8164         else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
8165         {
8166             // Exit child window
8167             ImGuiWindow* child_window = g.NavWindow;
8168             ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
8169             IM_ASSERT(child_window->ChildId != 0);
8170             FocusWindow(parent_window);
8171             SetNavID(child_window->ChildId, 0);
8172             g.NavIdIsAlive = false;
8173             if (g.NavDisableMouseHover)
8174                 g.NavMousePosDirty = true;
8175         }
8176         else if (g.OpenPopupStack.Size > 0)
8177         {
8178             // Close open popup/menu
8179             if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
8180                 ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
8181         }
8182         else if (g.NavLayer != 0)
8183         {
8184             // Leave the "menu" layer
8185             NavRestoreLayer(ImGuiNavLayer_Main);
8186         }
8187         else
8188         {
8189             // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
8190             if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
8191                 g.NavWindow->NavLastIds[0] = 0;
8192             g.NavId = 0;
8193         }
8194     }
8195 
8196     // Process manual activation request
8197     g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
8198     if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8199     {
8200         bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
8201         bool activate_pressed = activate_down && IsNavInputPressed(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
8202         if (g.ActiveId == 0 && activate_pressed)
8203             g.NavActivateId = g.NavId;
8204         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
8205             g.NavActivateDownId = g.NavId;
8206         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
8207             g.NavActivatePressedId = g.NavId;
8208         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputPressed(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
8209             g.NavInputId = g.NavId;
8210     }
8211     if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8212         g.NavDisableHighlight = true;
8213     if (g.NavActivateId != 0)
8214         IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
8215     g.NavMoveRequest = false;
8216 
8217     // Process programmatic activation request
8218     if (g.NavNextActivateId != 0)
8219         g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
8220     g.NavNextActivateId = 0;
8221 
8222     // Initiate directional inputs request
8223     if (g.NavMoveRequestForward == ImGuiNavForward_None)
8224     {
8225         g.NavMoveDir = ImGuiDir_None;
8226         g.NavMoveRequestFlags = ImGuiNavMoveFlags_None;
8227         if (g.NavWindow && !g.NavWindowingTarget && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8228         {
8229             if (!IsActiveIdUsingNavDir(ImGuiDir_Left)    && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadLeft, ImGuiNavInput_KeyLeft_, ImGuiInputReadMode_Repeat)) { g.NavMoveDir = ImGuiDir_Left; }
8230             if (!IsActiveIdUsingNavDir(ImGuiDir_Right)   && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadRight,ImGuiNavInput_KeyRight_,ImGuiInputReadMode_Repeat)) { g.NavMoveDir = ImGuiDir_Right; }
8231             if (!IsActiveIdUsingNavDir(ImGuiDir_Up)      && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadUp,   ImGuiNavInput_KeyUp_,   ImGuiInputReadMode_Repeat)) { g.NavMoveDir = ImGuiDir_Up; }
8232             if (!IsActiveIdUsingNavDir(ImGuiDir_Down)    && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadDown, ImGuiNavInput_KeyDown_, ImGuiInputReadMode_Repeat)) { g.NavMoveDir = ImGuiDir_Down; }
8233         }
8234         g.NavMoveClipDir = g.NavMoveDir;
8235     }
8236     else
8237     {
8238         // 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)
8239         // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function)
8240         IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
8241         IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
8242         g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
8243     }
8244 
8245     // Update PageUp/PageDown/Home/End scroll
8246     // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
8247     float nav_scoring_rect_offset_y = 0.0f;
8248     if (nav_keyboard_active)
8249         nav_scoring_rect_offset_y = NavUpdatePageUpPageDown();
8250 
8251     // 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
8252     if (g.NavMoveDir != ImGuiDir_None)
8253     {
8254         g.NavMoveRequest = true;
8255         g.NavMoveDirLast = g.NavMoveDir;
8256     }
8257     if (g.NavMoveRequest && g.NavId == 0)
8258     {
8259         //IMGUI_DEBUG_LOG("[Nav] NavInitRequest from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer);
8260         g.NavInitRequest = g.NavInitRequestFromMove = true;
8261         g.NavInitResultId = 0;
8262         g.NavDisableHighlight = false;
8263     }
8264     NavUpdateAnyRequestFlag();
8265 
8266     // Scrolling
8267     if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
8268     {
8269         // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
8270         ImGuiWindow* window = g.NavWindow;
8271         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.
8272         if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
8273         {
8274             if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
8275                 SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
8276             if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
8277                 SetScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
8278         }
8279 
8280         // *Normal* Manual scroll with NavScrollXXX keys
8281         // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
8282         ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f);
8283         if (scroll_dir.x != 0.0f && window->ScrollbarX)
8284         {
8285             SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
8286             g.NavMoveFromClampedRefRect = true;
8287         }
8288         if (scroll_dir.y != 0.0f)
8289         {
8290             SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
8291             g.NavMoveFromClampedRefRect = true;
8292         }
8293     }
8294 
8295     // Reset search results
8296     g.NavMoveResultLocal.Clear();
8297     g.NavMoveResultLocalVisibleSet.Clear();
8298     g.NavMoveResultOther.Clear();
8299 
8300     // 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
8301     if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0)
8302     {
8303         ImGuiWindow* window = g.NavWindow;
8304         ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1,1), window->InnerRect.Max - window->Pos + ImVec2(1,1));
8305         if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
8306         {
8307             float pad = window->CalcFontSize() * 0.5f;
8308             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
8309             window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel);
8310             g.NavId = 0;
8311         }
8312         g.NavMoveFromClampedRefRect = false;
8313     }
8314 
8315     // 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)
8316     ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0);
8317     g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect();
8318     g.NavScoringRectScreen.TranslateY(nav_scoring_rect_offset_y);
8319     g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x);
8320     g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x;
8321     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().
8322     //GetForegroundDrawList()->AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
8323     g.NavScoringCount = 0;
8324 #if IMGUI_DEBUG_NAV_RECTS
8325     if (g.NavWindow)
8326     {
8327         ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow);
8328         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]
8329         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); }
8330     }
8331 #endif
8332 }
8333 
8334 // Apply result from previous frame navigation directional move request
NavUpdateMoveResult()8335 static void ImGui::NavUpdateMoveResult()
8336 {
8337     ImGuiContext& g = *GImGui;
8338     if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
8339     {
8340         // 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)
8341         if (g.NavId != 0)
8342         {
8343             g.NavDisableHighlight = false;
8344             g.NavDisableMouseHover = true;
8345         }
8346         return;
8347     }
8348 
8349     // Select which result to use
8350     ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
8351 
8352     // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
8353     if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
8354         if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId)
8355             result = &g.NavMoveResultLocalVisibleSet;
8356 
8357     // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
8358     if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
8359         if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
8360             result = &g.NavMoveResultOther;
8361     IM_ASSERT(g.NavWindow && result->Window);
8362 
8363     // Scroll to keep newly navigated item fully into view.
8364     if (g.NavLayer == 0)
8365     {
8366         ImVec2 delta_scroll;
8367         if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_ScrollToEdge)
8368         {
8369             float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
8370             delta_scroll.y = result->Window->Scroll.y - scroll_target;
8371             SetScrollY(result->Window, scroll_target);
8372         }
8373         else
8374         {
8375             ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos);
8376             delta_scroll = ScrollToBringRectIntoView(result->Window, rect_abs);
8377         }
8378 
8379         // Offset our result position so mouse position can be applied immediately after in NavUpdate()
8380         result->RectRel.TranslateX(-delta_scroll.x);
8381         result->RectRel.TranslateY(-delta_scroll.y);
8382     }
8383 
8384     ClearActiveID();
8385     g.NavWindow = result->Window;
8386     if (g.NavId != result->ID)
8387     {
8388         // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
8389         g.NavJustMovedToId = result->ID;
8390         g.NavJustMovedToMultiSelectScopeId = result->SelectScopeId;
8391     }
8392     SetNavIDWithRectRel(result->ID, g.NavLayer, result->RectRel);
8393     g.NavMoveFromClampedRefRect = false;
8394 }
8395 
8396 // Handle PageUp/PageDown/Home/End keys
NavUpdatePageUpPageDown()8397 static float ImGui::NavUpdatePageUpPageDown()
8398 {
8399     ImGuiContext& g = *GImGui;
8400     if (g.NavMoveDir != ImGuiDir_None || g.NavWindow == NULL)
8401         return 0.0f;
8402     if ((g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != 0)
8403         return 0.0f;
8404 
8405     ImGuiWindow* window = g.NavWindow;
8406     const bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp);
8407     const bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown);
8408     const bool home_pressed = IsKeyPressed(g.IO.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home);
8409     const bool end_pressed = IsKeyPressed(g.IO.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End);
8410     if (page_up_held != page_down_held || home_pressed != end_pressed) // If either (not both) are pressed
8411     {
8412         if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll)
8413         {
8414             // Fallback manual-scroll when window has no navigable item
8415             if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
8416                 SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
8417             else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
8418                 SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
8419             else if (home_pressed)
8420                 SetScrollY(window, 0.0f);
8421             else if (end_pressed)
8422                 SetScrollY(window, window->ScrollMax.y);
8423         }
8424         else
8425         {
8426             ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
8427             const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
8428             float nav_scoring_rect_offset_y = 0.0f;
8429             if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
8430             {
8431                 nav_scoring_rect_offset_y = -page_offset_y;
8432                 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)
8433                 g.NavMoveClipDir = ImGuiDir_Up;
8434                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
8435             }
8436             else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
8437             {
8438                 nav_scoring_rect_offset_y = +page_offset_y;
8439                 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)
8440                 g.NavMoveClipDir = ImGuiDir_Down;
8441                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
8442             }
8443             else if (home_pressed)
8444             {
8445                 // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
8446                 // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result.
8447                 // Preserve current horizontal position if we have any.
8448                 nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y;
8449                 if (nav_rect_rel.IsInverted())
8450                     nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
8451                 g.NavMoveDir = ImGuiDir_Down;
8452                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
8453             }
8454             else if (end_pressed)
8455             {
8456                 nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y;
8457                 if (nav_rect_rel.IsInverted())
8458                     nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
8459                 g.NavMoveDir = ImGuiDir_Up;
8460                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
8461             }
8462             return nav_scoring_rect_offset_y;
8463         }
8464     }
8465     return 0.0f;
8466 }
8467 
FindWindowFocusIndex(ImGuiWindow * window)8468 static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N)
8469 {
8470     ImGuiContext& g = *GImGui;
8471     for (int i = g.WindowsFocusOrder.Size-1; i >= 0; i--)
8472         if (g.WindowsFocusOrder[i] == window)
8473             return i;
8474     return -1;
8475 }
8476 
FindWindowNavFocusable(int i_start,int i_stop,int dir)8477 static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
8478 {
8479     ImGuiContext& g = *GImGui;
8480     for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
8481         if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
8482             return g.WindowsFocusOrder[i];
8483     return NULL;
8484 }
8485 
NavUpdateWindowingHighlightWindow(int focus_change_dir)8486 static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
8487 {
8488     ImGuiContext& g = *GImGui;
8489     IM_ASSERT(g.NavWindowingTarget);
8490     if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
8491         return;
8492 
8493     const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget);
8494     ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
8495     if (!window_target)
8496         window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
8497     if (window_target) // Don't reset windowing target if there's a single window in the list
8498         g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
8499     g.NavWindowingToggleLayer = false;
8500 }
8501 
8502 // Windowing management mode
8503 // Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer)
8504 // Gamepad:  Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)
NavUpdateWindowing()8505 static void ImGui::NavUpdateWindowing()
8506 {
8507     ImGuiContext& g = *GImGui;
8508     ImGuiWindow* apply_focus_window = NULL;
8509     bool apply_toggle_layer = false;
8510 
8511     ImGuiWindow* modal_window = GetTopMostPopupModal();
8512     if (modal_window != NULL)
8513     {
8514         g.NavWindowingTarget = NULL;
8515         return;
8516     }
8517 
8518     // Fade out
8519     if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
8520     {
8521         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f);
8522         if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
8523             g.NavWindowingTargetAnim = NULL;
8524     }
8525 
8526     // Start CTRL-TAB or Square+L/R window selection
8527     bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
8528     bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
8529     if (start_windowing_with_gamepad || start_windowing_with_keyboard)
8530         if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
8531         {
8532             g.NavWindowingTarget = g.NavWindowingTargetAnim = window;
8533             g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
8534             g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true;
8535             g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad;
8536         }
8537 
8538     // Gamepad update
8539     g.NavWindowingTimer += g.IO.DeltaTime;
8540     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad)
8541     {
8542         // 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
8543         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
8544 
8545         // Select window to focus
8546         const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow);
8547         if (focus_change_dir != 0)
8548         {
8549             NavUpdateWindowingHighlightWindow(focus_change_dir);
8550             g.NavWindowingHighlightAlpha = 1.0f;
8551         }
8552 
8553         // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)
8554         if (!IsNavInputDown(ImGuiNavInput_Menu))
8555         {
8556             g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
8557             if (g.NavWindowingToggleLayer && g.NavWindow)
8558                 apply_toggle_layer = true;
8559             else if (!g.NavWindowingToggleLayer)
8560                 apply_focus_window = g.NavWindowingTarget;
8561             g.NavWindowingTarget = NULL;
8562         }
8563     }
8564 
8565     // Keyboard: Focus
8566     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard)
8567     {
8568         // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
8569         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
8570         if (IsKeyPressedMap(ImGuiKey_Tab, true))
8571             NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1);
8572         if (!g.IO.KeyCtrl)
8573             apply_focus_window = g.NavWindowingTarget;
8574     }
8575 
8576     // Keyboard: Press and Release ALT to toggle menu layer
8577     // 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
8578     if (IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Pressed))
8579         g.NavWindowingToggleLayer = true;
8580     if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && g.NavWindowingToggleLayer && IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released))
8581         if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev))
8582             apply_toggle_layer = true;
8583 
8584     // Move window
8585     if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
8586     {
8587         ImVec2 move_delta;
8588         if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift)
8589             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
8590         if (g.NavInputSource == ImGuiInputSource_NavGamepad)
8591             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down);
8592         if (move_delta.x != 0.0f || move_delta.y != 0.0f)
8593         {
8594             const float NAV_MOVE_SPEED = 800.0f;
8595             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
8596             SetWindowPos(g.NavWindowingTarget->RootWindow, g.NavWindowingTarget->RootWindow->Pos + move_delta * move_speed, ImGuiCond_Always);
8597             g.NavDisableMouseHover = true;
8598             MarkIniSettingsDirty(g.NavWindowingTarget);
8599         }
8600     }
8601 
8602     // Apply final focus
8603     if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
8604     {
8605         ClearActiveID();
8606         g.NavDisableHighlight = false;
8607         g.NavDisableMouseHover = true;
8608         apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
8609         ClosePopupsOverWindow(apply_focus_window, false);
8610         FocusWindow(apply_focus_window);
8611         if (apply_focus_window->NavLastIds[0] == 0)
8612             NavInitWindow(apply_focus_window, false);
8613 
8614         // If the window only has a menu layer, select it directly
8615         if (apply_focus_window->DC.NavLayerActiveMask == (1 << ImGuiNavLayer_Menu))
8616             g.NavLayer = ImGuiNavLayer_Menu;
8617     }
8618     if (apply_focus_window)
8619         g.NavWindowingTarget = NULL;
8620 
8621     // Apply menu/layer toggle
8622     if (apply_toggle_layer && g.NavWindow)
8623     {
8624         // Move to parent menu if necessary
8625         ImGuiWindow* new_nav_window = g.NavWindow;
8626         while (new_nav_window->ParentWindow
8627             && (new_nav_window->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) == 0
8628             && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
8629             && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
8630             new_nav_window = new_nav_window->ParentWindow;
8631         if (new_nav_window != g.NavWindow)
8632         {
8633             ImGuiWindow* old_nav_window = g.NavWindow;
8634             FocusWindow(new_nav_window);
8635             new_nav_window->NavLastChildNavWindow = old_nav_window;
8636         }
8637         g.NavDisableHighlight = false;
8638         g.NavDisableMouseHover = true;
8639 
8640         // When entering a regular menu bar with the Alt key, we always reinitialize the navigation ID.
8641         const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;
8642         NavRestoreLayer(new_nav_layer);
8643     }
8644 }
8645 
8646 // Window has already passed the IsWindowNavFocusable()
GetFallbackWindowNameForWindowingList(ImGuiWindow * window)8647 static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
8648 {
8649     if (window->Flags & ImGuiWindowFlags_Popup)
8650         return "(Popup)";
8651     if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
8652         return "(Main menu bar)";
8653     return "(Untitled)";
8654 }
8655 
8656 // Overlay displayed when using CTRL+TAB. Called by EndFrame().
NavUpdateWindowingOverlay()8657 void ImGui::NavUpdateWindowingOverlay()
8658 {
8659     ImGuiContext& g = *GImGui;
8660     IM_ASSERT(g.NavWindowingTarget != NULL);
8661 
8662     if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
8663         return;
8664 
8665     if (g.NavWindowingList == NULL)
8666         g.NavWindowingList = FindWindowByName("###NavWindowingList");
8667     SetNextWindowSizeConstraints(ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
8668     SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
8669     PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
8670     Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
8671     for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
8672     {
8673         ImGuiWindow* window = g.WindowsFocusOrder[n];
8674         if (!IsWindowNavFocusable(window))
8675             continue;
8676         const char* label = window->Name;
8677         if (label == FindRenderedTextEnd(label))
8678             label = GetFallbackWindowNameForWindowingList(window);
8679         Selectable(label, g.NavWindowingTarget == window);
8680     }
8681     End();
8682     PopStyleVar();
8683 }
8684 
8685 
8686 //-----------------------------------------------------------------------------
8687 // [SECTION] DRAG AND DROP
8688 //-----------------------------------------------------------------------------
8689 
ClearDragDrop()8690 void ImGui::ClearDragDrop()
8691 {
8692     ImGuiContext& g = *GImGui;
8693     g.DragDropActive = false;
8694     g.DragDropPayload.Clear();
8695     g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
8696     g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
8697     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
8698     g.DragDropAcceptFrameCount = -1;
8699 
8700     g.DragDropPayloadBufHeap.clear();
8701     memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
8702 }
8703 
8704 // Call when current ID is active.
8705 // 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)8706 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
8707 {
8708     ImGuiContext& g = *GImGui;
8709     ImGuiWindow* window = g.CurrentWindow;
8710 
8711     bool source_drag_active = false;
8712     ImGuiID source_id = 0;
8713     ImGuiID source_parent_id = 0;
8714     int mouse_button = 0;
8715     if (!(flags & ImGuiDragDropFlags_SourceExtern))
8716     {
8717         source_id = window->DC.LastItemId;
8718         if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case
8719             return false;
8720         if (g.IO.MouseDown[mouse_button] == false)
8721             return false;
8722 
8723         if (source_id == 0)
8724         {
8725             // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
8726             // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
8727             if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
8728             {
8729                 IM_ASSERT(0);
8730                 return false;
8731             }
8732 
8733             // Early out
8734             if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
8735                 return false;
8736 
8737             // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
8738             // We build a throwaway ID based on current ID stack + relative AABB of items in window.
8739             // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
8740             // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
8741             source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect);
8742             bool is_hovered = ItemHoverable(window->DC.LastItemRect, source_id);
8743             if (is_hovered && g.IO.MouseClicked[mouse_button])
8744             {
8745                 SetActiveID(source_id, window);
8746                 FocusWindow(window);
8747             }
8748             if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
8749                 g.ActiveIdAllowOverlap = is_hovered;
8750         }
8751         else
8752         {
8753             g.ActiveIdAllowOverlap = false;
8754         }
8755         if (g.ActiveId != source_id)
8756             return false;
8757         source_parent_id = window->IDStack.back();
8758         source_drag_active = IsMouseDragging(mouse_button);
8759     }
8760     else
8761     {
8762         window = NULL;
8763         source_id = ImHashStr("#SourceExtern");
8764         source_drag_active = true;
8765     }
8766 
8767     if (source_drag_active)
8768     {
8769         if (!g.DragDropActive)
8770         {
8771             IM_ASSERT(source_id != 0);
8772             ClearDragDrop();
8773             ImGuiPayload& payload = g.DragDropPayload;
8774             payload.SourceId = source_id;
8775             payload.SourceParentId = source_parent_id;
8776             g.DragDropActive = true;
8777             g.DragDropSourceFlags = flags;
8778             g.DragDropMouseButton = mouse_button;
8779         }
8780         g.DragDropSourceFrameCount = g.FrameCount;
8781         g.DragDropWithinSourceOrTarget = true;
8782 
8783         if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
8784         {
8785             // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
8786             // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
8787             BeginTooltip();
8788             if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
8789             {
8790                 ImGuiWindow* tooltip_window = g.CurrentWindow;
8791                 tooltip_window->SkipItems = true;
8792                 tooltip_window->HiddenFramesCanSkipItems = 1;
8793             }
8794         }
8795 
8796         if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
8797             window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
8798 
8799         return true;
8800     }
8801     return false;
8802 }
8803 
EndDragDropSource()8804 void ImGui::EndDragDropSource()
8805 {
8806     ImGuiContext& g = *GImGui;
8807     IM_ASSERT(g.DragDropActive);
8808     IM_ASSERT(g.DragDropWithinSourceOrTarget && "Not after a BeginDragDropSource()?");
8809 
8810     if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
8811         EndTooltip();
8812 
8813     // Discard the drag if have not called SetDragDropPayload()
8814     if (g.DragDropPayload.DataFrameCount == -1)
8815         ClearDragDrop();
8816     g.DragDropWithinSourceOrTarget = false;
8817 }
8818 
8819 // 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)8820 bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
8821 {
8822     ImGuiContext& g = *GImGui;
8823     ImGuiPayload& payload = g.DragDropPayload;
8824     if (cond == 0)
8825         cond = ImGuiCond_Always;
8826 
8827     IM_ASSERT(type != NULL);
8828     IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
8829     IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
8830     IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
8831     IM_ASSERT(payload.SourceId != 0);                               // Not called between BeginDragDropSource() and EndDragDropSource()
8832 
8833     if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
8834     {
8835         // Copy payload
8836         ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
8837         g.DragDropPayloadBufHeap.resize(0);
8838         if (data_size > sizeof(g.DragDropPayloadBufLocal))
8839         {
8840             // Store in heap
8841             g.DragDropPayloadBufHeap.resize((int)data_size);
8842             payload.Data = g.DragDropPayloadBufHeap.Data;
8843             memcpy(payload.Data, data, data_size);
8844         }
8845         else if (data_size > 0)
8846         {
8847             // Store locally
8848             memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
8849             payload.Data = g.DragDropPayloadBufLocal;
8850             memcpy(payload.Data, data, data_size);
8851         }
8852         else
8853         {
8854             payload.Data = NULL;
8855         }
8856         payload.DataSize = (int)data_size;
8857     }
8858     payload.DataFrameCount = g.FrameCount;
8859 
8860     return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
8861 }
8862 
BeginDragDropTargetCustom(const ImRect & bb,ImGuiID id)8863 bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
8864 {
8865     ImGuiContext& g = *GImGui;
8866     if (!g.DragDropActive)
8867         return false;
8868 
8869     ImGuiWindow* window = g.CurrentWindow;
8870     if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
8871         return false;
8872     IM_ASSERT(id != 0);
8873     if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
8874         return false;
8875     if (window->SkipItems)
8876         return false;
8877 
8878     IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
8879     g.DragDropTargetRect = bb;
8880     g.DragDropTargetId = id;
8881     g.DragDropWithinSourceOrTarget = true;
8882     return true;
8883 }
8884 
8885 // We don't use BeginDragDropTargetCustom() and duplicate its code because:
8886 // 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
8887 // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
8888 // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
BeginDragDropTarget()8889 bool ImGui::BeginDragDropTarget()
8890 {
8891     ImGuiContext& g = *GImGui;
8892     if (!g.DragDropActive)
8893         return false;
8894 
8895     ImGuiWindow* window = g.CurrentWindow;
8896     if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
8897         return false;
8898     if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
8899         return false;
8900 
8901     const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect;
8902     ImGuiID id = window->DC.LastItemId;
8903     if (id == 0)
8904         id = window->GetIDFromRectangle(display_rect);
8905     if (g.DragDropPayload.SourceId == id)
8906         return false;
8907 
8908     IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
8909     g.DragDropTargetRect = display_rect;
8910     g.DragDropTargetId = id;
8911     g.DragDropWithinSourceOrTarget = true;
8912     return true;
8913 }
8914 
IsDragDropPayloadBeingAccepted()8915 bool ImGui::IsDragDropPayloadBeingAccepted()
8916 {
8917     ImGuiContext& g = *GImGui;
8918     return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
8919 }
8920 
AcceptDragDropPayload(const char * type,ImGuiDragDropFlags flags)8921 const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
8922 {
8923     ImGuiContext& g = *GImGui;
8924     ImGuiWindow* window = g.CurrentWindow;
8925     ImGuiPayload& payload = g.DragDropPayload;
8926     IM_ASSERT(g.DragDropActive);                        // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
8927     IM_ASSERT(payload.DataFrameCount != -1);            // Forgot to call EndDragDropTarget() ?
8928     if (type != NULL && !payload.IsDataType(type))
8929         return NULL;
8930 
8931     // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
8932     // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
8933     const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
8934     ImRect r = g.DragDropTargetRect;
8935     float r_surface = r.GetWidth() * r.GetHeight();
8936     if (r_surface < g.DragDropAcceptIdCurrRectSurface)
8937     {
8938         g.DragDropAcceptFlags = flags;
8939         g.DragDropAcceptIdCurr = g.DragDropTargetId;
8940         g.DragDropAcceptIdCurrRectSurface = r_surface;
8941     }
8942 
8943     // Render default drop visuals
8944     payload.Preview = was_accepted_previously;
8945     flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
8946     if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
8947     {
8948         // FIXME-DRAG: Settle on a proper default visuals for drop target.
8949         r.Expand(3.5f);
8950         bool push_clip_rect = !window->ClipRect.Contains(r);
8951         if (push_clip_rect) window->DrawList->PushClipRect(r.Min-ImVec2(1,1), r.Max+ImVec2(1,1));
8952         window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f);
8953         if (push_clip_rect) window->DrawList->PopClipRect();
8954     }
8955 
8956     g.DragDropAcceptFrameCount = g.FrameCount;
8957     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()
8958     if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
8959         return NULL;
8960 
8961     return &payload;
8962 }
8963 
GetDragDropPayload()8964 const ImGuiPayload* ImGui::GetDragDropPayload()
8965 {
8966     ImGuiContext& g = *GImGui;
8967     return g.DragDropActive ? &g.DragDropPayload : NULL;
8968 }
8969 
8970 // We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
EndDragDropTarget()8971 void ImGui::EndDragDropTarget()
8972 {
8973     ImGuiContext& g = *GImGui;
8974     IM_ASSERT(g.DragDropActive);
8975     IM_ASSERT(g.DragDropWithinSourceOrTarget);
8976     g.DragDropWithinSourceOrTarget = false;
8977 }
8978 
8979 
8980 //-----------------------------------------------------------------------------
8981 // [SECTION] LOGGING/CAPTURING
8982 //-----------------------------------------------------------------------------
8983 // All text output from the interface can be captured into tty/file/clipboard.
8984 // By default, tree nodes are automatically opened during logging.
8985 //-----------------------------------------------------------------------------
8986 
8987 // Pass text data straight to log (without being displayed)
LogText(const char * fmt,...)8988 void ImGui::LogText(const char* fmt, ...)
8989 {
8990     ImGuiContext& g = *GImGui;
8991     if (!g.LogEnabled)
8992         return;
8993 
8994     va_list args;
8995     va_start(args, fmt);
8996     if (g.LogFile)
8997         vfprintf(g.LogFile, fmt, args);
8998     else
8999         g.LogBuffer.appendfv(fmt, args);
9000     va_end(args);
9001 }
9002 
9003 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
9004 // We split text into individual lines to add current tree level padding
LogRenderedText(const ImVec2 * ref_pos,const char * text,const char * text_end)9005 void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
9006 {
9007     ImGuiContext& g = *GImGui;
9008     ImGuiWindow* window = g.CurrentWindow;
9009 
9010     if (!text_end)
9011         text_end = FindRenderedTextEnd(text, text_end);
9012 
9013     const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + 1);
9014     if (ref_pos)
9015         g.LogLinePosY = ref_pos->y;
9016     if (log_new_line)
9017         g.LogLineFirstItem = true;
9018 
9019     const char* text_remaining = text;
9020     if (g.LogDepthRef > window->DC.TreeDepth)  // Re-adjust padding if we have popped out of our starting depth
9021         g.LogDepthRef = window->DC.TreeDepth;
9022     const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);
9023     for (;;)
9024     {
9025         // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
9026         // We don't add a trailing \n to allow a subsequent item on the same line to be captured.
9027         const char* line_start = text_remaining;
9028         const char* line_end = ImStreolRange(line_start, text_end);
9029         const bool is_first_line = (line_start == text);
9030         const bool is_last_line = (line_end == text_end);
9031         if (!is_last_line || (line_start != line_end))
9032         {
9033             const int char_count = (int)(line_end - line_start);
9034             if (log_new_line || !is_first_line)
9035                 LogText(IM_NEWLINE "%*s%.*s", tree_depth * 4, "", char_count, line_start);
9036             else if (g.LogLineFirstItem)
9037                 LogText("%*s%.*s", tree_depth * 4, "", char_count, line_start);
9038             else
9039                 LogText(" %.*s", char_count, line_start);
9040             g.LogLineFirstItem = false;
9041         }
9042         else if (log_new_line)
9043         {
9044             // An empty "" string at a different Y position should output a carriage return.
9045             LogText(IM_NEWLINE);
9046             break;
9047         }
9048 
9049         if (is_last_line)
9050             break;
9051         text_remaining = line_end + 1;
9052     }
9053 }
9054 
9055 // Start logging/capturing text output
LogBegin(ImGuiLogType type,int auto_open_depth)9056 void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth)
9057 {
9058     ImGuiContext& g = *GImGui;
9059     ImGuiWindow* window = g.CurrentWindow;
9060     IM_ASSERT(g.LogEnabled == false);
9061     IM_ASSERT(g.LogFile == NULL);
9062     IM_ASSERT(g.LogBuffer.empty());
9063     g.LogEnabled = true;
9064     g.LogType = type;
9065     g.LogDepthRef = window->DC.TreeDepth;
9066     g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);
9067     g.LogLinePosY = FLT_MAX;
9068     g.LogLineFirstItem = true;
9069 }
9070 
LogToTTY(int auto_open_depth)9071 void ImGui::LogToTTY(int auto_open_depth)
9072 {
9073     ImGuiContext& g = *GImGui;
9074     if (g.LogEnabled)
9075         return;
9076     LogBegin(ImGuiLogType_TTY, auto_open_depth);
9077     g.LogFile = stdout;
9078 }
9079 
9080 // Start logging/capturing text output to given file
LogToFile(int auto_open_depth,const char * filename)9081 void ImGui::LogToFile(int auto_open_depth, const char* filename)
9082 {
9083     ImGuiContext& g = *GImGui;
9084     if (g.LogEnabled)
9085         return;
9086 
9087     // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still
9088     // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE.
9089     // By opening the file in binary mode "ab" we have consistent output everywhere.
9090     if (!filename)
9091         filename = g.IO.LogFilename;
9092     if (!filename || !filename[0])
9093         return;
9094     FILE* f = ImFileOpen(filename, "ab");
9095     if (f == NULL)
9096     {
9097         IM_ASSERT(0);
9098         return;
9099     }
9100 
9101     LogBegin(ImGuiLogType_File, auto_open_depth);
9102     g.LogFile = f;
9103 }
9104 
9105 // Start logging/capturing text output to clipboard
LogToClipboard(int auto_open_depth)9106 void ImGui::LogToClipboard(int auto_open_depth)
9107 {
9108     ImGuiContext& g = *GImGui;
9109     if (g.LogEnabled)
9110         return;
9111     LogBegin(ImGuiLogType_Clipboard, auto_open_depth);
9112 }
9113 
LogToBuffer(int auto_open_depth)9114 void ImGui::LogToBuffer(int auto_open_depth)
9115 {
9116     ImGuiContext& g = *GImGui;
9117     if (g.LogEnabled)
9118         return;
9119     LogBegin(ImGuiLogType_Buffer, auto_open_depth);
9120 }
9121 
LogFinish()9122 void ImGui::LogFinish()
9123 {
9124     ImGuiContext& g = *GImGui;
9125     if (!g.LogEnabled)
9126         return;
9127 
9128     LogText(IM_NEWLINE);
9129     switch (g.LogType)
9130     {
9131     case ImGuiLogType_TTY:
9132         fflush(g.LogFile);
9133         break;
9134     case ImGuiLogType_File:
9135         fclose(g.LogFile);
9136         break;
9137     case ImGuiLogType_Buffer:
9138         break;
9139     case ImGuiLogType_Clipboard:
9140         if (!g.LogBuffer.empty())
9141             SetClipboardText(g.LogBuffer.begin());
9142         break;
9143     case ImGuiLogType_None:
9144         IM_ASSERT(0);
9145         break;
9146     }
9147 
9148     g.LogEnabled = false;
9149     g.LogType = ImGuiLogType_None;
9150     g.LogFile = NULL;
9151     g.LogBuffer.clear();
9152 }
9153 
9154 // Helper to display logging buttons
9155 // FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!)
LogButtons()9156 void ImGui::LogButtons()
9157 {
9158     ImGuiContext& g = *GImGui;
9159 
9160     PushID("LogButtons");
9161     const bool log_to_tty = Button("Log To TTY"); SameLine();
9162     const bool log_to_file = Button("Log To File"); SameLine();
9163     const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
9164     PushAllowKeyboardFocus(false);
9165     SetNextItemWidth(80.0f);
9166     SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL);
9167     PopAllowKeyboardFocus();
9168     PopID();
9169 
9170     // Start logging at the end of the function so that the buttons don't appear in the log
9171     if (log_to_tty)
9172         LogToTTY();
9173     if (log_to_file)
9174         LogToFile();
9175     if (log_to_clipboard)
9176         LogToClipboard();
9177 }
9178 
9179 //-----------------------------------------------------------------------------
9180 // [SECTION] SETTINGS
9181 //-----------------------------------------------------------------------------
9182 
MarkIniSettingsDirty()9183 void ImGui::MarkIniSettingsDirty()
9184 {
9185     ImGuiContext& g = *GImGui;
9186     if (g.SettingsDirtyTimer <= 0.0f)
9187         g.SettingsDirtyTimer = g.IO.IniSavingRate;
9188 }
9189 
MarkIniSettingsDirty(ImGuiWindow * window)9190 void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
9191 {
9192     ImGuiContext& g = *GImGui;
9193     if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
9194         if (g.SettingsDirtyTimer <= 0.0f)
9195             g.SettingsDirtyTimer = g.IO.IniSavingRate;
9196 }
9197 
CreateNewWindowSettings(const char * name)9198 ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
9199 {
9200     ImGuiContext& g = *GImGui;
9201     g.SettingsWindows.push_back(ImGuiWindowSettings());
9202     ImGuiWindowSettings* settings = &g.SettingsWindows.back();
9203 #if !IMGUI_DEBUG_INI_SETTINGS
9204     // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
9205     // Preserve the full string when IMGUI_DEBUG_INI_SETTINGS is set to make .ini inspection easier.
9206     if (const char* p = strstr(name, "###"))
9207         name = p;
9208 #endif
9209     size_t name_len = strlen(name);
9210     settings->NameOffset = g.SettingsWindowsNames.size();
9211     g.SettingsWindowsNames.append(name, name + name_len + 1); // Append with zero terminator
9212     settings->ID = ImHashStr(name, name_len);
9213     return settings;
9214 }
9215 
FindWindowSettings(ImGuiID id)9216 ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
9217 {
9218     ImGuiContext& g = *GImGui;
9219     for (int i = 0; i != g.SettingsWindows.Size; i++)
9220         if (g.SettingsWindows[i].ID == id)
9221             return &g.SettingsWindows[i];
9222     return NULL;
9223 }
9224 
FindOrCreateWindowSettings(const char * name)9225 ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name)
9226 {
9227     if (ImGuiWindowSettings* settings = FindWindowSettings(ImHashStr(name)))
9228         return settings;
9229     return CreateNewWindowSettings(name);
9230 }
9231 
LoadIniSettingsFromDisk(const char * ini_filename)9232 void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
9233 {
9234     size_t file_data_size = 0;
9235     char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
9236     if (!file_data)
9237         return;
9238     LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
9239     IM_FREE(file_data);
9240 }
9241 
FindSettingsHandler(const char * type_name)9242 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
9243 {
9244     ImGuiContext& g = *GImGui;
9245     const ImGuiID type_hash = ImHashStr(type_name);
9246     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
9247         if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
9248             return &g.SettingsHandlers[handler_n];
9249     return NULL;
9250 }
9251 
9252 // Zero-tolerance, no error reporting, cheap .ini parsing
LoadIniSettingsFromMemory(const char * ini_data,size_t ini_size)9253 void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
9254 {
9255     ImGuiContext& g = *GImGui;
9256     IM_ASSERT(g.Initialized);
9257     IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
9258 
9259     // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
9260     // 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..
9261     if (ini_size == 0)
9262         ini_size = strlen(ini_data);
9263     char* buf = (char*)IM_ALLOC(ini_size + 1);
9264     char* buf_end = buf + ini_size;
9265     memcpy(buf, ini_data, ini_size);
9266     buf[ini_size] = 0;
9267 
9268     void* entry_data = NULL;
9269     ImGuiSettingsHandler* entry_handler = NULL;
9270 
9271     char* line_end = NULL;
9272     for (char* line = buf; line < buf_end; line = line_end + 1)
9273     {
9274         // Skip new lines markers, then find end of the line
9275         while (*line == '\n' || *line == '\r')
9276             line++;
9277         line_end = line;
9278         while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
9279             line_end++;
9280         line_end[0] = 0;
9281         if (line[0] == ';')
9282             continue;
9283         if (line[0] == '[' && line_end > line && line_end[-1] == ']')
9284         {
9285             // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
9286             line_end[-1] = 0;
9287             const char* name_end = line_end - 1;
9288             const char* type_start = line + 1;
9289             char* type_end = (char*)(intptr_t)ImStrchrRange(type_start, name_end, ']');
9290             const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
9291             if (!type_end || !name_start)
9292             {
9293                 name_start = type_start; // Import legacy entries that have no type
9294                 type_start = "Window";
9295             }
9296             else
9297             {
9298                 *type_end = 0; // Overwrite first ']'
9299                 name_start++;  // Skip second '['
9300             }
9301             entry_handler = FindSettingsHandler(type_start);
9302             entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
9303         }
9304         else if (entry_handler != NULL && entry_data != NULL)
9305         {
9306             // Let type handler parse the line
9307             entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
9308         }
9309     }
9310     IM_FREE(buf);
9311     g.SettingsLoaded = true;
9312 }
9313 
SaveIniSettingsToDisk(const char * ini_filename)9314 void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
9315 {
9316     ImGuiContext& g = *GImGui;
9317     g.SettingsDirtyTimer = 0.0f;
9318     if (!ini_filename)
9319         return;
9320 
9321     size_t ini_data_size = 0;
9322     const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
9323     FILE* f = ImFileOpen(ini_filename, "wt");
9324     if (!f)
9325         return;
9326     fwrite(ini_data, sizeof(char), ini_data_size, f);
9327     fclose(f);
9328 }
9329 
9330 // Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
SaveIniSettingsToMemory(size_t * out_size)9331 const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
9332 {
9333     ImGuiContext& g = *GImGui;
9334     g.SettingsDirtyTimer = 0.0f;
9335     g.SettingsIniData.Buf.resize(0);
9336     g.SettingsIniData.Buf.push_back(0);
9337     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
9338     {
9339         ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
9340         handler->WriteAllFn(&g, handler, &g.SettingsIniData);
9341     }
9342     if (out_size)
9343         *out_size = (size_t)g.SettingsIniData.size();
9344     return g.SettingsIniData.c_str();
9345 }
9346 
SettingsHandlerWindow_ReadOpen(ImGuiContext *,ImGuiSettingsHandler *,const char * name)9347 static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
9348 {
9349     ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHashStr(name));
9350     if (!settings)
9351         settings = ImGui::CreateNewWindowSettings(name);
9352     return (void*)settings;
9353 }
9354 
SettingsHandlerWindow_ReadLine(ImGuiContext *,ImGuiSettingsHandler *,void * entry,const char * line)9355 static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
9356 {
9357     ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
9358     int x, y;
9359     int i;
9360     if (sscanf(line, "Pos=%i,%i", &x, &y) == 2)         settings->Pos = ImVec2ih((short)x, (short)y);
9361     else if (sscanf(line, "Size=%i,%i", &x, &y) == 2)   settings->Size = ImVec2ih((short)x, (short)y);
9362     else if (sscanf(line, "Collapsed=%d", &i) == 1)     settings->Collapsed = (i != 0);
9363 }
9364 
SettingsHandlerWindow_WriteAll(ImGuiContext * ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)9365 static void SettingsHandlerWindow_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
9366 {
9367     // Gather data from windows that were active during this session
9368     // (if a window wasn't opened in this session we preserve its settings)
9369     ImGuiContext& g = *ctx;
9370     for (int i = 0; i != g.Windows.Size; i++)
9371     {
9372         ImGuiWindow* window = g.Windows[i];
9373         if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
9374             continue;
9375 
9376         ImGuiWindowSettings* settings = (window->SettingsIdx != -1) ? &g.SettingsWindows[window->SettingsIdx] : ImGui::FindWindowSettings(window->ID);
9377         if (!settings)
9378         {
9379             settings = ImGui::CreateNewWindowSettings(window->Name);
9380             window->SettingsIdx = g.SettingsWindows.index_from_ptr(settings);
9381         }
9382         IM_ASSERT(settings->ID == window->ID);
9383         settings->Pos = ImVec2ih((short)window->Pos.x, (short)window->Pos.y);
9384         settings->Size = ImVec2ih((short)window->SizeFull.x, (short)window->SizeFull.y);
9385         settings->Collapsed = window->Collapsed;
9386     }
9387 
9388     // Write to text buffer
9389     buf->reserve(buf->size() + g.SettingsWindows.Size * 96); // ballpark reserve
9390     for (int i = 0; i != g.SettingsWindows.Size; i++)
9391     {
9392         const ImGuiWindowSettings* settings = &g.SettingsWindows[i];
9393         const char* settings_name = g.SettingsWindowsNames.c_str() + settings->NameOffset;
9394         buf->appendf("[%s][%s]\n", handler->TypeName, settings_name);
9395         buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
9396         buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
9397         buf->appendf("Collapsed=%d\n", settings->Collapsed);
9398         buf->appendf("\n");
9399     }
9400 }
9401 
9402 
9403 //-----------------------------------------------------------------------------
9404 // [SECTION] VIEWPORTS, PLATFORM WINDOWS
9405 //-----------------------------------------------------------------------------
9406 
9407 // (this section is filled in the 'docking' branch)
9408 
9409 
9410 //-----------------------------------------------------------------------------
9411 // [SECTION] DOCKING
9412 //-----------------------------------------------------------------------------
9413 
9414 // (this section is filled in the 'docking' branch)
9415 
9416 
9417 //-----------------------------------------------------------------------------
9418 // [SECTION] PLATFORM DEPENDENT HELPERS
9419 //-----------------------------------------------------------------------------
9420 
9421 #if defined(_WIN32) && !defined(_WINDOWS_) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS))
9422 #ifndef WIN32_LEAN_AND_MEAN
9423 #define WIN32_LEAN_AND_MEAN
9424 #endif
9425 #ifndef __MINGW32__
9426 #include <Windows.h>
9427 #else
9428 #include <windows.h>
9429 #endif
9430 #elif defined(__APPLE__)
9431 #include <TargetConditionals.h>
9432 #endif
9433 
9434 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
9435 
9436 #ifdef _MSC_VER
9437 #pragma comment(lib, "user32")
9438 #endif
9439 
9440 // Win32 clipboard implementation
GetClipboardTextFn_DefaultImpl(void *)9441 static const char* GetClipboardTextFn_DefaultImpl(void*)
9442 {
9443     static ImVector<char> buf_local;
9444     buf_local.clear();
9445     if (!::OpenClipboard(NULL))
9446         return NULL;
9447     HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
9448     if (wbuf_handle == NULL)
9449     {
9450         ::CloseClipboard();
9451         return NULL;
9452     }
9453     if (ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle))
9454     {
9455         int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
9456         buf_local.resize(buf_len);
9457         ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL);
9458     }
9459     ::GlobalUnlock(wbuf_handle);
9460     ::CloseClipboard();
9461     return buf_local.Data;
9462 }
9463 
SetClipboardTextFn_DefaultImpl(void *,const char * text)9464 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9465 {
9466     if (!::OpenClipboard(NULL))
9467         return;
9468     const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
9469     HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
9470     if (wbuf_handle == NULL)
9471     {
9472         ::CloseClipboard();
9473         return;
9474     }
9475     ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle);
9476     ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
9477     ::GlobalUnlock(wbuf_handle);
9478     ::EmptyClipboard();
9479     if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
9480         ::GlobalFree(wbuf_handle);
9481     ::CloseClipboard();
9482 }
9483 
9484 #elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS)
9485 
9486 #include <Carbon/Carbon.h>  // Use old API to avoid need for separate .mm file
9487 static PasteboardRef main_clipboard = 0;
9488 
9489 // OSX clipboard implementation
9490 // If you enable this you will need to add '-framework ApplicationServices' to your linker command-line!
SetClipboardTextFn_DefaultImpl(void *,const char * text)9491 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9492 {
9493     if (!main_clipboard)
9494         PasteboardCreate(kPasteboardClipboard, &main_clipboard);
9495     PasteboardClear(main_clipboard);
9496     CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text));
9497     if (cf_data)
9498     {
9499         PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0);
9500         CFRelease(cf_data);
9501     }
9502 }
9503 
GetClipboardTextFn_DefaultImpl(void *)9504 static const char* GetClipboardTextFn_DefaultImpl(void*)
9505 {
9506     if (!main_clipboard)
9507         PasteboardCreate(kPasteboardClipboard, &main_clipboard);
9508     PasteboardSynchronize(main_clipboard);
9509 
9510     ItemCount item_count = 0;
9511     PasteboardGetItemCount(main_clipboard, &item_count);
9512     for (ItemCount i = 0; i < item_count; i++)
9513     {
9514         PasteboardItemID item_id = 0;
9515         PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id);
9516         CFArrayRef flavor_type_array = 0;
9517         PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array);
9518         for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++)
9519         {
9520             CFDataRef cf_data;
9521             if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr)
9522             {
9523                 static ImVector<char> clipboard_text;
9524                 int length = (int)CFDataGetLength(cf_data);
9525                 clipboard_text.resize(length + 1);
9526                 CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)clipboard_text.Data);
9527                 clipboard_text[length] = 0;
9528                 CFRelease(cf_data);
9529                 return clipboard_text.Data;
9530             }
9531         }
9532     }
9533     return NULL;
9534 }
9535 
9536 #else
9537 
9538 // Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers.
GetClipboardTextFn_DefaultImpl(void *)9539 static const char* GetClipboardTextFn_DefaultImpl(void*)
9540 {
9541     ImGuiContext& g = *GImGui;
9542     return g.PrivateClipboard.empty() ? NULL : g.PrivateClipboard.begin();
9543 }
9544 
SetClipboardTextFn_DefaultImpl(void *,const char * text)9545 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9546 {
9547     ImGuiContext& g = *GImGui;
9548     g.PrivateClipboard.clear();
9549     const char* text_end = text + strlen(text);
9550     g.PrivateClipboard.resize((int)(text_end - text) + 1);
9551     memcpy(&g.PrivateClipboard[0], text, (size_t)(text_end - text));
9552     g.PrivateClipboard[(int)(text_end - text)] = 0;
9553 }
9554 
9555 #endif
9556 
9557 // Win32 API IME support (for Asian languages, etc.)
9558 #if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
9559 
9560 #include <imm.h>
9561 #ifdef _MSC_VER
9562 #pragma comment(lib, "imm32")
9563 #endif
9564 
ImeSetInputScreenPosFn_DefaultImpl(int x,int y)9565 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
9566 {
9567     // Notify OS Input Method Editor of text input position
9568     ImGuiIO& io = ImGui::GetIO();
9569     if (HWND hwnd = (HWND)io.ImeWindowHandle)
9570         if (HIMC himc = ::ImmGetContext(hwnd))
9571         {
9572             COMPOSITIONFORM cf;
9573             cf.ptCurrentPos.x = x;
9574             cf.ptCurrentPos.y = y;
9575             cf.dwStyle = CFS_FORCE_POSITION;
9576             ::ImmSetCompositionWindow(himc, &cf);
9577             ::ImmReleaseContext(hwnd, himc);
9578         }
9579 }
9580 
9581 #else
9582 
ImeSetInputScreenPosFn_DefaultImpl(int,int)9583 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
9584 
9585 #endif
9586 
9587 //-----------------------------------------------------------------------------
9588 // [SECTION] METRICS/DEBUG WINDOW
9589 //-----------------------------------------------------------------------------
9590 
9591 #ifndef IMGUI_DISABLE_METRICS_WINDOW
ShowMetricsWindow(bool * p_open)9592 void ImGui::ShowMetricsWindow(bool* p_open)
9593 {
9594     if (!ImGui::Begin("Dear ImGui Metrics", p_open))
9595     {
9596         ImGui::End();
9597         return;
9598     }
9599 
9600     // State
9601     enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type
9602     const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentRegionRect" };
9603     static bool show_windows_rects = false;
9604     static int  show_windows_rect_type = WRT_WorkRect;
9605     static bool show_windows_begin_order = false;
9606     static bool show_drawcmd_clip_rects = true;
9607 
9608     // Basic info
9609     ImGuiContext& g = *GImGui;
9610     ImGuiIO& io = ImGui::GetIO();
9611     ImGui::Text("Dear ImGui %s", ImGui::GetVersion());
9612     ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
9613     ImGui::Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
9614     ImGui::Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
9615     ImGui::Text("%d active allocations", io.MetricsActiveAllocations);
9616     ImGui::Separator();
9617 
9618     // Helper functions to display common structures:
9619     // - NodeDrawList
9620     // - NodeColumns
9621     // - NodeWindow
9622     // - NodeWindows
9623     // - NodeTabBar
9624     struct Funcs
9625     {
9626         static ImRect GetWindowRect(ImGuiWindow* window, int rect_type)
9627         {
9628             if (rect_type == WRT_OuterRect)                 { return window->Rect(); }
9629             else if (rect_type == WRT_OuterRectClipped)     { return window->OuterRectClipped; }
9630             else if (rect_type == WRT_InnerRect)            { return window->InnerRect; }
9631             else if (rect_type == WRT_InnerClipRect)        { return window->InnerClipRect; }
9632             else if (rect_type == WRT_WorkRect)             { return window->WorkRect; }
9633             else if (rect_type == WRT_Content)              { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
9634             else if (rect_type == WRT_ContentRegionRect)    { return window->ContentRegionRect; }
9635             IM_ASSERT(0);
9636             return ImRect();
9637         }
9638 
9639         static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label)
9640         {
9641             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);
9642             if (draw_list == ImGui::GetWindowDrawList())
9643             {
9644                 ImGui::SameLine();
9645                 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)
9646                 if (node_open) ImGui::TreePop();
9647                 return;
9648             }
9649 
9650             ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list
9651             if (window && IsItemHovered())
9652                 fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
9653             if (!node_open)
9654                 return;
9655 
9656             if (window && !window->WasActive)
9657                 ImGui::Text("(Note: owning Window is inactive: DrawList is not being rendered!)");
9658 
9659             int elem_offset = 0;
9660             for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
9661             {
9662                 if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0)
9663                     continue;
9664                 if (pcmd->UserCallback)
9665                 {
9666                     ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
9667                     continue;
9668                 }
9669                 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
9670                 char buf[300];
9671                 ImFormatString(buf, IM_ARRAYSIZE(buf), "Draw %4d triangles, tex 0x%p, clip_rect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
9672                     pcmd->ElemCount/3, (void*)(intptr_t)pcmd->TextureId, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
9673                 bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf);
9674                 if (show_drawcmd_clip_rects && fg_draw_list && ImGui::IsItemHovered())
9675                 {
9676                     ImRect clip_rect = pcmd->ClipRect;
9677                     ImRect vtxs_rect;
9678                     for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++)
9679                         vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos);
9680                     clip_rect.Floor(); fg_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,0,255,255));
9681                     vtxs_rect.Floor(); fg_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,255,0,255));
9682                 }
9683                 if (!pcmd_node_open)
9684                     continue;
9685 
9686                 // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
9687                 ImGui::Text("ElemCount: %d, ElemCount/3: %d, VtxOffset: +%d, IdxOffset: +%d", pcmd->ElemCount, pcmd->ElemCount/3, pcmd->VtxOffset, pcmd->IdxOffset);
9688                 ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
9689                 while (clipper.Step())
9690                     for (int prim = clipper.DisplayStart, idx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++)
9691                     {
9692                         char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf);
9693                         ImVec2 triangles_pos[3];
9694                         for (int n = 0; n < 3; n++, idx_i++)
9695                         {
9696                             int vtx_i = idx_buffer ? idx_buffer[idx_i] : idx_i;
9697                             ImDrawVert& v = draw_list->VtxBuffer[vtx_i];
9698                             triangles_pos[n] = v.pos;
9699                             buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n",
9700                                 (n == 0) ? "elem" : "    ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
9701                         }
9702                         ImGui::Selectable(buf, false);
9703                         if (fg_draw_list && ImGui::IsItemHovered())
9704                         {
9705                             ImDrawListFlags backup_flags = fg_draw_list->Flags;
9706                             fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines at is more readable for very large and thin triangles.
9707                             fg_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f);
9708                             fg_draw_list->Flags = backup_flags;
9709                         }
9710                     }
9711                 ImGui::TreePop();
9712             }
9713             ImGui::TreePop();
9714         }
9715 
9716         static void NodeColumns(const ImGuiColumns* columns)
9717         {
9718             if (!ImGui::TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
9719                 return;
9720             ImGui::BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX);
9721             for (int column_n = 0; column_n < columns->Columns.Size; column_n++)
9722                 ImGui::BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, GetColumnOffsetFromNorm(columns, columns->Columns[column_n].OffsetNorm));
9723             ImGui::TreePop();
9724         }
9725 
9726         static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label)
9727         {
9728             if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size))
9729                 return;
9730             for (int i = 0; i < windows.Size; i++)
9731                 Funcs::NodeWindow(windows[i], "Window");
9732             ImGui::TreePop();
9733         }
9734 
9735         static void NodeWindow(ImGuiWindow* window, const char* label)
9736         {
9737             if (window == NULL)
9738             {
9739                 ImGui::BulletText("%s: NULL", label);
9740                 return;
9741             }
9742             if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, (window->Active || window->WasActive), window))
9743                 return;
9744             ImGuiWindowFlags flags = window->Flags;
9745             NodeDrawList(window, window->DrawList, "DrawList");
9746             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);
9747             ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
9748                 (flags & ImGuiWindowFlags_ChildWindow)  ? "Child " : "",      (flags & ImGuiWindowFlags_Tooltip)     ? "Tooltip "   : "",  (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
9749                 (flags & ImGuiWindowFlags_Modal)        ? "Modal " : "",      (flags & ImGuiWindowFlags_ChildMenu)   ? "ChildMenu " : "",  (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
9750                 (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
9751             ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y);
9752             ImGui::BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
9753             ImGui::BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems);
9754             ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask);
9755             ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
9756             if (!window->NavRectRel[0].IsInverted())
9757                 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);
9758             else
9759                 ImGui::BulletText("NavRectRel[0]: <None>");
9760             if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
9761             if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow");
9762             if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
9763             if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
9764             {
9765                 for (int n = 0; n < window->ColumnsStorage.Size; n++)
9766                     NodeColumns(&window->ColumnsStorage[n]);
9767                 ImGui::TreePop();
9768             }
9769             NodeStorage(&window->StateStorage, "Storage");
9770             ImGui::TreePop();
9771         }
9772 
9773         static void NodeTabBar(ImGuiTabBar* tab_bar)
9774         {
9775             // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
9776             char buf[256];
9777             char* p = buf;
9778             const char* buf_end = buf + IM_ARRAYSIZE(buf);
9779             ImFormatString(p, buf_end - p, "TabBar (%d tabs)%s", tab_bar->Tabs.Size, (tab_bar->PrevFrameVisible < ImGui::GetFrameCount() - 2) ? " *Inactive*" : "");
9780             if (ImGui::TreeNode(tab_bar, "%s", buf))
9781             {
9782                 for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
9783                 {
9784                     const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
9785                     ImGui::PushID(tab);
9786                     if (ImGui::SmallButton("<")) { TabBarQueueChangeTabOrder(tab_bar, tab, -1); } ImGui::SameLine(0, 2);
9787                     if (ImGui::SmallButton(">")) { TabBarQueueChangeTabOrder(tab_bar, tab, +1); } ImGui::SameLine();
9788                     ImGui::Text("%02d%c Tab 0x%08X", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID);
9789                     ImGui::PopID();
9790                 }
9791                 ImGui::TreePop();
9792             }
9793         }
9794 
9795         static void NodeStorage(ImGuiStorage* storage, const char* label)
9796         {
9797             if (!ImGui::TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes()))
9798                 return;
9799             for (int n = 0; n < storage->Data.Size; n++)
9800             {
9801                 const ImGuiStorage::ImGuiStoragePair& p = storage->Data[n];
9802                 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.
9803             }
9804             ImGui::TreePop();
9805         }
9806     };
9807 
9808     Funcs::NodeWindows(g.Windows, "Windows");
9809     if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size))
9810     {
9811         for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++)
9812             Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList");
9813         ImGui::TreePop();
9814     }
9815 
9816     if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
9817     {
9818         for (int i = 0; i < g.OpenPopupStack.Size; i++)
9819         {
9820             ImGuiWindow* window = g.OpenPopupStack[i].Window;
9821             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" : "");
9822         }
9823         ImGui::TreePop();
9824     }
9825 
9826     if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.Data.Size))
9827     {
9828         for (int n = 0; n < g.TabBars.Data.Size; n++)
9829             Funcs::NodeTabBar(g.TabBars.GetByIndex(n));
9830         ImGui::TreePop();
9831     }
9832 
9833 #if 0
9834     if (ImGui::TreeNode("Docking"))
9835     {
9836         ImGui::TreePop();
9837     }
9838 #endif
9839 
9840 #if 0
9841     if (ImGui::TreeNode("Tables", "Tables (%d)", g.Tables.Data.Size))
9842     {
9843         ImGui::TreePop();
9844     }
9845 #endif
9846 
9847     if (ImGui::TreeNode("Internal state"))
9848     {
9849         const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
9850         ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
9851         ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
9852         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
9853         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]);
9854         ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
9855         ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
9856         ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
9857         ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
9858         ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
9859         ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
9860         ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
9861         ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
9862         ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
9863         ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
9864         ImGui::TreePop();
9865     }
9866 
9867     if (ImGui::TreeNode("Tools"))
9868     {
9869         // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
9870         if (ImGui::Button("Item Picker.."))
9871             ImGui::DebugStartItemPicker();
9872 
9873         ImGui::Checkbox("Show windows begin order", &show_windows_begin_order);
9874         ImGui::Checkbox("Show windows rectangles", &show_windows_rects);
9875         ImGui::SameLine();
9876         ImGui::SetNextItemWidth(ImGui::GetFontSize() * 12);
9877         show_windows_rects |= ImGui::Combo("##show_windows_rect_type", &show_windows_rect_type, wrt_rects_names, WRT_Count);
9878         if (show_windows_rects && g.NavWindow)
9879         {
9880             ImGui::BulletText("'%s':", g.NavWindow->Name);
9881             ImGui::Indent();
9882             for (int rect_n = 0; rect_n < WRT_Count; rect_n++)
9883             {
9884                 ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n);
9885                 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]);
9886             }
9887             ImGui::Unindent();
9888         }
9889         ImGui::Checkbox("Show clipping rectangle when hovering ImDrawCmd node", &show_drawcmd_clip_rects);
9890         ImGui::TreePop();
9891     }
9892 
9893     // Tool: Display windows Rectangles and Begin Order
9894     if (show_windows_rects || show_windows_begin_order)
9895     {
9896         for (int n = 0; n < g.Windows.Size; n++)
9897         {
9898             ImGuiWindow* window = g.Windows[n];
9899             if (!window->WasActive)
9900                 continue;
9901             ImDrawList* draw_list = GetForegroundDrawList(window);
9902             if (show_windows_rects)
9903             {
9904                 ImRect r = Funcs::GetWindowRect(window, show_windows_rect_type);
9905                 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
9906             }
9907             if (show_windows_begin_order && !(window->Flags & ImGuiWindowFlags_ChildWindow))
9908             {
9909                 char buf[32];
9910                 ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
9911                 float font_size = ImGui::GetFontSize();
9912                 draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
9913                 draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf);
9914             }
9915         }
9916     }
9917     ImGui::End();
9918 }
9919 
9920 #else
9921 
ShowMetricsWindow(bool *)9922 void ImGui::ShowMetricsWindow(bool*) { }
9923 
9924 #endif
9925 
9926 //-----------------------------------------------------------------------------
9927 
9928 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
9929 // 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.
9930 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
9931 #include "imgui_user.inl"
9932 #endif
9933 
9934 //-----------------------------------------------------------------------------
9935