1 // dear imgui, v1.74
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   - HOW A SIMPLE APPLICATION MAY LOOK LIKE (2 variations)
36   - 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 (Geomtry, String, Format, Hash, File functions)
49 // [SECTION] MISC HELPERS/UTILITIES (File functions)
50 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
51 // [SECTION] MISC HELPERS/UTILITIES (Color functions)
52 // [SECTION] ImGuiStorage
53 // [SECTION] ImGuiTextFilter
54 // [SECTION] ImGuiTextBuffer
55 // [SECTION] ImGuiListClipper
56 // [SECTION] RENDER HELPERS
57 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
58 // [SECTION] ERROR CHECKING
59 // [SECTION] SCROLLING
60 // [SECTION] TOOLTIPS
61 // [SECTION] POPUPS
62 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
63 // [SECTION] DRAG AND DROP
64 // [SECTION] LOGGING/CAPTURING
65 // [SECTION] SETTINGS
66 // [SECTION] PLATFORM DEPENDENT HELPERS
67 // [SECTION] METRICS/DEBUG WINDOW
68 
69 */
70 
71 //-----------------------------------------------------------------------------
72 // DOCUMENTATION
73 //-----------------------------------------------------------------------------
74 
75 /*
76 
77  MISSION STATEMENT
78  =================
79 
80  - Easy to use to create code-driven and data-driven tools.
81  - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
82  - Easy to hack and improve.
83  - Minimize screen real-estate usage.
84  - Minimize setup and maintenance.
85  - Minimize state storage on user side.
86  - Portable, minimize dependencies, run on target (consoles, phones, etc.).
87  - Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window,.
88    opening a tree node for the first time, etc. but a typical frame should not allocate anything).
89 
90  Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
91  - Doesn't look fancy, doesn't animate.
92  - Limited layout features, intricate layouts are typically crafted in code.
93 
94 
95  END-USER GUIDE
96  ==============
97 
98  - Double-click on title bar to collapse window.
99  - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin().
100  - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents).
101  - Click and drag on any empty space to move window.
102  - TAB/SHIFT+TAB to cycle through keyboard editable fields.
103  - CTRL+Click on a slider or drag box to input value as text.
104  - Use mouse wheel to scroll.
105  - Text editor:
106    - Hold SHIFT or use mouse to select text.
107    - CTRL+Left/Right to word jump.
108    - CTRL+Shift+Left/Right to select words.
109    - CTRL+A our Double-Click to select all.
110    - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
111    - CTRL+Z,CTRL+Y to undo/redo.
112    - ESCAPE to revert text to its original value.
113    - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
114    - Controls are automatically adjusted for OSX to match standard OSX text editing operations.
115  - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard.
116  - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://goo.gl/9LgVZW
117 
118 
119  PROGRAMMER GUIDE
120  ================
121 
122  READ FIRST
123  ----------
124  - Remember to read the FAQ (https://www.dearimgui.org/faq)
125  - 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
126    or destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, less bugs.
127  - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
128  - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
129  - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
130    You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links docs/README.md.
131  - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
132    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,
133    where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
134  - Our origin are on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
135  - This codebase is also optimized to yield decent performances with typical "Debug" builds settings.
136  - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).
137    If you get an assert, read the messages and comments around the assert.
138  - 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.
139  - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
140    See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
141    However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase.
142  - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!).
143 
144  HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
145  ----------------------------------------------
146  - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
147  - Or maintain your own branch where you have imconfig.h modified.
148  - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
149    If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
150    from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
151    likely be a comment about it. Please report any issue to the GitHub page!
152  - Try to keep your copy of dear imgui reasonably up to date.
153 
154  GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
155  ---------------------------------------------------------------
156  - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
157  - Add the Dear ImGui source files to your projects or using your preferred build system.
158    It is recommended you build and statically link the .cpp files as part of your project and not as shared library (DLL).
159  - 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.
160  - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
161  - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
162    Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
163    phases of your own application. All rendering information are stored into command-lists that you will retrieve after calling ImGui::Render().
164  - Refer to the bindings and demo applications in the examples/ folder for instruction on how to setup your code.
165  - 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.
166 
167  HOW A SIMPLE APPLICATION MAY LOOK LIKE
168  --------------------------------------
169  EXHIBIT 1: USING THE EXAMPLE BINDINGS (imgui_impl_XXX.cpp files from the examples/ folder).
170 
171      // Application init: create a dear imgui context, setup some options, load fonts
172      ImGui::CreateContext();
173      ImGuiIO& io = ImGui::GetIO();
174      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
175      // TODO: Fill optional fields of the io structure later.
176      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
177 
178      // Initialize helper Platform and Renderer bindings (here we are using imgui_impl_win32 and imgui_impl_dx11)
179      ImGui_ImplWin32_Init(hwnd);
180      ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
181 
182      // Application main loop
183      while (true)
184      {
185          // Feed inputs to dear imgui, start new frame
186          ImGui_ImplDX11_NewFrame();
187          ImGui_ImplWin32_NewFrame();
188          ImGui::NewFrame();
189 
190          // Any application code here
191          ImGui::Text("Hello, world!");
192 
193          // Render dear imgui into screen
194          ImGui::Render();
195          ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
196          g_pSwapChain->Present(1, 0);
197      }
198 
199      // Shutdown
200      ImGui_ImplDX11_Shutdown();
201      ImGui_ImplWin32_Shutdown();
202      ImGui::DestroyContext();
203 
204  EXHIBIT 2: IMPLEMENTING CUSTOM BINDING / CUSTOM ENGINE
205 
206      // Application init: create a dear imgui context, setup some options, load fonts
207      ImGui::CreateContext();
208      ImGuiIO& io = ImGui::GetIO();
209      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
210      // TODO: Fill optional fields of the io structure later.
211      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
212 
213      // Build and load the texture atlas into a texture
214      // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
215      int width, height;
216      unsigned char* pixels = NULL;
217      io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
218 
219      // At this point you've got the texture data and you need to upload that your your graphic system:
220      // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
221      // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID.
222      MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
223      io.Fonts->TexID = (void*)texture;
224 
225      // Application main loop
226      while (true)
227      {
228         // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
229         // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform bindings)
230         io.DeltaTime = 1.0f/60.0f;              // set the time elapsed since the previous frame (in seconds)
231         io.DisplaySize.x = 1920.0f;             // set the current display width
232         io.DisplaySize.y = 1280.0f;             // set the current display height here
233         io.MousePos = my_mouse_pos;             // set the mouse position
234         io.MouseDown[0] = my_mouse_buttons[0];  // set the mouse button states
235         io.MouseDown[1] = my_mouse_buttons[1];
236 
237         // Call NewFrame(), after this point you can use ImGui::* functions anytime
238         // (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use Dear ImGui everywhere)
239         ImGui::NewFrame();
240 
241         // Most of your application code here
242         ImGui::Text("Hello, world!");
243         MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
244         MyGameRender(); // may use any Dear ImGui functions as well!
245 
246         // Render dear imgui, swap buffers
247         // (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)
248         ImGui::EndFrame();
249         ImGui::Render();
250         ImDrawData* draw_data = ImGui::GetDrawData();
251         MyImGuiRenderFunction(draw_data);
252         SwapBuffers();
253      }
254 
255      // Shutdown
256      ImGui::DestroyContext();
257 
258  HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
259  ---------------------------------------------
260     void void MyImGuiRenderFunction(ImDrawData* draw_data)
261     {
262        // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
263        // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
264        // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
265        // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
266        for (int n = 0; n < draw_data->CmdListsCount; n++)
267        {
268           const ImDrawList* cmd_list = draw_data->CmdLists[n];
269           const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;  // vertex buffer generated by Dear ImGui
270           const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;   // index buffer generated by Dear ImGui
271           for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
272           {
273              const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
274              if (pcmd->UserCallback)
275              {
276                  pcmd->UserCallback(cmd_list, pcmd);
277              }
278              else
279              {
280                  // The texture for the draw call is specified by pcmd->TextureId.
281                  // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization.
282                  MyEngineBindTexture((MyTexture*)pcmd->TextureId);
283 
284                  // We are using scissoring to clip some objects. All low-level graphics API should supports it.
285                  // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
286                  //   (some elements visible outside their bounds) but you can fix that once everything else works!
287                  // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize)
288                  //   In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize.
289                  //   However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github),
290                  //   always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
291                  // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
292                  ImVec2 pos = draw_data->DisplayPos;
293                  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));
294 
295                  // Render 'pcmd->ElemCount/3' indexed triangles.
296                  // By default the indices ImDrawIdx are 16-bit, you can change them to 32-bit in imconfig.h if your engine doesn't support 16-bit indices.
297                  MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
298              }
299              idx_buffer += pcmd->ElemCount;
300           }
301        }
302     }
303 
304  - The examples/ folders contains many actual implementation of the pseudo-codes above.
305  - When calling NewFrame(), the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags are updated.
306    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
307    rest of your application. In every cases you need to pass on the inputs to Dear ImGui.
308  - Refer to the FAQ for more information. Amusingly, it is called a FAQ because people frequently run into the same issues!
309 
310  USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
311  ------------------------------------------
312  - The gamepad/keyboard navigation is fairly functional and keeps being improved.
313  - Gamepad support is particularly useful to use dear imgui on a console system (e.g. PS4, Switch, XB1) without a mouse!
314  - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787
315  - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
316  - Gamepad:
317     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
318     - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
319       Note that io.NavInputs[] is cleared by EndFrame().
320     - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
321          0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
322     - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
323       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.).
324     - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://goo.gl/9LgVZW.
325     - 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
326       to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
327  - Keyboard:
328     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
329       NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
330     - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
331       will be set. For more advanced uses, you may want to read from:
332        - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
333        - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
334        - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions.
335       Please reach out if you think the game vs navigation input sharing could be improved.
336  - Mouse:
337     - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
338     - 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.
339     - 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.
340       Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
341       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.
342       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.
343       (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!)
344       (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
345        to set a boolean to ignore your other external mouse positions until the external source is moved again.)
346 
347 
348  API BREAKING CHANGES
349  ====================
350 
351  Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
352  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.
353  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.
354  You can read releases logs https://github.com/ocornut/imgui/releases for more details.
355 
356  - 2019/11/21 (1.74) - ImFontAtlas::AddCustomRectRegular() now requires an ID larger than 0x110000 (instead of 0x10000) to conform with supporting Unicode planes 1-16 in a future update. ID below 0x110000 will now assert.
357  - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency.
358  - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency.
359  - 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. See docs/Changelog.txt or grep this log for details and new names, or see how they were implemented until 1.73.
360  - 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.
361                        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.
362                        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).
363                        If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you.
364  - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete).
365  - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete).
366  - 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.
367  - 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
368                        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.
369                        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.
370                        Please reach out if you are affected.
371  - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete).
372  - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c).
373  - 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.
374  - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).
375  - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).
376  - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).
377  - 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!
378  - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
379  - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
380  - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).
381  - 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.
382  - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
383  - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
384  - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
385  - 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.
386                        If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
387  - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
388  - 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.
389                        NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
390                        Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
391  - 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).
392  - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
393  - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
394  - 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.
395  - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
396  - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
397  - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
398  - 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.).
399                        old bindings will still work as is, however prefer using the separated bindings as they will be updated to support multi-viewports.
400                        when adopting new bindings follow the main.cpp code of your preferred examples/ folder to know which functions to call.
401                        in particular, note that old bindings called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function.
402  - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
403  - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
404  - 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.
405                        If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
406                        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.
407                        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.
408  - 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",
409                        consistent with other functions. Kept redirection functions (will obsolete).
410  - 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.
411  - 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).
412  - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
413  - 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.
414  - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
415  - 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.
416  - 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.
417  - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
418                        - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
419                        - removed Shutdown() function, as DestroyContext() serve this purpose.
420                        - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
421                        - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
422                        - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
423  - 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.
424  - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
425  - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
426  - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
427  - 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.
428  - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
429  - 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
430  - 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.
431  - 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.
432  - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
433  - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
434                      - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
435  - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
436  - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
437  - 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.
438  - 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.
439                        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.
440  - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
441  - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
442  - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
443  - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
444  - 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.
445  - 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.
446  - 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.
447                        removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
448                          IsItemHoveredRect()        --> IsItemHovered(ImGuiHoveredFlags_RectOnly)
449                          IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
450                          IsMouseHoveringWindow()    --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior]
451  - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
452  - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
453  - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).
454  - 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).
455  - 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)".
456  - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
457                      - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
458                      - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
459  - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
460  - 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.
461  - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type.
462  - 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.
463  - 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).
464  - 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).
465  - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
466  - 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.
467                      - 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.
468                      - 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))'
469  - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
470  - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
471  - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
472  - 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().
473  - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
474  - 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.
475  - 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.
476  - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
477                        If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you, otherwise if <1.0f you need tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
478                        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:
479                        ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col) { float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a; return ImVec4((win_bg_col.x * win_bg_col.w + title_bg_col.x) * k, (win_bg_col.y * win_bg_col.w + title_bg_col.y) * k, (win_bg_col.z * win_bg_col.w + title_bg_col.z) * k, new_a); }
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 docs/FONTS.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: 4996)     // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
793 #endif
794 
795 // Clang/GCC warnings with -Weverything
796 #if defined(__clang__)
797 #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!
798 #pragma clang diagnostic ignored "-Wold-style-cast"         // warning : use of old-style cast                              // yes, they are more terse.
799 #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.
800 #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.
801 #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.
802 #pragma clang diagnostic ignored "-Wglobal-constructors"    // warning : declaration requires a global destructor           // similar to above, not sure what the exact difference is.
803 #pragma clang diagnostic ignored "-Wsign-conversion"        // warning : implicit conversion changes signedness             //
804 #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.
805 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast"       // warning : cast to 'void *' from smaller integer type 'int'
806 #if __has_warning("-Wzero-as-null-pointer-constant")
807 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"  // warning : zero as null pointer constant              // some standard header variations use #define NULL 0
808 #endif
809 #if __has_warning("-Wdouble-promotion")
810 #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.
811 #endif
812 #elif defined(__GNUC__)
813 // We disable -Wpragmas because GCC doesn't provide an has_warning equivalent and some forks/patches may not following the warning/version association.
814 #pragma GCC diagnostic ignored "-Wpragmas"                  // warning: unknown option after '#pragma GCC diagnostic' kind
815 #pragma GCC diagnostic ignored "-Wunused-function"          // warning: 'xxxx' defined but not used
816 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"      // warning: cast to pointer from integer of different size
817 #pragma GCC diagnostic ignored "-Wformat"                   // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
818 #pragma GCC diagnostic ignored "-Wdouble-promotion"         // warning: implicit conversion from 'float' to 'double' when passing argument to function
819 #pragma GCC diagnostic ignored "-Wconversion"               // warning: conversion to 'xxxx' from 'xxxx' may alter its value
820 #pragma GCC diagnostic ignored "-Wformat-nonliteral"        // warning: format not a string literal, format string not checked
821 #pragma GCC diagnostic ignored "-Wstrict-overflow"          // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
822 #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
823 #endif
824 
825 // 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.
826 static const float NAV_WINDOWING_HIGHLIGHT_DELAY            = 0.20f;    // Time before the highlight and screen dimming starts fading in
827 static const float NAV_WINDOWING_LIST_APPEAR_DELAY          = 0.15f;    // Time before the window list starts to appear
828 
829 // Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by back-end)
830 static const float WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS = 4.0f;     // Extend outside and inside windows. Affect FindHoveredWindow().
831 static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f;    // Reduce visual noise by only highlighting the border after a certain time.
832 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.
833 
834 //-------------------------------------------------------------------------
835 // [SECTION] FORWARD DECLARATIONS
836 //-------------------------------------------------------------------------
837 
838 static void             SetCurrentWindow(ImGuiWindow* window);
839 static void             FindHoveredWindow();
840 static ImGuiWindow*     CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
841 static ImVec2           CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges);
842 
843 static void             AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
844 static void             AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
845 
846 static ImRect           GetViewportRect();
847 
848 // Settings
849 static void*            WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
850 static void             WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
851 static void             WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf);
852 
853 // Platform Dependents default implementation for IO functions
854 static const char*      GetClipboardTextFn_DefaultImpl(void* user_data);
855 static void             SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
856 static void             ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
857 
858 namespace ImGui
859 {
860 static bool             BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags);
861 
862 // Navigation
863 static void             NavUpdate();
864 static void             NavUpdateWindowing();
865 static void             NavUpdateWindowingOverlay();
866 static void             NavUpdateMoveResult();
867 static float            NavUpdatePageUpPageDown();
868 static inline void      NavUpdateAnyRequestFlag();
869 static bool             NavScoreItem(ImGuiNavMoveResult* result, ImRect cand);
870 static void             NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id);
871 static ImVec2           NavCalcPreferredRefPos();
872 static void             NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
873 static ImGuiWindow*     NavRestoreLastChildNavWindow(ImGuiWindow* window);
874 static int              FindWindowFocusIndex(ImGuiWindow* window);
875 
876 // Error Checking
877 static void             ErrorCheckEndFrame();
878 static void             ErrorCheckBeginEndCompareStacksSize(ImGuiWindow* window, bool write);
879 
880 // Misc
881 static void             UpdateMouseInputs();
882 static void             UpdateMouseWheel();
883 static bool             UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]);
884 static void             UpdateDebugToolItemPicker();
885 static void             RenderWindowOuterBorders(ImGuiWindow* window);
886 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);
887 static void             RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
888 
889 }
890 
891 //-----------------------------------------------------------------------------
892 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
893 //-----------------------------------------------------------------------------
894 
895 // Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
896 // ImGui::CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext().
897 // 1) Important: globals are not shared across DLL boundaries! If you use DLLs or any form of hot-reloading: you will need to call
898 //    SetCurrentContext() (with the pointer you got from CreateContext) from each unique static/DLL boundary, and after each hot-reloading.
899 //    In your debugger, add GImGui to your watch window and notice how its value changes depending on which location you are currently stepping into.
900 // 2) Important: Dear ImGui functions are not thread-safe because of this pointer.
901 //    If you want thread-safety to allow N threads to access N different contexts, you can:
902 //    - Change this variable to use thread local storage so each thread can refer to a different context, in imconfig.h:
903 //          struct ImGuiContext;
904 //          extern thread_local ImGuiContext* MyImGuiTLS;
905 //          #define GImGui MyImGuiTLS
906 //      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.
907 //    - Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
908 //    - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from different namespace.
909 #ifndef GImGui
910 ImGuiContext*   GImGui = NULL;
911 #endif
912 
913 // Memory Allocator functions. Use SetAllocatorFunctions() to change them.
914 // If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file.
915 // 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.
916 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
MallocWrapper(size_t size,void * user_data)917 static void*   MallocWrapper(size_t size, void* user_data)    { IM_UNUSED(user_data); return malloc(size); }
FreeWrapper(void * ptr,void * user_data)918 static void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); free(ptr); }
919 #else
MallocWrapper(size_t size,void * user_data)920 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)921 static void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }
922 #endif
923 
924 static void*  (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper;
925 static void   (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper;
926 static void*    GImAllocatorUserData = NULL;
927 
928 //-----------------------------------------------------------------------------
929 // [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
930 //-----------------------------------------------------------------------------
931 
ImGuiStyle()932 ImGuiStyle::ImGuiStyle()
933 {
934     Alpha                   = 1.0f;             // Global alpha applies to everything in ImGui
935     WindowPadding           = ImVec2(8,8);      // Padding within a window
936     WindowRounding          = 7.0f;             // Radius of window corners rounding. Set to 0.0f to have rectangular windows
937     WindowBorderSize        = 1.0f;             // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
938     WindowMinSize           = ImVec2(32,32);    // Minimum window size
939     WindowTitleAlign        = ImVec2(0.0f,0.5f);// Alignment for title bar text
940     WindowMenuButtonPosition= ImGuiDir_Left;    // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.
941     ChildRounding           = 0.0f;             // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
942     ChildBorderSize         = 1.0f;             // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
943     PopupRounding           = 0.0f;             // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
944     PopupBorderSize         = 1.0f;             // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
945     FramePadding            = ImVec2(4,3);      // Padding within a framed rectangle (used by most widgets)
946     FrameRounding           = 0.0f;             // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
947     FrameBorderSize         = 0.0f;             // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
948     ItemSpacing             = ImVec2(8,4);      // Horizontal and vertical spacing between widgets/lines
949     ItemInnerSpacing        = ImVec2(4,4);      // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
950     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!
951     IndentSpacing           = 21.0f;            // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
952     ColumnsMinSpacing       = 6.0f;             // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
953     ScrollbarSize           = 14.0f;            // Width of the vertical scrollbar, Height of the horizontal scrollbar
954     ScrollbarRounding       = 9.0f;             // Radius of grab corners rounding for scrollbar
955     GrabMinSize             = 10.0f;            // Minimum width/height of a grab box for slider/scrollbar
956     GrabRounding            = 0.0f;             // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
957     TabRounding             = 4.0f;             // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
958     TabBorderSize           = 0.0f;             // Thickness of border around tabs.
959     ColorButtonPosition     = ImGuiDir_Right;   // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
960     ButtonTextAlign         = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
961     SelectableTextAlign     = ImVec2(0.0f,0.0f);// Alignment of selectable text when button is larger than text.
962     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.
963     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.
964     MouseCursorScale        = 1.0f;             // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
965     AntiAliasedLines        = true;             // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
966     AntiAliasedFill         = true;             // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
967     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.
968 
969     // Default theme
970     ImGui::StyleColorsDark(this);
971 }
972 
973 // 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.
974 // 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)975 void ImGuiStyle::ScaleAllSizes(float scale_factor)
976 {
977     WindowPadding = ImFloor(WindowPadding * scale_factor);
978     WindowRounding = ImFloor(WindowRounding * scale_factor);
979     WindowMinSize = ImFloor(WindowMinSize * scale_factor);
980     ChildRounding = ImFloor(ChildRounding * scale_factor);
981     PopupRounding = ImFloor(PopupRounding * scale_factor);
982     FramePadding = ImFloor(FramePadding * scale_factor);
983     FrameRounding = ImFloor(FrameRounding * scale_factor);
984     ItemSpacing = ImFloor(ItemSpacing * scale_factor);
985     ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
986     TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
987     IndentSpacing = ImFloor(IndentSpacing * scale_factor);
988     ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
989     ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
990     ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
991     GrabMinSize = ImFloor(GrabMinSize * scale_factor);
992     GrabRounding = ImFloor(GrabRounding * scale_factor);
993     TabRounding = ImFloor(TabRounding * scale_factor);
994     DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
995     DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
996     MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
997 }
998 
ImGuiIO()999 ImGuiIO::ImGuiIO()
1000 {
1001     // Most fields are initialized with zero
1002     memset(this, 0, sizeof(*this));
1003 
1004     // Settings
1005     ConfigFlags = ImGuiConfigFlags_None;
1006     BackendFlags = ImGuiBackendFlags_None;
1007     DisplaySize = ImVec2(-1.0f, -1.0f);
1008     DeltaTime = 1.0f/60.0f;
1009     IniSavingRate = 5.0f;
1010     IniFilename = "imgui.ini";
1011     LogFilename = "imgui_log.txt";
1012     MouseDoubleClickTime = 0.30f;
1013     MouseDoubleClickMaxDist = 6.0f;
1014     for (int i = 0; i < ImGuiKey_COUNT; i++)
1015         KeyMap[i] = -1;
1016     KeyRepeatDelay = 0.275f;
1017     KeyRepeatRate = 0.050f;
1018     UserData = NULL;
1019 
1020     Fonts = NULL;
1021     FontGlobalScale = 1.0f;
1022     FontDefault = NULL;
1023     FontAllowUserScaling = false;
1024     DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1025 
1026     // Miscellaneous options
1027     MouseDrawCursor = false;
1028 #ifdef __APPLE__
1029     ConfigMacOSXBehaviors = true;  // Set Mac OS X style defaults based on __APPLE__ compile time flag
1030 #else
1031     ConfigMacOSXBehaviors = false;
1032 #endif
1033     ConfigInputTextCursorBlink = true;
1034     ConfigWindowsResizeFromEdges = true;
1035     ConfigWindowsMoveFromTitleBarOnly = false;
1036     ConfigWindowsMemoryCompactTimer = 60.0f;
1037 
1038     // Platform Functions
1039     BackendPlatformName = BackendRendererName = NULL;
1040     BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1041     GetClipboardTextFn = GetClipboardTextFn_DefaultImpl;   // Platform dependent default implementations
1042     SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
1043     ClipboardUserData = NULL;
1044     ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
1045     ImeWindowHandle = NULL;
1046 
1047 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1048     RenderDrawListsFn = NULL;
1049 #endif
1050 
1051     // Input (NB: we already have memset zero the entire structure!)
1052     MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1053     MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1054     MouseDragThreshold = 6.0f;
1055     for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1056     for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i]  = KeysDownDurationPrev[i] = -1.0f;
1057     for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
1058 }
1059 
1060 // Pass in translated ASCII characters for text input.
1061 // - with glfw you can get those from the callback set in glfwSetCharCallback()
1062 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(unsigned int c)1063 void ImGuiIO::AddInputCharacter(unsigned int c)
1064 {
1065     if (c > 0 && c <= IM_UNICODE_CODEPOINT_MAX)
1066         InputQueueCharacters.push_back((ImWchar)c);
1067 }
1068 
AddInputCharactersUTF8(const char * utf8_chars)1069 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1070 {
1071     while (*utf8_chars != 0)
1072     {
1073         unsigned int c = 0;
1074         utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
1075         if (c > 0 && c <= IM_UNICODE_CODEPOINT_MAX)
1076             InputQueueCharacters.push_back((ImWchar)c);
1077     }
1078 }
1079 
ClearInputCharacters()1080 void ImGuiIO::ClearInputCharacters()
1081 {
1082     InputQueueCharacters.resize(0);
1083 }
1084 
1085 //-----------------------------------------------------------------------------
1086 // [SECTION] MISC HELPERS/UTILITIES (Geometry, String, Format, Hash, File functions)
1087 //-----------------------------------------------------------------------------
1088 
ImLineClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & p)1089 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1090 {
1091     ImVec2 ap = p - a;
1092     ImVec2 ab_dir = b - a;
1093     float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1094     if (dot < 0.0f)
1095         return a;
1096     float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1097     if (dot > ab_len_sqr)
1098         return b;
1099     return a + ab_dir * dot / ab_len_sqr;
1100 }
1101 
ImTriangleContainsPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1102 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1103 {
1104     bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1105     bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1106     bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1107     return ((b1 == b2) && (b2 == b3));
1108 }
1109 
ImTriangleBarycentricCoords(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p,float & out_u,float & out_v,float & out_w)1110 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1111 {
1112     ImVec2 v0 = b - a;
1113     ImVec2 v1 = c - a;
1114     ImVec2 v2 = p - a;
1115     const float denom = v0.x * v1.y - v1.x * v0.y;
1116     out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1117     out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1118     out_u = 1.0f - out_v - out_w;
1119 }
1120 
ImTriangleClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1121 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1122 {
1123     ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1124     ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1125     ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1126     float dist2_ab = ImLengthSqr(p - proj_ab);
1127     float dist2_bc = ImLengthSqr(p - proj_bc);
1128     float dist2_ca = ImLengthSqr(p - proj_ca);
1129     float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1130     if (m == dist2_ab)
1131         return proj_ab;
1132     if (m == dist2_bc)
1133         return proj_bc;
1134     return proj_ca;
1135 }
1136 
1137 // 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)1138 int ImStricmp(const char* str1, const char* str2)
1139 {
1140     int d;
1141     while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
1142     return d;
1143 }
1144 
ImStrnicmp(const char * str1,const char * str2,size_t count)1145 int ImStrnicmp(const char* str1, const char* str2, size_t count)
1146 {
1147     int d = 0;
1148     while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
1149     return d;
1150 }
1151 
ImStrncpy(char * dst,const char * src,size_t count)1152 void ImStrncpy(char* dst, const char* src, size_t count)
1153 {
1154     if (count < 1)
1155         return;
1156     if (count > 1)
1157         strncpy(dst, src, count - 1);
1158     dst[count - 1] = 0;
1159 }
1160 
ImStrdup(const char * str)1161 char* ImStrdup(const char* str)
1162 {
1163     size_t len = strlen(str);
1164     void* buf = IM_ALLOC(len + 1);
1165     return (char*)memcpy(buf, (const void*)str, len + 1);
1166 }
1167 
ImStrdupcpy(char * dst,size_t * p_dst_size,const char * src)1168 char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
1169 {
1170     size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1;
1171     size_t src_size = strlen(src) + 1;
1172     if (dst_buf_size < src_size)
1173     {
1174         IM_FREE(dst);
1175         dst = (char*)IM_ALLOC(src_size);
1176         if (p_dst_size)
1177             *p_dst_size = src_size;
1178     }
1179     return (char*)memcpy(dst, (const void*)src, src_size);
1180 }
1181 
ImStrchrRange(const char * str,const char * str_end,char c)1182 const char* ImStrchrRange(const char* str, const char* str_end, char c)
1183 {
1184     const char* p = (const char*)memchr(str, (int)c, str_end - str);
1185     return p;
1186 }
1187 
ImStrlenW(const ImWchar * str)1188 int ImStrlenW(const ImWchar* str)
1189 {
1190     //return (int)wcslen((const wchar_t*)str);  // FIXME-OPT: Could use this when wchar_t are 16-bit
1191     int n = 0;
1192     while (*str++) n++;
1193     return n;
1194 }
1195 
1196 // Find end-of-line. Return pointer will point to either first \n, either str_end.
ImStreolRange(const char * str,const char * str_end)1197 const char* ImStreolRange(const char* str, const char* str_end)
1198 {
1199     const char* p = (const char*)memchr(str, '\n', str_end - str);
1200     return p ? p : str_end;
1201 }
1202 
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)1203 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
1204 {
1205     while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1206         buf_mid_line--;
1207     return buf_mid_line;
1208 }
1209 
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)1210 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1211 {
1212     if (!needle_end)
1213         needle_end = needle + strlen(needle);
1214 
1215     const char un0 = (char)toupper(*needle);
1216     while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1217     {
1218         if (toupper(*haystack) == un0)
1219         {
1220             const char* b = needle + 1;
1221             for (const char* a = haystack + 1; b < needle_end; a++, b++)
1222                 if (toupper(*a) != toupper(*b))
1223                     break;
1224             if (b == needle_end)
1225                 return haystack;
1226         }
1227         haystack++;
1228     }
1229     return NULL;
1230 }
1231 
1232 // 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)1233 void ImStrTrimBlanks(char* buf)
1234 {
1235     char* p = buf;
1236     while (p[0] == ' ' || p[0] == '\t')     // Leading blanks
1237         p++;
1238     char* p_start = p;
1239     while (*p != 0)                         // Find end of string
1240         p++;
1241     while (p > p_start && (p[-1] == ' ' || p[-1] == '\t'))  // Trailing blanks
1242         p--;
1243     if (p_start != buf)                     // Copy memory if we had leading blanks
1244         memmove(buf, p_start, p - p_start);
1245     buf[p - p_start] = 0;                   // Zero terminate
1246 }
1247 
ImStrSkipBlank(const char * str)1248 const char* ImStrSkipBlank(const char* str)
1249 {
1250     while (str[0] == ' ' || str[0] == '\t')
1251         str++;
1252     return str;
1253 }
1254 
1255 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1256 // 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.
1257 // B) When buf==NULL vsnprintf() will return the output size.
1258 #ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1259 
1260 // We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h)
1261 // You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1262 // and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are
1263 // designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.)
1264 //#define IMGUI_USE_STB_SPRINTF
1265 #ifdef IMGUI_USE_STB_SPRINTF
1266 #define STB_SPRINTF_IMPLEMENTATION
1267 #include "stb_sprintf.h"
1268 #endif
1269 
1270 #if defined(_MSC_VER) && !defined(vsnprintf)
1271 #define vsnprintf _vsnprintf
1272 #endif
1273 
ImFormatString(char * buf,size_t buf_size,const char * fmt,...)1274 int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1275 {
1276     va_list args;
1277     va_start(args, fmt);
1278 #ifdef IMGUI_USE_STB_SPRINTF
1279     int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1280 #else
1281     int w = vsnprintf(buf, buf_size, fmt, args);
1282 #endif
1283     va_end(args);
1284     if (buf == NULL)
1285         return w;
1286     if (w == -1 || w >= (int)buf_size)
1287         w = (int)buf_size - 1;
1288     buf[w] = 0;
1289     return w;
1290 }
1291 
ImFormatStringV(char * buf,size_t buf_size,const char * fmt,va_list args)1292 int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1293 {
1294 #ifdef IMGUI_USE_STB_SPRINTF
1295     int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1296 #else
1297     int w = vsnprintf(buf, buf_size, fmt, args);
1298 #endif
1299     if (buf == NULL)
1300         return w;
1301     if (w == -1 || w >= (int)buf_size)
1302         w = (int)buf_size - 1;
1303     buf[w] = 0;
1304     return w;
1305 }
1306 #endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1307 
1308 // CRC32 needs a 1KB lookup table (not cache friendly)
1309 // Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
1310 // - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
1311 static const ImU32 GCrc32LookupTable[256] =
1312 {
1313     0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
1314     0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
1315     0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
1316     0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
1317     0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
1318     0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
1319     0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
1320     0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
1321     0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
1322     0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
1323     0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
1324     0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
1325     0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
1326     0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
1327     0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
1328     0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
1329 };
1330 
1331 // Known size hash
1332 // It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
1333 // 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)1334 ImU32 ImHashData(const void* data_p, size_t data_size, ImU32 seed)
1335 {
1336     ImU32 crc = ~seed;
1337     const unsigned char* data = (const unsigned char*)data_p;
1338     const ImU32* crc32_lut = GCrc32LookupTable;
1339     while (data_size-- != 0)
1340         crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
1341     return ~crc;
1342 }
1343 
1344 // Zero-terminated string hash, with support for ### to reset back to seed value
1345 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1346 // Because this syntax is rarely used we are optimizing for the common case.
1347 // - If we reach ### in the string we discard the hash so far and reset to the seed.
1348 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
1349 // 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)1350 ImU32 ImHashStr(const char* data_p, size_t data_size, ImU32 seed)
1351 {
1352     seed = ~seed;
1353     ImU32 crc = seed;
1354     const unsigned char* data = (const unsigned char*)data_p;
1355     const ImU32* crc32_lut = GCrc32LookupTable;
1356     if (data_size != 0)
1357     {
1358         while (data_size-- != 0)
1359         {
1360             unsigned char c = *data++;
1361             if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')
1362                 crc = seed;
1363             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1364         }
1365     }
1366     else
1367     {
1368         while (unsigned char c = *data++)
1369         {
1370             if (c == '#' && data[0] == '#' && data[1] == '#')
1371                 crc = seed;
1372             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1373         }
1374     }
1375     return ~crc;
1376 }
1377 
1378 //-----------------------------------------------------------------------------
1379 // [SECTION] MISC HELPERS/UTILITIES (File functions)
1380 //-----------------------------------------------------------------------------
1381 
1382 // Default file functions
1383 #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
ImFileOpen(const char * filename,const char * mode)1384 ImFileHandle ImFileOpen(const char* filename, const char* mode)
1385 {
1386 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__)
1387     // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.
1388     const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1;
1389     const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1;
1390     ImVector<ImWchar> buf;
1391     buf.resize(filename_wsize + mode_wsize);
1392     ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL);
1393     ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL);
1394     return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]);
1395 #else
1396     return fopen(filename, mode);
1397 #endif
1398 }
1399 
1400 // We should in theory be using fseeko()/ftello() with off_t and _fseeki64()/_ftelli64() with __int64, waiting for the PR that does that in a very portable pre-C++11 zero-warnings way.
ImFileClose(ImFileHandle f)1401 bool    ImFileClose(ImFileHandle f)     { return fclose(f) == 0; }
ImFileGetSize(ImFileHandle f)1402 ImU64   ImFileGetSize(ImFileHandle f)   { long off = 0, sz = 0; return ((off = ftell(f)) != -1 && !fseek(f, 0, SEEK_END) && (sz = ftell(f)) != -1 && !fseek(f, off, SEEK_SET)) ? (ImU64)sz : (ImU64)-1; }
ImFileRead(void * data,ImU64 sz,ImU64 count,ImFileHandle f)1403 ImU64   ImFileRead(void* data, ImU64 sz, ImU64 count, ImFileHandle f)           { return fread(data, (size_t)sz, (size_t)count, f); }
ImFileWrite(const void * data,ImU64 sz,ImU64 count,ImFileHandle f)1404 ImU64   ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f)    { return fwrite(data, (size_t)sz, (size_t)count, f); }
1405 #endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
1406 
1407 // Helper: Load file content into memory
1408 // Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree()
ImFileLoadToMemory(const char * filename,const char * mode,size_t * out_file_size,int padding_bytes)1409 void*   ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes)
1410 {
1411     IM_ASSERT(filename && mode);
1412     if (out_file_size)
1413         *out_file_size = 0;
1414 
1415     ImFileHandle f;
1416     if ((f = ImFileOpen(filename, mode)) == NULL)
1417         return NULL;
1418 
1419     size_t file_size = (size_t)ImFileGetSize(f);
1420     if (file_size == (size_t)-1)
1421     {
1422         ImFileClose(f);
1423         return NULL;
1424     }
1425 
1426     void* file_data = IM_ALLOC(file_size + padding_bytes);
1427     if (file_data == NULL)
1428     {
1429         ImFileClose(f);
1430         return NULL;
1431     }
1432     if (ImFileRead(file_data, 1, file_size, f) != file_size)
1433     {
1434         ImFileClose(f);
1435         IM_FREE(file_data);
1436         return NULL;
1437     }
1438     if (padding_bytes > 0)
1439         memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
1440 
1441     ImFileClose(f);
1442     if (out_file_size)
1443         *out_file_size = file_size;
1444 
1445     return file_data;
1446 }
1447 
1448 //-----------------------------------------------------------------------------
1449 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
1450 //-----------------------------------------------------------------------------
1451 
1452 // Convert UTF-8 to 32-bit character, process single character input.
1453 // Based on stb_from_utf8() from github.com/nothings/stb/
1454 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)1455 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1456 {
1457     unsigned int c = (unsigned int)-1;
1458     const unsigned char* str = (const unsigned char*)in_text;
1459     if (!(*str & 0x80))
1460     {
1461         c = (unsigned int)(*str++);
1462         *out_char = c;
1463         return 1;
1464     }
1465     if ((*str & 0xe0) == 0xc0)
1466     {
1467         *out_char = IM_UNICODE_CODEPOINT_INVALID; // will be invalid but not end of string
1468         if (in_text_end && in_text_end - (const char*)str < 2) return 1;
1469         if (*str < 0xc2) return 2;
1470         c = (unsigned int)((*str++ & 0x1f) << 6);
1471         if ((*str & 0xc0) != 0x80) return 2;
1472         c += (*str++ & 0x3f);
1473         *out_char = c;
1474         return 2;
1475     }
1476     if ((*str & 0xf0) == 0xe0)
1477     {
1478         *out_char = IM_UNICODE_CODEPOINT_INVALID; // will be invalid but not end of string
1479         if (in_text_end && in_text_end - (const char*)str < 3) return 1;
1480         if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3;
1481         if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below
1482         c = (unsigned int)((*str++ & 0x0f) << 12);
1483         if ((*str & 0xc0) != 0x80) return 3;
1484         c += (unsigned int)((*str++ & 0x3f) << 6);
1485         if ((*str & 0xc0) != 0x80) return 3;
1486         c += (*str++ & 0x3f);
1487         *out_char = c;
1488         return 3;
1489     }
1490     if ((*str & 0xf8) == 0xf0)
1491     {
1492         *out_char = IM_UNICODE_CODEPOINT_INVALID; // will be invalid but not end of string
1493         if (in_text_end && in_text_end - (const char*)str < 4) return 1;
1494         if (*str > 0xf4) return 4;
1495         if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4;
1496         if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below
1497         c = (unsigned int)((*str++ & 0x07) << 18);
1498         if ((*str & 0xc0) != 0x80) return 4;
1499         c += (unsigned int)((*str++ & 0x3f) << 12);
1500         if ((*str & 0xc0) != 0x80) return 4;
1501         c += (unsigned int)((*str++ & 0x3f) << 6);
1502         if ((*str & 0xc0) != 0x80) return 4;
1503         c += (*str++ & 0x3f);
1504         // utf-8 encodings of values used in surrogate pairs are invalid
1505         if ((c & 0xFFFFF800) == 0xD800) return 4;
1506         *out_char = c;
1507         return 4;
1508     }
1509     *out_char = 0;
1510     return 0;
1511 }
1512 
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1513 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1514 {
1515     ImWchar* buf_out = buf;
1516     ImWchar* buf_end = buf + buf_size;
1517     while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1518     {
1519         unsigned int c;
1520         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1521         if (c == 0)
1522             break;
1523         if (c <= IM_UNICODE_CODEPOINT_MAX)    // FIXME: Losing characters that don't fit in 2 bytes
1524             *buf_out++ = (ImWchar)c;
1525     }
1526     *buf_out = 0;
1527     if (in_text_remaining)
1528         *in_text_remaining = in_text;
1529     return (int)(buf_out - buf);
1530 }
1531 
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1532 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1533 {
1534     int char_count = 0;
1535     while ((!in_text_end || in_text < in_text_end) && *in_text)
1536     {
1537         unsigned int c;
1538         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1539         if (c == 0)
1540             break;
1541         if (c <= IM_UNICODE_CODEPOINT_MAX)
1542             char_count++;
1543     }
1544     return char_count;
1545 }
1546 
1547 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8(char * buf,int buf_size,unsigned int c)1548 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1549 {
1550     if (c < 0x80)
1551     {
1552         buf[0] = (char)c;
1553         return 1;
1554     }
1555     if (c < 0x800)
1556     {
1557         if (buf_size < 2) return 0;
1558         buf[0] = (char)(0xc0 + (c >> 6));
1559         buf[1] = (char)(0x80 + (c & 0x3f));
1560         return 2;
1561     }
1562     if (c >= 0xdc00 && c < 0xe000)
1563     {
1564         return 0;
1565     }
1566     if (c >= 0xd800 && c < 0xdc00)
1567     {
1568         if (buf_size < 4) return 0;
1569         buf[0] = (char)(0xf0 + (c >> 18));
1570         buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1571         buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1572         buf[3] = (char)(0x80 + ((c ) & 0x3f));
1573         return 4;
1574     }
1575     //else if (c < 0x10000)
1576     {
1577         if (buf_size < 3) return 0;
1578         buf[0] = (char)(0xe0 + (c >> 12));
1579         buf[1] = (char)(0x80 + ((c>> 6) & 0x3f));
1580         buf[2] = (char)(0x80 + ((c ) & 0x3f));
1581         return 3;
1582     }
1583 }
1584 
1585 // Not optimal but we very rarely use this function.
ImTextCountUtf8BytesFromChar(const char * in_text,const char * in_text_end)1586 int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
1587 {
1588     unsigned int dummy = 0;
1589     return ImTextCharFromUtf8(&dummy, in_text, in_text_end);
1590 }
1591 
ImTextCountUtf8BytesFromChar(unsigned int c)1592 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1593 {
1594     if (c < 0x80) return 1;
1595     if (c < 0x800) return 2;
1596     if (c >= 0xdc00 && c < 0xe000) return 0;
1597     if (c >= 0xd800 && c < 0xdc00) return 4;
1598     return 3;
1599 }
1600 
ImTextStrToUtf8(char * buf,int buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1601 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1602 {
1603     char* buf_out = buf;
1604     const char* buf_end = buf + buf_size;
1605     while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1606     {
1607         unsigned int c = (unsigned int)(*in_text++);
1608         if (c < 0x80)
1609             *buf_out++ = (char)c;
1610         else
1611             buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c);
1612     }
1613     *buf_out = 0;
1614     return (int)(buf_out - buf);
1615 }
1616 
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1617 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1618 {
1619     int bytes_count = 0;
1620     while ((!in_text_end || in_text < in_text_end) && *in_text)
1621     {
1622         unsigned int c = (unsigned int)(*in_text++);
1623         if (c < 0x80)
1624             bytes_count++;
1625         else
1626             bytes_count += ImTextCountUtf8BytesFromChar(c);
1627     }
1628     return bytes_count;
1629 }
1630 
1631 //-----------------------------------------------------------------------------
1632 // [SECTION] MISC HELPERS/UTILTIES (Color functions)
1633 // Note: The Convert functions are early design which are not consistent with other API.
1634 //-----------------------------------------------------------------------------
1635 
ColorConvertU32ToFloat4(ImU32 in)1636 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1637 {
1638     float s = 1.0f/255.0f;
1639     return ImVec4(
1640         ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1641         ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1642         ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1643         ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1644 }
1645 
ColorConvertFloat4ToU32(const ImVec4 & in)1646 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1647 {
1648     ImU32 out;
1649     out  = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1650     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1651     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1652     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1653     return out;
1654 }
1655 
1656 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1657 // 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)1658 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1659 {
1660     float K = 0.f;
1661     if (g < b)
1662     {
1663         ImSwap(g, b);
1664         K = -1.f;
1665     }
1666     if (r < g)
1667     {
1668         ImSwap(r, g);
1669         K = -2.f / 6.f - K;
1670     }
1671 
1672     const float chroma = r - (g < b ? g : b);
1673     out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
1674     out_s = chroma / (r + 1e-20f);
1675     out_v = r;
1676 }
1677 
1678 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1679 // 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)1680 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1681 {
1682     if (s == 0.0f)
1683     {
1684         // gray
1685         out_r = out_g = out_b = v;
1686         return;
1687     }
1688 
1689     h = ImFmod(h, 1.0f) / (60.0f/360.0f);
1690     int   i = (int)h;
1691     float f = h - (float)i;
1692     float p = v * (1.0f - s);
1693     float q = v * (1.0f - s * f);
1694     float t = v * (1.0f - s * (1.0f - f));
1695 
1696     switch (i)
1697     {
1698     case 0: out_r = v; out_g = t; out_b = p; break;
1699     case 1: out_r = q; out_g = v; out_b = p; break;
1700     case 2: out_r = p; out_g = v; out_b = t; break;
1701     case 3: out_r = p; out_g = q; out_b = v; break;
1702     case 4: out_r = t; out_g = p; out_b = v; break;
1703     case 5: default: out_r = v; out_g = p; out_b = q; break;
1704     }
1705 }
1706 
GetColorU32(ImGuiCol idx,float alpha_mul)1707 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
1708 {
1709     ImGuiStyle& style = GImGui->Style;
1710     ImVec4 c = style.Colors[idx];
1711     c.w *= style.Alpha * alpha_mul;
1712     return ColorConvertFloat4ToU32(c);
1713 }
1714 
GetColorU32(const ImVec4 & col)1715 ImU32 ImGui::GetColorU32(const ImVec4& col)
1716 {
1717     ImGuiStyle& style = GImGui->Style;
1718     ImVec4 c = col;
1719     c.w *= style.Alpha;
1720     return ColorConvertFloat4ToU32(c);
1721 }
1722 
GetStyleColorVec4(ImGuiCol idx)1723 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
1724 {
1725     ImGuiStyle& style = GImGui->Style;
1726     return style.Colors[idx];
1727 }
1728 
GetColorU32(ImU32 col)1729 ImU32 ImGui::GetColorU32(ImU32 col)
1730 {
1731     float style_alpha = GImGui->Style.Alpha;
1732     if (style_alpha >= 1.0f)
1733         return col;
1734     ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
1735     a = (ImU32)(a * style_alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
1736     return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
1737 }
1738 
1739 //-----------------------------------------------------------------------------
1740 // [SECTION] ImGuiStorage
1741 // Helper: Key->value storage
1742 //-----------------------------------------------------------------------------
1743 
1744 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair> & data,ImGuiID key)1745 static ImGuiStorage::ImGuiStoragePair* LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair>& data, ImGuiID key)
1746 {
1747     ImGuiStorage::ImGuiStoragePair* first = data.Data;
1748     ImGuiStorage::ImGuiStoragePair* last = data.Data + data.Size;
1749     size_t count = (size_t)(last - first);
1750     while (count > 0)
1751     {
1752         size_t count2 = count >> 1;
1753         ImGuiStorage::ImGuiStoragePair* mid = first + count2;
1754         if (mid->key < key)
1755         {
1756             first = ++mid;
1757             count -= count2 + 1;
1758         }
1759         else
1760         {
1761             count = count2;
1762         }
1763     }
1764     return first;
1765 }
1766 
1767 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
BuildSortByKey()1768 void ImGuiStorage::BuildSortByKey()
1769 {
1770     struct StaticFunc
1771     {
1772         static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)
1773         {
1774             // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1775             if (((const ImGuiStoragePair*)lhs)->key > ((const ImGuiStoragePair*)rhs)->key) return +1;
1776             if (((const ImGuiStoragePair*)lhs)->key < ((const ImGuiStoragePair*)rhs)->key) return -1;
1777             return 0;
1778         }
1779     };
1780     if (Data.Size > 1)
1781         ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairCompareByID);
1782 }
1783 
GetInt(ImGuiID key,int default_val) const1784 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1785 {
1786     ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1787     if (it == Data.end() || it->key != key)
1788         return default_val;
1789     return it->val_i;
1790 }
1791 
GetBool(ImGuiID key,bool default_val) const1792 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1793 {
1794     return GetInt(key, default_val ? 1 : 0) != 0;
1795 }
1796 
GetFloat(ImGuiID key,float default_val) const1797 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1798 {
1799     ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1800     if (it == Data.end() || it->key != key)
1801         return default_val;
1802     return it->val_f;
1803 }
1804 
GetVoidPtr(ImGuiID key) const1805 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1806 {
1807     ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1808     if (it == Data.end() || it->key != key)
1809         return NULL;
1810     return it->val_p;
1811 }
1812 
1813 // 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)1814 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1815 {
1816     ImGuiStoragePair* it = LowerBound(Data, key);
1817     if (it == Data.end() || it->key != key)
1818         it = Data.insert(it, ImGuiStoragePair(key, default_val));
1819     return &it->val_i;
1820 }
1821 
GetBoolRef(ImGuiID key,bool default_val)1822 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1823 {
1824     return (bool*)GetIntRef(key, default_val ? 1 : 0);
1825 }
1826 
GetFloatRef(ImGuiID key,float default_val)1827 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1828 {
1829     ImGuiStoragePair* it = LowerBound(Data, key);
1830     if (it == Data.end() || it->key != key)
1831         it = Data.insert(it, ImGuiStoragePair(key, default_val));
1832     return &it->val_f;
1833 }
1834 
GetVoidPtrRef(ImGuiID key,void * default_val)1835 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1836 {
1837     ImGuiStoragePair* it = LowerBound(Data, key);
1838     if (it == Data.end() || it->key != key)
1839         it = Data.insert(it, ImGuiStoragePair(key, default_val));
1840     return &it->val_p;
1841 }
1842 
1843 // 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)1844 void ImGuiStorage::SetInt(ImGuiID key, int val)
1845 {
1846     ImGuiStoragePair* it = LowerBound(Data, key);
1847     if (it == Data.end() || it->key != key)
1848     {
1849         Data.insert(it, ImGuiStoragePair(key, val));
1850         return;
1851     }
1852     it->val_i = val;
1853 }
1854 
SetBool(ImGuiID key,bool val)1855 void ImGuiStorage::SetBool(ImGuiID key, bool val)
1856 {
1857     SetInt(key, val ? 1 : 0);
1858 }
1859 
SetFloat(ImGuiID key,float val)1860 void ImGuiStorage::SetFloat(ImGuiID key, float val)
1861 {
1862     ImGuiStoragePair* it = LowerBound(Data, key);
1863     if (it == Data.end() || it->key != key)
1864     {
1865         Data.insert(it, ImGuiStoragePair(key, val));
1866         return;
1867     }
1868     it->val_f = val;
1869 }
1870 
SetVoidPtr(ImGuiID key,void * val)1871 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1872 {
1873     ImGuiStoragePair* it = LowerBound(Data, key);
1874     if (it == Data.end() || it->key != key)
1875     {
1876         Data.insert(it, ImGuiStoragePair(key, val));
1877         return;
1878     }
1879     it->val_p = val;
1880 }
1881 
SetAllInt(int v)1882 void ImGuiStorage::SetAllInt(int v)
1883 {
1884     for (int i = 0; i < Data.Size; i++)
1885         Data[i].val_i = v;
1886 }
1887 
1888 //-----------------------------------------------------------------------------
1889 // [SECTION] ImGuiTextFilter
1890 //-----------------------------------------------------------------------------
1891 
1892 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)1893 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
1894 {
1895     if (default_filter)
1896     {
1897         ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
1898         Build();
1899     }
1900     else
1901     {
1902         InputBuf[0] = 0;
1903         CountGrep = 0;
1904     }
1905 }
1906 
Draw(const char * label,float width)1907 bool ImGuiTextFilter::Draw(const char* label, float width)
1908 {
1909     if (width != 0.0f)
1910         ImGui::SetNextItemWidth(width);
1911     bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
1912     if (value_changed)
1913         Build();
1914     return value_changed;
1915 }
1916 
split(char separator,ImVector<ImGuiTextRange> * out) const1917 void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const
1918 {
1919     out->resize(0);
1920     const char* wb = b;
1921     const char* we = wb;
1922     while (we < e)
1923     {
1924         if (*we == separator)
1925         {
1926             out->push_back(ImGuiTextRange(wb, we));
1927             wb = we + 1;
1928         }
1929         we++;
1930     }
1931     if (wb != we)
1932         out->push_back(ImGuiTextRange(wb, we));
1933 }
1934 
Build()1935 void ImGuiTextFilter::Build()
1936 {
1937     Filters.resize(0);
1938     ImGuiTextRange input_range(InputBuf, InputBuf+strlen(InputBuf));
1939     input_range.split(',', &Filters);
1940 
1941     CountGrep = 0;
1942     for (int i = 0; i != Filters.Size; i++)
1943     {
1944         ImGuiTextRange& f = Filters[i];
1945         while (f.b < f.e && ImCharIsBlankA(f.b[0]))
1946             f.b++;
1947         while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
1948             f.e--;
1949         if (f.empty())
1950             continue;
1951         if (Filters[i].b[0] != '-')
1952             CountGrep += 1;
1953     }
1954 }
1955 
PassFilter(const char * text,const char * text_end) const1956 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
1957 {
1958     if (Filters.empty())
1959         return true;
1960 
1961     if (text == NULL)
1962         text = "";
1963 
1964     for (int i = 0; i != Filters.Size; i++)
1965     {
1966         const ImGuiTextRange& f = Filters[i];
1967         if (f.empty())
1968             continue;
1969         if (f.b[0] == '-')
1970         {
1971             // Subtract
1972             if (ImStristr(text, text_end, f.b + 1, f.e) != NULL)
1973                 return false;
1974         }
1975         else
1976         {
1977             // Grep
1978             if (ImStristr(text, text_end, f.b, f.e) != NULL)
1979                 return true;
1980         }
1981     }
1982 
1983     // Implicit * grep
1984     if (CountGrep == 0)
1985         return true;
1986 
1987     return false;
1988 }
1989 
1990 //-----------------------------------------------------------------------------
1991 // [SECTION] ImGuiTextBuffer
1992 //-----------------------------------------------------------------------------
1993 
1994 // On some platform vsnprintf() takes va_list by reference and modifies it.
1995 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
1996 #ifndef va_copy
1997 #if defined(__GNUC__) || defined(__clang__)
1998 #define va_copy(dest, src) __builtin_va_copy(dest, src)
1999 #else
2000 #define va_copy(dest, src) (dest = src)
2001 #endif
2002 #endif
2003 
2004 char ImGuiTextBuffer::EmptyString[1] = { 0 };
2005 
append(const char * str,const char * str_end)2006 void ImGuiTextBuffer::append(const char* str, const char* str_end)
2007 {
2008     int len = str_end ? (int)(str_end - str) : (int)strlen(str);
2009 
2010     // Add zero-terminator the first time
2011     const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2012     const int needed_sz = write_off + len;
2013     if (write_off + len >= Buf.Capacity)
2014     {
2015         int new_capacity = Buf.Capacity * 2;
2016         Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2017     }
2018 
2019     Buf.resize(needed_sz);
2020     memcpy(&Buf[write_off - 1], str, (size_t)len);
2021     Buf[write_off - 1 + len] = 0;
2022 }
2023 
appendf(const char * fmt,...)2024 void ImGuiTextBuffer::appendf(const char* fmt, ...)
2025 {
2026     va_list args;
2027     va_start(args, fmt);
2028     appendfv(fmt, args);
2029     va_end(args);
2030 }
2031 
2032 // Helper: Text buffer for logging/accumulating text
appendfv(const char * fmt,va_list args)2033 void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
2034 {
2035     va_list args_copy;
2036     va_copy(args_copy, args);
2037 
2038     int len = ImFormatStringV(NULL, 0, fmt, args);         // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
2039     if (len <= 0)
2040     {
2041         va_end(args_copy);
2042         return;
2043     }
2044 
2045     // Add zero-terminator the first time
2046     const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2047     const int needed_sz = write_off + len;
2048     if (write_off + len >= Buf.Capacity)
2049     {
2050         int new_capacity = Buf.Capacity * 2;
2051         Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2052     }
2053 
2054     Buf.resize(needed_sz);
2055     ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
2056     va_end(args_copy);
2057 }
2058 
2059 //-----------------------------------------------------------------------------
2060 // [SECTION] ImGuiListClipper
2061 // This is currently not as flexible/powerful as it should be and really confusing/spaghetti, mostly because we changed
2062 // the API mid-way through development and support two ways to using the clipper, needs some rework (see TODO)
2063 //-----------------------------------------------------------------------------
2064 
2065 // Helper to calculate coarse clipping of large list of evenly sized items.
2066 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
2067 // 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)2068 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
2069 {
2070     ImGuiContext& g = *GImGui;
2071     ImGuiWindow* window = g.CurrentWindow;
2072     if (g.LogEnabled)
2073     {
2074         // If logging is active, do not perform any clipping
2075         *out_items_display_start = 0;
2076         *out_items_display_end = items_count;
2077         return;
2078     }
2079     if (window->SkipItems)
2080     {
2081         *out_items_display_start = *out_items_display_end = 0;
2082         return;
2083     }
2084 
2085     // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect
2086     ImRect unclipped_rect = window->ClipRect;
2087     if (g.NavMoveRequest)
2088         unclipped_rect.Add(g.NavScoringRectScreen);
2089 
2090     const ImVec2 pos = window->DC.CursorPos;
2091     int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
2092     int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
2093 
2094     // When performing a navigation request, ensure we have one item extra in the direction we are moving to
2095     if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up)
2096         start--;
2097     if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down)
2098         end++;
2099 
2100     start = ImClamp(start, 0, items_count);
2101     end = ImClamp(end + 1, start, items_count);
2102     *out_items_display_start = start;
2103     *out_items_display_end = end;
2104 }
2105 
SetCursorPosYAndSetupDummyPrevLine(float pos_y,float line_height)2106 static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height)
2107 {
2108     // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
2109     // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
2110     // The clipper should probably have a 4th step to display the last item in a regular manner.
2111     ImGuiContext& g = *GImGui;
2112     ImGuiWindow* window = g.CurrentWindow;
2113     window->DC.CursorPos.y = pos_y;
2114     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y);
2115     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.
2116     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.
2117     if (ImGuiColumns* columns = window->DC.CurrentColumns)
2118         columns->LineMinY = window->DC.CursorPos.y;                         // Setting this so that cell Y position are set properly
2119 }
2120 
2121 // Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
2122 // Use case B: Begin() called from constructor with items_height>0
2123 // 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)2124 void ImGuiListClipper::Begin(int count, float items_height)
2125 {
2126     ImGuiContext& g = *GImGui;
2127     ImGuiWindow* window = g.CurrentWindow;
2128 
2129     StartPosY = window->DC.CursorPos.y;
2130     ItemsHeight = items_height;
2131     ItemsCount = count;
2132     StepNo = 0;
2133     DisplayEnd = DisplayStart = -1;
2134     if (ItemsHeight > 0.0f)
2135     {
2136         ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display
2137         if (DisplayStart > 0)
2138             SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor
2139         StepNo = 2;
2140     }
2141 }
2142 
End()2143 void ImGuiListClipper::End()
2144 {
2145     if (ItemsCount < 0)
2146         return;
2147     // 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.
2148     if (ItemsCount < INT_MAX)
2149         SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor
2150     ItemsCount = -1;
2151     StepNo = 3;
2152 }
2153 
Step()2154 bool ImGuiListClipper::Step()
2155 {
2156     ImGuiContext& g = *GImGui;
2157     ImGuiWindow* window = g.CurrentWindow;
2158 
2159     if (ItemsCount == 0 || window->SkipItems)
2160     {
2161         ItemsCount = -1;
2162         return false;
2163     }
2164     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.
2165     {
2166         DisplayStart = 0;
2167         DisplayEnd = 1;
2168         StartPosY = window->DC.CursorPos.y;
2169         StepNo = 1;
2170         return true;
2171     }
2172     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.
2173     {
2174         if (ItemsCount == 1) { ItemsCount = -1; return false; }
2175         float items_height = window->DC.CursorPos.y - StartPosY;
2176         IM_ASSERT(items_height > 0.0f);   // If this triggers, it means Item 0 hasn't moved the cursor vertically
2177         Begin(ItemsCount - 1, items_height);
2178         DisplayStart++;
2179         DisplayEnd++;
2180         StepNo = 3;
2181         return true;
2182     }
2183     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.
2184     {
2185         IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0);
2186         StepNo = 3;
2187         return true;
2188     }
2189     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.
2190         End();
2191     return false;
2192 }
2193 
2194 //-----------------------------------------------------------------------------
2195 // [SECTION] RENDER HELPERS
2196 // Those (internal) functions are currently quite a legacy mess - their signature and behavior will change.
2197 // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: state.
2198 //-----------------------------------------------------------------------------
2199 
FindRenderedTextEnd(const char * text,const char * text_end)2200 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2201 {
2202     const char* text_display_end = text;
2203     if (!text_end)
2204         text_end = (const char*)-1;
2205 
2206     while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2207         text_display_end++;
2208     return text_display_end;
2209 }
2210 
2211 // Internal ImGui functions to render text
2212 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)2213 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2214 {
2215     ImGuiContext& g = *GImGui;
2216     ImGuiWindow* window = g.CurrentWindow;
2217 
2218     // Hide anything after a '##' string
2219     const char* text_display_end;
2220     if (hide_text_after_hash)
2221     {
2222         text_display_end = FindRenderedTextEnd(text, text_end);
2223     }
2224     else
2225     {
2226         if (!text_end)
2227             text_end = text + strlen(text); // FIXME-OPT
2228         text_display_end = text_end;
2229     }
2230 
2231     if (text != text_display_end)
2232     {
2233         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2234         if (g.LogEnabled)
2235             LogRenderedText(&pos, text, text_display_end);
2236     }
2237 }
2238 
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)2239 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2240 {
2241     ImGuiContext& g = *GImGui;
2242     ImGuiWindow* window = g.CurrentWindow;
2243 
2244     if (!text_end)
2245         text_end = text + strlen(text); // FIXME-OPT
2246 
2247     if (text != text_end)
2248     {
2249         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2250         if (g.LogEnabled)
2251             LogRenderedText(&pos, text, text_end);
2252     }
2253 }
2254 
2255 // Default clip_rect uses (pos_min,pos_max)
2256 // 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)2257 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)
2258 {
2259     // Perform CPU side clipping for single clipped element to avoid using scissor state
2260     ImVec2 pos = pos_min;
2261     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
2262 
2263     const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
2264     const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
2265     bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2266     if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
2267         need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2268 
2269     // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
2270     if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
2271     if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
2272 
2273     // Render
2274     if (need_clipping)
2275     {
2276         ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2277         draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2278     }
2279     else
2280     {
2281         draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2282     }
2283 }
2284 
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)2285 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)
2286 {
2287     // Hide anything after a '##' string
2288     const char* text_display_end = FindRenderedTextEnd(text, text_end);
2289     const int text_len = (int)(text_display_end - text);
2290     if (text_len == 0)
2291         return;
2292 
2293     ImGuiContext& g = *GImGui;
2294     ImGuiWindow* window = g.CurrentWindow;
2295     RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
2296     if (g.LogEnabled)
2297         LogRenderedText(&pos_min, text, text_display_end);
2298 }
2299 
2300 
2301 // Another overly complex function until we reorganize everything into a nice all-in-one helper.
2302 // 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.
2303 // 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)2304 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)
2305 {
2306     ImGuiContext& g = *GImGui;
2307     if (text_end_full == NULL)
2308         text_end_full = FindRenderedTextEnd(text);
2309     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);
2310 
2311     //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));
2312     //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));
2313     //draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255));
2314     // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
2315     if (text_size.x > pos_max.x - pos_min.x)
2316     {
2317         // Hello wo...
2318         // |       |   |
2319         // min   max   ellipsis_max
2320         //          <-> this is generally some padding value
2321 
2322         const ImFont* font = draw_list->_Data->Font;
2323         const float font_size = draw_list->_Data->FontSize;
2324         const char* text_end_ellipsis = NULL;
2325 
2326         ImWchar ellipsis_char = font->EllipsisChar;
2327         int ellipsis_char_count = 1;
2328         if (ellipsis_char == (ImWchar)-1)
2329         {
2330             ellipsis_char = (ImWchar)'.';
2331             ellipsis_char_count = 3;
2332         }
2333         const ImFontGlyph* glyph = font->FindGlyph(ellipsis_char);
2334 
2335         float ellipsis_glyph_width = glyph->X1;                 // Width of the glyph with no padding on either side
2336         float ellipsis_total_width = ellipsis_glyph_width;      // Full width of entire ellipsis
2337 
2338         if (ellipsis_char_count > 1)
2339         {
2340             // Full ellipsis size without free spacing after it.
2341             const float spacing_between_dots = 1.0f * (draw_list->_Data->FontSize / font->FontSize);
2342             ellipsis_glyph_width = glyph->X1 - glyph->X0 + spacing_between_dots;
2343             ellipsis_total_width = ellipsis_glyph_width * (float)ellipsis_char_count - spacing_between_dots;
2344         }
2345 
2346         // We can now claim the space between pos_max.x and ellipsis_max.x
2347         const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_total_width) - pos_min.x, 1.0f);
2348         float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
2349         if (text == text_end_ellipsis && text_end_ellipsis < text_end_full)
2350         {
2351             // Always display at least 1 character if there's no room for character + ellipsis
2352             text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full);
2353             text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x;
2354         }
2355         while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1]))
2356         {
2357             // 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)
2358             text_end_ellipsis--;
2359             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
2360         }
2361 
2362         // Render text, render ellipsis
2363         RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
2364         float ellipsis_x = pos_min.x + text_size_clipped_x;
2365         if (ellipsis_x + ellipsis_total_width <= ellipsis_max_x)
2366             for (int i = 0; i < ellipsis_char_count; i++)
2367             {
2368                 font->RenderChar(draw_list, font_size, ImVec2(ellipsis_x, pos_min.y), GetColorU32(ImGuiCol_Text), ellipsis_char);
2369                 ellipsis_x += ellipsis_glyph_width;
2370             }
2371     }
2372     else
2373     {
2374         RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));
2375     }
2376 
2377     if (g.LogEnabled)
2378         LogRenderedText(&pos_min, text, text_end_full);
2379 }
2380 
2381 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)2382 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2383 {
2384     ImGuiContext& g = *GImGui;
2385     ImGuiWindow* window = g.CurrentWindow;
2386     window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2387     const float border_size = g.Style.FrameBorderSize;
2388     if (border && border_size > 0.0f)
2389     {
2390         window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2391         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2392     }
2393 }
2394 
RenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding)2395 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
2396 {
2397     ImGuiContext& g = *GImGui;
2398     ImGuiWindow* window = g.CurrentWindow;
2399     const float border_size = g.Style.FrameBorderSize;
2400     if (border_size > 0.0f)
2401     {
2402         window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2403         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2404     }
2405 }
2406 
2407 // 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)2408 void ImGui::RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale)
2409 {
2410     const float h = draw_list->_Data->FontSize * 1.00f;
2411     float r = h * 0.40f * scale;
2412     ImVec2 center = pos + ImVec2(h * 0.50f, h * 0.50f * scale);
2413 
2414     ImVec2 a, b, c;
2415     switch (dir)
2416     {
2417     case ImGuiDir_Up:
2418     case ImGuiDir_Down:
2419         if (dir == ImGuiDir_Up) r = -r;
2420         a = ImVec2(+0.000f,+0.750f) * r;
2421         b = ImVec2(-0.866f,-0.750f) * r;
2422         c = ImVec2(+0.866f,-0.750f) * r;
2423         break;
2424     case ImGuiDir_Left:
2425     case ImGuiDir_Right:
2426         if (dir == ImGuiDir_Left) r = -r;
2427         a = ImVec2(+0.750f,+0.000f) * r;
2428         b = ImVec2(-0.750f,+0.866f) * r;
2429         c = ImVec2(-0.750f,-0.866f) * r;
2430         break;
2431     case ImGuiDir_None:
2432     case ImGuiDir_COUNT:
2433         IM_ASSERT(0);
2434         break;
2435     }
2436     draw_list->AddTriangleFilled(center + a, center + b, center + c, col);
2437 }
2438 
RenderBullet(ImDrawList * draw_list,ImVec2 pos,ImU32 col)2439 void ImGui::RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col)
2440 {
2441     draw_list->AddCircleFilled(pos, draw_list->_Data->FontSize * 0.20f, col, 8);
2442 }
2443 
RenderCheckMark(ImVec2 pos,ImU32 col,float sz)2444 void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz)
2445 {
2446     ImGuiContext& g = *GImGui;
2447     ImGuiWindow* window = g.CurrentWindow;
2448 
2449     float thickness = ImMax(sz / 5.0f, 1.0f);
2450     sz -= thickness*0.5f;
2451     pos += ImVec2(thickness*0.25f, thickness*0.25f);
2452 
2453     float third = sz / 3.0f;
2454     float bx = pos.x + third;
2455     float by = pos.y + sz - third*0.5f;
2456     window->DrawList->PathLineTo(ImVec2(bx - third, by - third));
2457     window->DrawList->PathLineTo(ImVec2(bx, by));
2458     window->DrawList->PathLineTo(ImVec2(bx + third*2, by - third*2));
2459     window->DrawList->PathStroke(col, false, thickness);
2460 }
2461 
RenderNavHighlight(const ImRect & bb,ImGuiID id,ImGuiNavHighlightFlags flags)2462 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
2463 {
2464     ImGuiContext& g = *GImGui;
2465     if (id != g.NavId)
2466         return;
2467     if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
2468         return;
2469     ImGuiWindow* window = g.CurrentWindow;
2470     if (window->DC.NavHideHighlightOneFrame)
2471         return;
2472 
2473     float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
2474     ImRect display_rect = bb;
2475     display_rect.ClipWith(window->ClipRect);
2476     if (flags & ImGuiNavHighlightFlags_TypeDefault)
2477     {
2478         const float THICKNESS = 2.0f;
2479         const float DISTANCE = 3.0f + THICKNESS * 0.5f;
2480         display_rect.Expand(ImVec2(DISTANCE,DISTANCE));
2481         bool fully_visible = window->ClipRect.Contains(display_rect);
2482         if (!fully_visible)
2483             window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
2484         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);
2485         if (!fully_visible)
2486             window->DrawList->PopClipRect();
2487     }
2488     if (flags & ImGuiNavHighlightFlags_TypeThin)
2489     {
2490         window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f);
2491     }
2492 }
2493 
2494 //-----------------------------------------------------------------------------
2495 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
2496 //-----------------------------------------------------------------------------
2497 
2498 // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
ImGuiWindow(ImGuiContext * context,const char * name)2499 ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
2500     : DrawListInst(&context->DrawListSharedData)
2501 {
2502     Name = ImStrdup(name);
2503     ID = ImHashStr(name);
2504     IDStack.push_back(ID);
2505     Flags = ImGuiWindowFlags_None;
2506     Pos = ImVec2(0.0f, 0.0f);
2507     Size = SizeFull = ImVec2(0.0f, 0.0f);
2508     ContentSize = ContentSizeExplicit = ImVec2(0.0f, 0.0f);
2509     WindowPadding = ImVec2(0.0f, 0.0f);
2510     WindowRounding = 0.0f;
2511     WindowBorderSize = 0.0f;
2512     NameBufLen = (int)strlen(name) + 1;
2513     MoveId = GetID("#MOVE");
2514     ChildId = 0;
2515     Scroll = ImVec2(0.0f, 0.0f);
2516     ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
2517     ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
2518     ScrollbarSizes = ImVec2(0.0f, 0.0f);
2519     ScrollbarX = ScrollbarY = false;
2520     Active = WasActive = false;
2521     WriteAccessed = false;
2522     Collapsed = false;
2523     WantCollapseToggle = false;
2524     SkipItems = false;
2525     Appearing = false;
2526     Hidden = false;
2527     HasCloseButton = false;
2528     ResizeBorderHeld = -1;
2529     BeginCount = 0;
2530     BeginOrderWithinParent = -1;
2531     BeginOrderWithinContext = -1;
2532     PopupId = 0;
2533     AutoFitFramesX = AutoFitFramesY = -1;
2534     AutoFitChildAxises = 0x00;
2535     AutoFitOnlyGrows = false;
2536     AutoPosLastDirection = ImGuiDir_None;
2537     HiddenFramesCanSkipItems = HiddenFramesCannotSkipItems = 0;
2538     SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
2539     SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
2540 
2541     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.
2542 
2543     LastFrameActive = -1;
2544     LastTimeActive = -1.0f;
2545     ItemWidthDefault = 0.0f;
2546     FontWindowScale = 1.0f;
2547     SettingsOffset = -1;
2548 
2549     DrawList = &DrawListInst;
2550     DrawList->_OwnerName = Name;
2551     ParentWindow = NULL;
2552     RootWindow = NULL;
2553     RootWindowForTitleBarHighlight = NULL;
2554     RootWindowForNav = NULL;
2555 
2556     NavLastIds[0] = NavLastIds[1] = 0;
2557     NavRectRel[0] = NavRectRel[1] = ImRect();
2558     NavLastChildNavWindow = NULL;
2559 
2560     MemoryCompacted = false;
2561     MemoryDrawListIdxCapacity = MemoryDrawListVtxCapacity = 0;
2562 }
2563 
~ImGuiWindow()2564 ImGuiWindow::~ImGuiWindow()
2565 {
2566     IM_ASSERT(DrawList == &DrawListInst);
2567     IM_DELETE(Name);
2568     for (int i = 0; i != ColumnsStorage.Size; i++)
2569         ColumnsStorage[i].~ImGuiColumns();
2570 }
2571 
GetID(const char * str,const char * str_end)2572 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
2573 {
2574     ImGuiID seed = IDStack.back();
2575     ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2576     ImGui::KeepAliveID(id);
2577     return id;
2578 }
2579 
GetID(const void * ptr)2580 ImGuiID ImGuiWindow::GetID(const void* ptr)
2581 {
2582     ImGuiID seed = IDStack.back();
2583     ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
2584     ImGui::KeepAliveID(id);
2585     return id;
2586 }
2587 
GetID(int n)2588 ImGuiID ImGuiWindow::GetID(int n)
2589 {
2590     ImGuiID seed = IDStack.back();
2591     ImGuiID id = ImHashData(&n, sizeof(n), seed);
2592     ImGui::KeepAliveID(id);
2593     return id;
2594 }
2595 
GetIDNoKeepAlive(const char * str,const char * str_end)2596 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
2597 {
2598     ImGuiID seed = IDStack.back();
2599     return ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2600 }
2601 
GetIDNoKeepAlive(const void * ptr)2602 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
2603 {
2604     ImGuiID seed = IDStack.back();
2605     return ImHashData(&ptr, sizeof(void*), seed);
2606 }
2607 
GetIDNoKeepAlive(int n)2608 ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n)
2609 {
2610     ImGuiID seed = IDStack.back();
2611     return ImHashData(&n, sizeof(n), seed);
2612 }
2613 
2614 // This is only used in rare/specific situations to manufacture an ID out of nowhere.
GetIDFromRectangle(const ImRect & r_abs)2615 ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
2616 {
2617     ImGuiID seed = IDStack.back();
2618     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) };
2619     ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
2620     ImGui::KeepAliveID(id);
2621     return id;
2622 }
2623 
SetCurrentWindow(ImGuiWindow * window)2624 static void SetCurrentWindow(ImGuiWindow* window)
2625 {
2626     ImGuiContext& g = *GImGui;
2627     g.CurrentWindow = window;
2628     if (window)
2629         g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
2630 }
2631 
2632 // Free up/compact internal window buffers, we can use this when a window becomes unused.
2633 // This is currently unused by the library, but you may call this yourself for easy GC.
2634 // Not freed:
2635 // - ImGuiWindow, ImGuiWindowSettings, Name
2636 // - StateStorage, ColumnsStorage (may hold useful data)
2637 // This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.
GcCompactTransientWindowBuffers(ImGuiWindow * window)2638 void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window)
2639 {
2640     window->MemoryCompacted = true;
2641     window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;
2642     window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;
2643     window->IDStack.clear();
2644     window->DrawList->ClearFreeMemory();
2645     window->DC.ChildWindows.clear();
2646     window->DC.ItemFlagsStack.clear();
2647     window->DC.ItemWidthStack.clear();
2648     window->DC.TextWrapPosStack.clear();
2649     window->DC.GroupStack.clear();
2650 }
2651 
GcAwakeTransientWindowBuffers(ImGuiWindow * window)2652 void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window)
2653 {
2654     // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.
2655     // The other buffers tends to amortize much faster.
2656     window->MemoryCompacted = false;
2657     window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity);
2658     window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity);
2659     window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0;
2660 }
2661 
SetNavID(ImGuiID id,int nav_layer)2662 void ImGui::SetNavID(ImGuiID id, int nav_layer)
2663 {
2664     ImGuiContext& g = *GImGui;
2665     IM_ASSERT(g.NavWindow);
2666     IM_ASSERT(nav_layer == 0 || nav_layer == 1);
2667     g.NavId = id;
2668     g.NavWindow->NavLastIds[nav_layer] = id;
2669 }
2670 
SetNavIDWithRectRel(ImGuiID id,int nav_layer,const ImRect & rect_rel)2671 void ImGui::SetNavIDWithRectRel(ImGuiID id, int nav_layer, const ImRect& rect_rel)
2672 {
2673     ImGuiContext& g = *GImGui;
2674     SetNavID(id, nav_layer);
2675     g.NavWindow->NavRectRel[nav_layer] = rect_rel;
2676     g.NavMousePosDirty = true;
2677     g.NavDisableHighlight = false;
2678     g.NavDisableMouseHover = true;
2679 }
2680 
SetActiveID(ImGuiID id,ImGuiWindow * window)2681 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
2682 {
2683     ImGuiContext& g = *GImGui;
2684     g.ActiveIdIsJustActivated = (g.ActiveId != id);
2685     if (g.ActiveIdIsJustActivated)
2686     {
2687         g.ActiveIdTimer = 0.0f;
2688         g.ActiveIdHasBeenPressedBefore = false;
2689         g.ActiveIdHasBeenEditedBefore = false;
2690         if (id != 0)
2691         {
2692             g.LastActiveId = id;
2693             g.LastActiveIdTimer = 0.0f;
2694         }
2695     }
2696     g.ActiveId = id;
2697     g.ActiveIdAllowOverlap = false;
2698     g.ActiveIdWindow = window;
2699     g.ActiveIdHasBeenEditedThisFrame = false;
2700     if (id)
2701     {
2702         g.ActiveIdIsAlive = id;
2703         g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
2704     }
2705 
2706     // Clear declaration of inputs claimed by the widget
2707     // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
2708     g.ActiveIdUsingNavDirMask = 0x00;
2709     g.ActiveIdUsingNavInputMask = 0x00;
2710     g.ActiveIdUsingKeyInputMask = 0x00;
2711 }
2712 
2713 // FIXME-NAV: The existence of SetNavID/SetNavIDWithRectRel/SetFocusID is incredibly messy and confusing and needs some explanation or refactoring.
SetFocusID(ImGuiID id,ImGuiWindow * window)2714 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
2715 {
2716     ImGuiContext& g = *GImGui;
2717     IM_ASSERT(id != 0);
2718 
2719     // Assume that SetFocusID() is called in the context where its NavLayer is the current layer, which is the case everywhere we call it.
2720     const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
2721     if (g.NavWindow != window)
2722         g.NavInitRequest = false;
2723     g.NavId = id;
2724     g.NavWindow = window;
2725     g.NavLayer = nav_layer;
2726     window->NavLastIds[nav_layer] = id;
2727     if (window->DC.LastItemId == id)
2728         window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos);
2729 
2730     if (g.ActiveIdSource == ImGuiInputSource_Nav)
2731         g.NavDisableMouseHover = true;
2732     else
2733         g.NavDisableHighlight = true;
2734 }
2735 
ClearActiveID()2736 void ImGui::ClearActiveID()
2737 {
2738     SetActiveID(0, NULL);
2739 }
2740 
SetHoveredID(ImGuiID id)2741 void ImGui::SetHoveredID(ImGuiID id)
2742 {
2743     ImGuiContext& g = *GImGui;
2744     g.HoveredId = id;
2745     g.HoveredIdAllowOverlap = false;
2746     if (id != 0 && g.HoveredIdPreviousFrame != id)
2747         g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
2748 }
2749 
GetHoveredID()2750 ImGuiID ImGui::GetHoveredID()
2751 {
2752     ImGuiContext& g = *GImGui;
2753     return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
2754 }
2755 
KeepAliveID(ImGuiID id)2756 void ImGui::KeepAliveID(ImGuiID id)
2757 {
2758     ImGuiContext& g = *GImGui;
2759     if (g.ActiveId == id)
2760         g.ActiveIdIsAlive = id;
2761     if (g.ActiveIdPreviousFrame == id)
2762         g.ActiveIdPreviousFrameIsAlive = true;
2763 }
2764 
MarkItemEdited(ImGuiID id)2765 void ImGui::MarkItemEdited(ImGuiID id)
2766 {
2767     // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
2768     // 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.
2769     ImGuiContext& g = *GImGui;
2770     IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive);
2771     IM_UNUSED(id); // Avoid unused variable warnings when asserts are compiled out.
2772     //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
2773     g.ActiveIdHasBeenEditedThisFrame = true;
2774     g.ActiveIdHasBeenEditedBefore = true;
2775     g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
2776 }
2777 
IsWindowContentHoverable(ImGuiWindow * window,ImGuiHoveredFlags flags)2778 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
2779 {
2780     // An active popup disable hovering on other windows (apart from its own children)
2781     // FIXME-OPT: This could be cached/stored within the window.
2782     ImGuiContext& g = *GImGui;
2783     if (g.NavWindow)
2784         if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
2785             if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
2786             {
2787                 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
2788                 // NB: The order of those two tests is important because Modal windows are also Popups.
2789                 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
2790                     return false;
2791                 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
2792                     return false;
2793             }
2794 
2795     return true;
2796 }
2797 
2798 // Advance cursor given item size for layout.
ItemSize(const ImVec2 & size,float text_baseline_y)2799 void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
2800 {
2801     ImGuiContext& g = *GImGui;
2802     ImGuiWindow* window = g.CurrentWindow;
2803     if (window->SkipItems)
2804         return;
2805 
2806     // We increase the height in this function to accommodate for baseline offset.
2807     // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
2808     // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
2809     const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
2810     const float line_height = ImMax(window->DC.CurrLineSize.y, size.y + offset_to_match_baseline_y);
2811 
2812     // Always align ourselves on pixel boundaries
2813     //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]
2814     window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
2815     window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y;
2816     window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);    // Next line
2817     window->DC.CursorPos.y = IM_FLOOR(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y);        // Next line
2818     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
2819     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
2820     //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
2821 
2822     window->DC.PrevLineSize.y = line_height;
2823     window->DC.CurrLineSize.y = 0.0f;
2824     window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
2825     window->DC.CurrLineTextBaseOffset = 0.0f;
2826 
2827     // Horizontal layout mode
2828     if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
2829         SameLine();
2830 }
2831 
ItemSize(const ImRect & bb,float text_baseline_y)2832 void ImGui::ItemSize(const ImRect& bb, float text_baseline_y)
2833 {
2834     ItemSize(bb.GetSize(), text_baseline_y);
2835 }
2836 
2837 // Declare item bounding box for clipping and interaction.
2838 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
2839 // 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)2840 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg)
2841 {
2842     ImGuiContext& g = *GImGui;
2843     ImGuiWindow* window = g.CurrentWindow;
2844 
2845     if (id != 0)
2846     {
2847         // Navigation processing runs prior to clipping early-out
2848         //  (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
2849         //  (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
2850         //      unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
2851         //      thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
2852         //      We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
2853         //      to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
2854         // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
2855         // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
2856         window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask;
2857         if (g.NavId == id || g.NavAnyRequest)
2858             if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
2859                 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
2860                     NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
2861 
2862         // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd()
2863 #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
2864         if (id == g.DebugItemPickerBreakID)
2865         {
2866             IM_DEBUG_BREAK();
2867             g.DebugItemPickerBreakID = 0;
2868         }
2869 #endif
2870     }
2871 
2872     window->DC.LastItemId = id;
2873     window->DC.LastItemRect = bb;
2874     window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None;
2875     g.NextItemData.Flags = ImGuiNextItemDataFlags_None;
2876 
2877 #ifdef IMGUI_ENABLE_TEST_ENGINE
2878     if (id != 0)
2879         IMGUI_TEST_ENGINE_ITEM_ADD(nav_bb_arg ? *nav_bb_arg : bb, id);
2880 #endif
2881 
2882     // Clipping test
2883     const bool is_clipped = IsClippedEx(bb, id, false);
2884     if (is_clipped)
2885         return false;
2886     //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
2887 
2888     // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
2889     if (IsMouseHoveringRect(bb.Min, bb.Max))
2890         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect;
2891     return true;
2892 }
2893 
2894 // This is roughly matching the behavior of internal-facing ItemHoverable()
2895 // - 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()
2896 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
IsItemHovered(ImGuiHoveredFlags flags)2897 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
2898 {
2899     ImGuiContext& g = *GImGui;
2900     ImGuiWindow* window = g.CurrentWindow;
2901     if (g.NavDisableMouseHover && !g.NavDisableHighlight)
2902         return IsItemFocused();
2903 
2904     // Test for bounding box overlap, as updated as ItemAdd()
2905     if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
2906         return false;
2907     IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0);   // Flags not supported by this function
2908 
2909     // Test if we are hovering the right window (our window could be behind another window)
2910     // [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.
2911     // 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.
2912     //if (g.HoveredWindow != window)
2913     //    return false;
2914     if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
2915         return false;
2916 
2917     // Test if another item is active (e.g. being dragged)
2918     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
2919         if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
2920             return false;
2921 
2922     // Test if interactions on this window are blocked by an active popup or modal.
2923     // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
2924     if (!IsWindowContentHoverable(window, flags))
2925         return false;
2926 
2927     // Test if the item is disabled
2928     if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
2929         return false;
2930 
2931     // Special handling for the dummy item after Begin() which represent the title bar or tab.
2932     // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
2933     if (window->DC.LastItemId == window->MoveId && window->WriteAccessed)
2934         return false;
2935     return true;
2936 }
2937 
2938 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
ItemHoverable(const ImRect & bb,ImGuiID id)2939 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
2940 {
2941     ImGuiContext& g = *GImGui;
2942     if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
2943         return false;
2944 
2945     ImGuiWindow* window = g.CurrentWindow;
2946     if (g.HoveredWindow != window)
2947         return false;
2948     if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
2949         return false;
2950     if (!IsMouseHoveringRect(bb.Min, bb.Max))
2951         return false;
2952     if (g.NavDisableMouseHover || !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
2953         return false;
2954     if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
2955         return false;
2956 
2957     SetHoveredID(id);
2958 
2959     // [DEBUG] Item Picker tool!
2960     // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making
2961     // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered
2962     // items if we perform the test in ItemAdd(), but that would incur a small runtime cost.
2963     // #define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX in imconfig.h if you want this check to also be performed in ItemAdd().
2964     if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
2965         GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
2966     if (g.DebugItemPickerBreakID == id)
2967         IM_DEBUG_BREAK();
2968 
2969     return true;
2970 }
2971 
IsClippedEx(const ImRect & bb,ImGuiID id,bool clip_even_when_logged)2972 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
2973 {
2974     ImGuiContext& g = *GImGui;
2975     ImGuiWindow* window = g.CurrentWindow;
2976     if (!bb.Overlaps(window->ClipRect))
2977         if (id == 0 || id != g.ActiveId)
2978             if (clip_even_when_logged || !g.LogEnabled)
2979                 return true;
2980     return false;
2981 }
2982 
2983 // Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out.
FocusableItemRegister(ImGuiWindow * window,ImGuiID id)2984 bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id)
2985 {
2986     ImGuiContext& g = *GImGui;
2987 
2988     // Increment counters
2989     const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0;
2990     window->DC.FocusCounterAll++;
2991     if (is_tab_stop)
2992         window->DC.FocusCounterTab++;
2993 
2994     // Process TAB/Shift-TAB to tab *OUT* of the currently focused item.
2995     // (Note that we can always TAB out of a widget that doesn't allow tabbing in)
2996     if (g.ActiveId == id && g.FocusTabPressed && !IsActiveIdUsingKey(ImGuiKey_Tab) && g.FocusRequestNextWindow == NULL)
2997     {
2998         g.FocusRequestNextWindow = window;
2999         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.
3000     }
3001 
3002     // Handle focus requests
3003     if (g.FocusRequestCurrWindow == window)
3004     {
3005         if (window->DC.FocusCounterAll == g.FocusRequestCurrCounterAll)
3006             return true;
3007         if (is_tab_stop && window->DC.FocusCounterTab == g.FocusRequestCurrCounterTab)
3008         {
3009             g.NavJustTabbedId = id;
3010             return true;
3011         }
3012 
3013         // If another item is about to be focused, we clear our own active id
3014         if (g.ActiveId == id)
3015             ClearActiveID();
3016     }
3017 
3018     return false;
3019 }
3020 
FocusableItemUnregister(ImGuiWindow * window)3021 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
3022 {
3023     window->DC.FocusCounterAll--;
3024     window->DC.FocusCounterTab--;
3025 }
3026 
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)3027 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
3028 {
3029     if (wrap_pos_x < 0.0f)
3030         return 0.0f;
3031 
3032     ImGuiWindow* window = GImGui->CurrentWindow;
3033     if (wrap_pos_x == 0.0f)
3034         wrap_pos_x = window->WorkRect.Max.x;
3035     else if (wrap_pos_x > 0.0f)
3036         wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
3037 
3038     return ImMax(wrap_pos_x - pos.x, 1.0f);
3039 }
3040 
3041 // IM_ALLOC() == ImGui::MemAlloc()
MemAlloc(size_t size)3042 void* ImGui::MemAlloc(size_t size)
3043 {
3044     if (ImGuiContext* ctx = GImGui)
3045         ctx->IO.MetricsActiveAllocations++;
3046     return GImAllocatorAllocFunc(size, GImAllocatorUserData);
3047 }
3048 
3049 // IM_FREE() == ImGui::MemFree()
MemFree(void * ptr)3050 void ImGui::MemFree(void* ptr)
3051 {
3052     if (ptr)
3053         if (ImGuiContext* ctx = GImGui)
3054             ctx->IO.MetricsActiveAllocations--;
3055     return GImAllocatorFreeFunc(ptr, GImAllocatorUserData);
3056 }
3057 
GetClipboardText()3058 const char* ImGui::GetClipboardText()
3059 {
3060     ImGuiContext& g = *GImGui;
3061     return g.IO.GetClipboardTextFn ? g.IO.GetClipboardTextFn(g.IO.ClipboardUserData) : "";
3062 }
3063 
SetClipboardText(const char * text)3064 void ImGui::SetClipboardText(const char* text)
3065 {
3066     ImGuiContext& g = *GImGui;
3067     if (g.IO.SetClipboardTextFn)
3068         g.IO.SetClipboardTextFn(g.IO.ClipboardUserData, text);
3069 }
3070 
GetVersion()3071 const char* ImGui::GetVersion()
3072 {
3073     return IMGUI_VERSION;
3074 }
3075 
3076 // Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself
3077 // 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()3078 ImGuiContext* ImGui::GetCurrentContext()
3079 {
3080     return GImGui;
3081 }
3082 
SetCurrentContext(ImGuiContext * ctx)3083 void ImGui::SetCurrentContext(ImGuiContext* ctx)
3084 {
3085 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
3086     IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
3087 #else
3088     GImGui = ctx;
3089 #endif
3090 }
3091 
3092 // Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui.
3093 // Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
3094 // If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. your user code
3095 // may see different structures than what imgui.cpp sees, which is problematic.
3096 // 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)3097 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)
3098 {
3099     bool error = false;
3100     if (strcmp(version, IMGUI_VERSION)!=0) { error = true; IM_ASSERT(strcmp(version,IMGUI_VERSION)==0 && "Mismatched version string!");  }
3101     if (sz_io    != sizeof(ImGuiIO))       { error = true; IM_ASSERT(sz_io    == sizeof(ImGuiIO)      && "Mismatched struct layout!"); }
3102     if (sz_style != sizeof(ImGuiStyle))    { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle)   && "Mismatched struct layout!"); }
3103     if (sz_vec2  != sizeof(ImVec2))        { error = true; IM_ASSERT(sz_vec2  == sizeof(ImVec2)       && "Mismatched struct layout!"); }
3104     if (sz_vec4  != sizeof(ImVec4))        { error = true; IM_ASSERT(sz_vec4  == sizeof(ImVec4)       && "Mismatched struct layout!"); }
3105     if (sz_vert  != sizeof(ImDrawVert))    { error = true; IM_ASSERT(sz_vert  == sizeof(ImDrawVert)   && "Mismatched struct layout!"); }
3106     if (sz_idx   != sizeof(ImDrawIdx))     { error = true; IM_ASSERT(sz_idx   == sizeof(ImDrawIdx)    && "Mismatched struct layout!"); }
3107     return !error;
3108 }
3109 
SetAllocatorFunctions(void * (* alloc_func)(size_t sz,void * user_data),void (* free_func)(void * ptr,void * user_data),void * user_data)3110 void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data)
3111 {
3112     GImAllocatorAllocFunc = alloc_func;
3113     GImAllocatorFreeFunc = free_func;
3114     GImAllocatorUserData = user_data;
3115 }
3116 
CreateContext(ImFontAtlas * shared_font_atlas)3117 ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
3118 {
3119     ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
3120     if (GImGui == NULL)
3121         SetCurrentContext(ctx);
3122     Initialize(ctx);
3123     return ctx;
3124 }
3125 
DestroyContext(ImGuiContext * ctx)3126 void ImGui::DestroyContext(ImGuiContext* ctx)
3127 {
3128     if (ctx == NULL)
3129         ctx = GImGui;
3130     Shutdown(ctx);
3131     if (GImGui == ctx)
3132         SetCurrentContext(NULL);
3133     IM_DELETE(ctx);
3134 }
3135 
GetIO()3136 ImGuiIO& ImGui::GetIO()
3137 {
3138     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3139     return GImGui->IO;
3140 }
3141 
GetStyle()3142 ImGuiStyle& ImGui::GetStyle()
3143 {
3144     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3145     return GImGui->Style;
3146 }
3147 
3148 // Same value as passed to the old io.RenderDrawListsFn function. Valid after Render() and until the next call to NewFrame()
GetDrawData()3149 ImDrawData* ImGui::GetDrawData()
3150 {
3151     ImGuiContext& g = *GImGui;
3152     return g.DrawData.Valid ? &g.DrawData : NULL;
3153 }
3154 
GetTime()3155 double ImGui::GetTime()
3156 {
3157     return GImGui->Time;
3158 }
3159 
GetFrameCount()3160 int ImGui::GetFrameCount()
3161 {
3162     return GImGui->FrameCount;
3163 }
3164 
GetBackgroundDrawList()3165 ImDrawList* ImGui::GetBackgroundDrawList()
3166 {
3167     return &GImGui->BackgroundDrawList;
3168 }
3169 
GetForegroundDrawList()3170 ImDrawList* ImGui::GetForegroundDrawList()
3171 {
3172     return &GImGui->ForegroundDrawList;
3173 }
3174 
GetDrawListSharedData()3175 ImDrawListSharedData* ImGui::GetDrawListSharedData()
3176 {
3177     return &GImGui->DrawListSharedData;
3178 }
3179 
StartMouseMovingWindow(ImGuiWindow * window)3180 void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
3181 {
3182     // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
3183     // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
3184     // This is because we want ActiveId to be set even when the window is not permitted to move.
3185     ImGuiContext& g = *GImGui;
3186     FocusWindow(window);
3187     SetActiveID(window->MoveId, window);
3188     g.NavDisableHighlight = true;
3189     g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos;
3190 
3191     bool can_move_window = true;
3192     if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
3193         can_move_window = false;
3194     if (can_move_window)
3195         g.MovingWindow = window;
3196 }
3197 
3198 // Handle mouse moving window
3199 // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
UpdateMouseMovingWindowNewFrame()3200 void ImGui::UpdateMouseMovingWindowNewFrame()
3201 {
3202     ImGuiContext& g = *GImGui;
3203     if (g.MovingWindow != NULL)
3204     {
3205         // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
3206         // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
3207         KeepAliveID(g.ActiveId);
3208         IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
3209         ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
3210         if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
3211         {
3212             ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
3213             if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
3214             {
3215                 MarkIniSettingsDirty(moving_window);
3216                 SetWindowPos(moving_window, pos, ImGuiCond_Always);
3217             }
3218             FocusWindow(g.MovingWindow);
3219         }
3220         else
3221         {
3222             ClearActiveID();
3223             g.MovingWindow = NULL;
3224         }
3225     }
3226     else
3227     {
3228         // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
3229         if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
3230         {
3231             KeepAliveID(g.ActiveId);
3232             if (!g.IO.MouseDown[0])
3233                 ClearActiveID();
3234         }
3235     }
3236 }
3237 
3238 // Initiate moving window, handle left-click and right-click focus
UpdateMouseMovingWindowEndFrame()3239 void ImGui::UpdateMouseMovingWindowEndFrame()
3240 {
3241     // Initiate moving window
3242     ImGuiContext& g = *GImGui;
3243     if (g.ActiveId != 0 || g.HoveredId != 0)
3244         return;
3245 
3246     // Unless we just made a window/popup appear
3247     if (g.NavWindow && g.NavWindow->Appearing)
3248         return;
3249 
3250     // Click to focus window and start moving (after we're done with all our widgets)
3251     if (g.IO.MouseClicked[0])
3252     {
3253         if (g.HoveredRootWindow != NULL)
3254         {
3255             StartMouseMovingWindow(g.HoveredWindow);
3256             if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoTitleBar))
3257                 if (!g.HoveredRootWindow->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
3258                     g.MovingWindow = NULL;
3259         }
3260         else if (g.NavWindow != NULL && GetTopMostPopupModal() == NULL)
3261         {
3262             // Clicking on void disable focus
3263             FocusWindow(NULL);
3264         }
3265     }
3266 
3267     // With right mouse button we close popups without changing focus based on where the mouse is aimed
3268     // Instead, focus will be restored to the window under the bottom-most closed popup.
3269     // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
3270     if (g.IO.MouseClicked[1])
3271     {
3272         // Find the top-most window between HoveredWindow and the top-most Modal Window.
3273         // This is where we can trim the popup stack.
3274         ImGuiWindow* modal = GetTopMostPopupModal();
3275         bool hovered_window_above_modal = false;
3276         if (modal == NULL)
3277             hovered_window_above_modal = true;
3278         for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--)
3279         {
3280             ImGuiWindow* window = g.Windows[i];
3281             if (window == modal)
3282                 break;
3283             if (window == g.HoveredWindow)
3284                 hovered_window_above_modal = true;
3285         }
3286         ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true);
3287     }
3288 }
3289 
IsWindowActiveAndVisible(ImGuiWindow * window)3290 static bool IsWindowActiveAndVisible(ImGuiWindow* window)
3291 {
3292     return (window->Active) && (!window->Hidden);
3293 }
3294 
UpdateMouseInputs()3295 static void ImGui::UpdateMouseInputs()
3296 {
3297     ImGuiContext& g = *GImGui;
3298 
3299     // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
3300     if (IsMousePosValid(&g.IO.MousePos))
3301         g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos);
3302 
3303     // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
3304     if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
3305         g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
3306     else
3307         g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
3308     if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
3309         g.NavDisableMouseHover = false;
3310 
3311     g.IO.MousePosPrev = g.IO.MousePos;
3312     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3313     {
3314         g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
3315         g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
3316         g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
3317         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;
3318         g.IO.MouseDoubleClicked[i] = false;
3319         if (g.IO.MouseClicked[i])
3320         {
3321             if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime)
3322             {
3323                 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3324                 if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
3325                     g.IO.MouseDoubleClicked[i] = true;
3326                 g.IO.MouseClickedTime[i] = -FLT_MAX;    // so the third click isn't turned into a double-click
3327             }
3328             else
3329             {
3330                 g.IO.MouseClickedTime[i] = g.Time;
3331             }
3332             g.IO.MouseClickedPos[i] = g.IO.MousePos;
3333             g.IO.MouseDownWasDoubleClick[i] = g.IO.MouseDoubleClicked[i];
3334             g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
3335             g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
3336         }
3337         else if (g.IO.MouseDown[i])
3338         {
3339             // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
3340             ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3341             g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
3342             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);
3343             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);
3344         }
3345         if (!g.IO.MouseDown[i] && !g.IO.MouseReleased[i])
3346             g.IO.MouseDownWasDoubleClick[i] = false;
3347         if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
3348             g.NavDisableMouseHover = false;
3349     }
3350 }
3351 
StartLockWheelingWindow(ImGuiWindow * window)3352 static void StartLockWheelingWindow(ImGuiWindow* window)
3353 {
3354     ImGuiContext& g = *GImGui;
3355     if (g.WheelingWindow == window)
3356         return;
3357     g.WheelingWindow = window;
3358     g.WheelingWindowRefMousePos = g.IO.MousePos;
3359     g.WheelingWindowTimer = WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER;
3360 }
3361 
UpdateMouseWheel()3362 void ImGui::UpdateMouseWheel()
3363 {
3364     ImGuiContext& g = *GImGui;
3365 
3366     // Reset the locked window if we move the mouse or after the timer elapses
3367     if (g.WheelingWindow != NULL)
3368     {
3369         g.WheelingWindowTimer -= g.IO.DeltaTime;
3370         if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
3371             g.WheelingWindowTimer = 0.0f;
3372         if (g.WheelingWindowTimer <= 0.0f)
3373         {
3374             g.WheelingWindow = NULL;
3375             g.WheelingWindowTimer = 0.0f;
3376         }
3377     }
3378 
3379     if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f)
3380         return;
3381 
3382     ImGuiWindow* window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
3383     if (!window || window->Collapsed)
3384         return;
3385 
3386     // Zoom / Scale window
3387     // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
3388     if (g.IO.MouseWheel != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
3389     {
3390         StartLockWheelingWindow(window);
3391         const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
3392         const float scale = new_font_scale / window->FontWindowScale;
3393         window->FontWindowScale = new_font_scale;
3394         if (!(window->Flags & ImGuiWindowFlags_ChildWindow))
3395         {
3396             const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
3397             SetWindowPos(window, window->Pos + offset, 0);
3398             window->Size = ImFloor(window->Size * scale);
3399             window->SizeFull = ImFloor(window->SizeFull * scale);
3400         }
3401         return;
3402     }
3403 
3404     // Mouse wheel scrolling
3405     // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent
3406 
3407     // Vertical Mouse Wheel scrolling
3408     const float wheel_y = (g.IO.MouseWheel != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f;
3409     if (wheel_y != 0.0f && !g.IO.KeyCtrl)
3410     {
3411         StartLockWheelingWindow(window);
3412         while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3413             window = window->ParentWindow;
3414         if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3415         {
3416             float max_step = window->InnerRect.GetHeight() * 0.67f;
3417             float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step));
3418             SetScrollY(window, window->Scroll.y - wheel_y * scroll_step);
3419         }
3420     }
3421 
3422     // Horizontal Mouse Wheel scrolling, or Vertical Mouse Wheel w/ Shift held
3423     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;
3424     if (wheel_x != 0.0f && !g.IO.KeyCtrl)
3425     {
3426         StartLockWheelingWindow(window);
3427         while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.x == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3428             window = window->ParentWindow;
3429         if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3430         {
3431             float max_step = window->InnerRect.GetWidth() * 0.67f;
3432             float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step));
3433             SetScrollX(window, window->Scroll.x - wheel_x * scroll_step);
3434         }
3435     }
3436 }
3437 
3438 // 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()3439 void ImGui::UpdateHoveredWindowAndCaptureFlags()
3440 {
3441     ImGuiContext& g = *GImGui;
3442 
3443     // Find the window hovered by mouse:
3444     // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3445     // - 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.
3446     // - 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.
3447     FindHoveredWindow();
3448 
3449     // Modal windows prevents cursor from hovering behind them.
3450     ImGuiWindow* modal_window = GetTopMostPopupModal();
3451     if (modal_window)
3452         if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window))
3453             g.HoveredRootWindow = g.HoveredWindow = NULL;
3454 
3455     // Disabled mouse?
3456     if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
3457         g.HoveredWindow = g.HoveredRootWindow = NULL;
3458 
3459     // 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.
3460     int mouse_earliest_button_down = -1;
3461     bool mouse_any_down = false;
3462     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3463     {
3464         if (g.IO.MouseClicked[i])
3465             g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty());
3466         mouse_any_down |= g.IO.MouseDown[i];
3467         if (g.IO.MouseDown[i])
3468             if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
3469                 mouse_earliest_button_down = i;
3470     }
3471     const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
3472 
3473     // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
3474     // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
3475     const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
3476     if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
3477         g.HoveredWindow = g.HoveredRootWindow = NULL;
3478 
3479     // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app)
3480     if (g.WantCaptureMouseNextFrame != -1)
3481         g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
3482     else
3483         g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty());
3484 
3485     // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app)
3486     if (g.WantCaptureKeyboardNextFrame != -1)
3487         g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
3488     else
3489         g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
3490     if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
3491         g.IO.WantCaptureKeyboard = true;
3492 
3493     // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
3494     g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
3495 }
3496 
NewFrameSanityChecks()3497 static void NewFrameSanityChecks()
3498 {
3499     ImGuiContext& g = *GImGui;
3500 
3501     // Check user data
3502     // (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)
3503     IM_ASSERT(g.Initialized);
3504     IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0)              && "Need a positive DeltaTime!");
3505     IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount)  && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
3506     IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f  && "Invalid DisplaySize value!");
3507     IM_ASSERT(g.IO.Fonts->Fonts.Size > 0                                && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3508     IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded()                          && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3509     IM_ASSERT(g.Style.CurveTessellationTol > 0.0f                       && "Invalid style setting!");
3510     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)!");
3511     IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting.");
3512     IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
3513     for (int n = 0; n < ImGuiKey_COUNT; n++)
3514         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)");
3515 
3516     // 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)
3517     if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
3518         IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
3519 
3520     // Perform simple check: the beta io.ConfigWindowsResizeFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
3521     if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
3522         g.IO.ConfigWindowsResizeFromEdges = false;
3523 }
3524 
NewFrame()3525 void ImGui::NewFrame()
3526 {
3527     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3528     ImGuiContext& g = *GImGui;
3529 
3530 #ifdef IMGUI_ENABLE_TEST_ENGINE
3531     ImGuiTestEngineHook_PreNewFrame(&g);
3532 #endif
3533 
3534     // Check and assert for various common IO and Configuration mistakes
3535     NewFrameSanityChecks();
3536 
3537     // Load settings on first frame (if not explicitly loaded manually before)
3538     if (!g.SettingsLoaded)
3539     {
3540         IM_ASSERT(g.SettingsWindows.empty());
3541         if (g.IO.IniFilename)
3542             LoadIniSettingsFromDisk(g.IO.IniFilename);
3543         g.SettingsLoaded = true;
3544     }
3545 
3546     // Save settings (with a delay after the last modification, so we don't spam disk too much)
3547     if (g.SettingsDirtyTimer > 0.0f)
3548     {
3549         g.SettingsDirtyTimer -= g.IO.DeltaTime;
3550         if (g.SettingsDirtyTimer <= 0.0f)
3551         {
3552             if (g.IO.IniFilename != NULL)
3553                 SaveIniSettingsToDisk(g.IO.IniFilename);
3554             else
3555                 g.IO.WantSaveIniSettings = true;  // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
3556             g.SettingsDirtyTimer = 0.0f;
3557         }
3558     }
3559 
3560     g.Time += g.IO.DeltaTime;
3561     g.WithinFrameScope = true;
3562     g.FrameCount += 1;
3563     g.TooltipOverrideCount = 0;
3564     g.WindowsActiveCount = 0;
3565 
3566     // Setup current font and draw list shared data
3567     g.IO.Fonts->Locked = true;
3568     SetCurrentFont(GetDefaultFont());
3569     IM_ASSERT(g.Font->IsLoaded());
3570     g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
3571     g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
3572     g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
3573     if (g.Style.AntiAliasedLines)
3574         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
3575     if (g.Style.AntiAliasedFill)
3576         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
3577     if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
3578         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
3579 
3580     g.BackgroundDrawList.Clear();
3581     g.BackgroundDrawList.PushTextureID(g.IO.Fonts->TexID);
3582     g.BackgroundDrawList.PushClipRectFullScreen();
3583 
3584     g.ForegroundDrawList.Clear();
3585     g.ForegroundDrawList.PushTextureID(g.IO.Fonts->TexID);
3586     g.ForegroundDrawList.PushClipRectFullScreen();
3587 
3588     // Mark rendering data as invalid to prevent user who may have a handle on it to use it.
3589     g.DrawData.Clear();
3590 
3591     // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
3592     if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
3593         KeepAliveID(g.DragDropPayload.SourceId);
3594 
3595     // Clear reference to active widget if the widget isn't alive anymore
3596     if (!g.HoveredIdPreviousFrame)
3597         g.HoveredIdTimer = 0.0f;
3598     if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
3599         g.HoveredIdNotActiveTimer = 0.0f;
3600     if (g.HoveredId)
3601         g.HoveredIdTimer += g.IO.DeltaTime;
3602     if (g.HoveredId && g.ActiveId != g.HoveredId)
3603         g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
3604     g.HoveredIdPreviousFrame = g.HoveredId;
3605     g.HoveredId = 0;
3606     g.HoveredIdAllowOverlap = false;
3607     if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
3608         ClearActiveID();
3609     if (g.ActiveId)
3610         g.ActiveIdTimer += g.IO.DeltaTime;
3611     g.LastActiveIdTimer += g.IO.DeltaTime;
3612     g.ActiveIdPreviousFrame = g.ActiveId;
3613     g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
3614     g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore;
3615     g.ActiveIdIsAlive = 0;
3616     g.ActiveIdHasBeenEditedThisFrame = false;
3617     g.ActiveIdPreviousFrameIsAlive = false;
3618     g.ActiveIdIsJustActivated = false;
3619     if (g.TempInputTextId != 0 && g.ActiveId != g.TempInputTextId)
3620         g.TempInputTextId = 0;
3621     if (g.ActiveId == 0)
3622     {
3623         g.ActiveIdUsingNavDirMask = g.ActiveIdUsingNavInputMask = 0;
3624         g.ActiveIdUsingKeyInputMask = 0;
3625     }
3626 
3627     // Drag and drop
3628     g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
3629     g.DragDropAcceptIdCurr = 0;
3630     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
3631     g.DragDropWithinSourceOrTarget = false;
3632 
3633     // Update keyboard input state
3634     memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
3635     for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
3636         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;
3637 
3638     // Update gamepad/keyboard directional navigation
3639     NavUpdate();
3640 
3641     // Update mouse input state
3642     UpdateMouseInputs();
3643 
3644     // Calculate frame-rate for the user, as a purely luxurious feature
3645     g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
3646     g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
3647     g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
3648     g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX;
3649 
3650     // Find hovered window
3651     // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
3652     UpdateHoveredWindowAndCaptureFlags();
3653 
3654     // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
3655     UpdateMouseMovingWindowNewFrame();
3656 
3657     // Background darkening/whitening
3658     if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
3659         g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
3660     else
3661         g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
3662 
3663     g.MouseCursor = ImGuiMouseCursor_Arrow;
3664     g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
3665     g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
3666 
3667     // Mouse wheel scrolling, scale
3668     UpdateMouseWheel();
3669 
3670     // Pressing TAB activate widget focus
3671     g.FocusTabPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab));
3672     if (g.ActiveId == 0 && g.FocusTabPressed)
3673     {
3674         // Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also
3675         // manipulate the Next fields even, even though they will be turned into Curr fields by the code below.
3676         g.FocusRequestNextWindow = g.NavWindow;
3677         g.FocusRequestNextCounterAll = INT_MAX;
3678         if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
3679             g.FocusRequestNextCounterTab = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1);
3680         else
3681             g.FocusRequestNextCounterTab = g.IO.KeyShift ? -1 : 0;
3682     }
3683 
3684     // Turn queued focus request into current one
3685     g.FocusRequestCurrWindow = NULL;
3686     g.FocusRequestCurrCounterAll = g.FocusRequestCurrCounterTab = INT_MAX;
3687     if (g.FocusRequestNextWindow != NULL)
3688     {
3689         ImGuiWindow* window = g.FocusRequestNextWindow;
3690         g.FocusRequestCurrWindow = window;
3691         if (g.FocusRequestNextCounterAll != INT_MAX && window->DC.FocusCounterAll != -1)
3692             g.FocusRequestCurrCounterAll = ImModPositive(g.FocusRequestNextCounterAll, window->DC.FocusCounterAll + 1);
3693         if (g.FocusRequestNextCounterTab != INT_MAX && window->DC.FocusCounterTab != -1)
3694             g.FocusRequestCurrCounterTab = ImModPositive(g.FocusRequestNextCounterTab, window->DC.FocusCounterTab + 1);
3695         g.FocusRequestNextWindow = NULL;
3696         g.FocusRequestNextCounterAll = g.FocusRequestNextCounterTab = INT_MAX;
3697     }
3698 
3699     g.NavIdTabCounter = INT_MAX;
3700 
3701     // Mark all windows as not visible and compact unused memory.
3702     IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size);
3703     const float memory_compact_start_time = (g.IO.ConfigWindowsMemoryCompactTimer >= 0.0f) ? (float)g.Time - g.IO.ConfigWindowsMemoryCompactTimer : FLT_MAX;
3704     for (int i = 0; i != g.Windows.Size; i++)
3705     {
3706         ImGuiWindow* window = g.Windows[i];
3707         window->WasActive = window->Active;
3708         window->BeginCount = 0;
3709         window->Active = false;
3710         window->WriteAccessed = false;
3711 
3712         // Garbage collect (this is totally functional but we may need decide if the side-effects are desirable)
3713         if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
3714             GcCompactTransientWindowBuffers(window);
3715     }
3716 
3717     // Closing the focused window restore focus to the first active root window in descending z-order
3718     if (g.NavWindow && !g.NavWindow->WasActive)
3719         FocusTopMostWindowUnderOne(NULL, NULL);
3720 
3721     // No window should be open at the beginning of the frame.
3722     // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
3723     g.CurrentWindowStack.resize(0);
3724     g.BeginPopupStack.resize(0);
3725     ClosePopupsOverWindow(g.NavWindow, false);
3726 
3727     // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
3728     UpdateDebugToolItemPicker();
3729 
3730     // Create implicit/fallback window - which we will only render it if the user has added something to it.
3731     // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
3732     // This fallback is particularly important as it avoid ImGui:: calls from crashing.
3733     SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver);
3734     Begin("Debug##Default");
3735     g.WithinFrameScopeWithImplicitWindow = true;
3736 
3737 #ifdef IMGUI_ENABLE_TEST_ENGINE
3738     ImGuiTestEngineHook_PostNewFrame(&g);
3739 #endif
3740 }
3741 
3742 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
UpdateDebugToolItemPicker()3743 void ImGui::UpdateDebugToolItemPicker()
3744 {
3745     ImGuiContext& g = *GImGui;
3746     g.DebugItemPickerBreakID = 0;
3747     if (g.DebugItemPickerActive)
3748     {
3749         const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
3750         ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
3751         if (ImGui::IsKeyPressedMap(ImGuiKey_Escape))
3752             g.DebugItemPickerActive = false;
3753         if (ImGui::IsMouseClicked(0) && hovered_id)
3754         {
3755             g.DebugItemPickerBreakID = hovered_id;
3756             g.DebugItemPickerActive = false;
3757         }
3758         ImGui::SetNextWindowBgAlpha(0.60f);
3759         ImGui::BeginTooltip();
3760         ImGui::Text("HoveredId: 0x%08X", hovered_id);
3761         ImGui::Text("Press ESC to abort picking.");
3762         ImGui::TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!");
3763         ImGui::EndTooltip();
3764     }
3765 }
3766 
Initialize(ImGuiContext * context)3767 void ImGui::Initialize(ImGuiContext* context)
3768 {
3769     ImGuiContext& g = *context;
3770     IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
3771 
3772     // Add .ini handle for ImGuiWindow type
3773     {
3774         ImGuiSettingsHandler ini_handler;
3775         ini_handler.TypeName = "Window";
3776         ini_handler.TypeHash = ImHashStr("Window");
3777         ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen;
3778         ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;
3779         ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll;
3780         g.SettingsHandlers.push_back(ini_handler);
3781     }
3782 
3783     g.Initialized = true;
3784 }
3785 
3786 // This function is merely here to free heap allocations.
Shutdown(ImGuiContext * context)3787 void ImGui::Shutdown(ImGuiContext* context)
3788 {
3789     // 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)
3790     ImGuiContext& g = *context;
3791     if (g.IO.Fonts && g.FontAtlasOwnedByContext)
3792     {
3793         g.IO.Fonts->Locked = false;
3794         IM_DELETE(g.IO.Fonts);
3795     }
3796     g.IO.Fonts = NULL;
3797 
3798     // Cleanup of other data are conditional on actually having initialized Dear ImGui.
3799     if (!g.Initialized)
3800         return;
3801 
3802     // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
3803     if (g.SettingsLoaded && g.IO.IniFilename != NULL)
3804     {
3805         ImGuiContext* backup_context = GImGui;
3806         SetCurrentContext(context);
3807         SaveIniSettingsToDisk(g.IO.IniFilename);
3808         SetCurrentContext(backup_context);
3809     }
3810 
3811     // Clear everything else
3812     for (int i = 0; i < g.Windows.Size; i++)
3813         IM_DELETE(g.Windows[i]);
3814     g.Windows.clear();
3815     g.WindowsFocusOrder.clear();
3816     g.WindowsSortBuffer.clear();
3817     g.CurrentWindow = NULL;
3818     g.CurrentWindowStack.clear();
3819     g.WindowsById.Clear();
3820     g.NavWindow = NULL;
3821     g.HoveredWindow = g.HoveredRootWindow = NULL;
3822     g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
3823     g.MovingWindow = NULL;
3824     g.ColorModifiers.clear();
3825     g.StyleModifiers.clear();
3826     g.FontStack.clear();
3827     g.OpenPopupStack.clear();
3828     g.BeginPopupStack.clear();
3829     g.DrawDataBuilder.ClearFreeMemory();
3830     g.BackgroundDrawList.ClearFreeMemory();
3831     g.ForegroundDrawList.ClearFreeMemory();
3832 
3833     g.TabBars.Clear();
3834     g.CurrentTabBarStack.clear();
3835     g.ShrinkWidthBuffer.clear();
3836 
3837     g.PrivateClipboard.clear();
3838     g.InputTextState.ClearFreeMemory();
3839 
3840     g.SettingsWindows.clear();
3841     g.SettingsHandlers.clear();
3842 
3843     if (g.LogFile)
3844     {
3845 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
3846         if (g.LogFile != stdout)
3847 #endif
3848             ImFileClose(g.LogFile);
3849         g.LogFile = NULL;
3850     }
3851     g.LogBuffer.clear();
3852 
3853     g.Initialized = false;
3854 }
3855 
3856 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)3857 static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
3858 {
3859     const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
3860     const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
3861     if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
3862         return d;
3863     if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
3864         return d;
3865     return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
3866 }
3867 
AddWindowToSortBuffer(ImVector<ImGuiWindow * > * out_sorted_windows,ImGuiWindow * window)3868 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
3869 {
3870     out_sorted_windows->push_back(window);
3871     if (window->Active)
3872     {
3873         int count = window->DC.ChildWindows.Size;
3874         if (count > 1)
3875             ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
3876         for (int i = 0; i < count; i++)
3877         {
3878             ImGuiWindow* child = window->DC.ChildWindows[i];
3879             if (child->Active)
3880                 AddWindowToSortBuffer(out_sorted_windows, child);
3881         }
3882     }
3883 }
3884 
AddDrawListToDrawData(ImVector<ImDrawList * > * out_list,ImDrawList * draw_list)3885 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
3886 {
3887     if (draw_list->CmdBuffer.empty())
3888         return;
3889 
3890     // Remove trailing command if unused
3891     ImDrawCmd& last_cmd = draw_list->CmdBuffer.back();
3892     if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL)
3893     {
3894         draw_list->CmdBuffer.pop_back();
3895         if (draw_list->CmdBuffer.empty())
3896             return;
3897     }
3898 
3899     // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
3900     // May trigger for you if you are using PrimXXX functions incorrectly.
3901     IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
3902     IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
3903     if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset))
3904         IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
3905 
3906     // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
3907     // If this assert triggers because you are drawing lots of stuff manually:
3908     // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds.
3909     //   Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics window to inspect draw list contents.
3910     // - If you want large meshes with more than 64K vertices, you can either:
3911     //   (A) Handle the ImDrawCmd::VtxOffset value in your renderer back-end, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'.
3912     //       Most example back-ends already support this from 1.71. Pre-1.71 back-ends won't.
3913     //       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.
3914     //   (B) Or handle 32-bit indices in your renderer back-end, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h.
3915     //       Most example back-ends already support this. For example, the OpenGL example code detect index size at compile-time:
3916     //         glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
3917     //       Your own engine or render API may use different parameters or function calls to specify index sizes.
3918     //       2 and 4 bytes indices are generally supported by most graphics API.
3919     // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching
3920     //   the 64K limit to split your draw commands in multiple draw lists.
3921     if (sizeof(ImDrawIdx) == 2)
3922         IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
3923 
3924     out_list->push_back(draw_list);
3925 }
3926 
AddWindowToDrawData(ImVector<ImDrawList * > * out_render_list,ImGuiWindow * window)3927 static void AddWindowToDrawData(ImVector<ImDrawList*>* out_render_list, ImGuiWindow* window)
3928 {
3929     ImGuiContext& g = *GImGui;
3930     g.IO.MetricsRenderWindows++;
3931     AddDrawListToDrawData(out_render_list, window->DrawList);
3932     for (int i = 0; i < window->DC.ChildWindows.Size; i++)
3933     {
3934         ImGuiWindow* child = window->DC.ChildWindows[i];
3935         if (IsWindowActiveAndVisible(child)) // clipped children may have been marked not active
3936             AddWindowToDrawData(out_render_list, child);
3937     }
3938 }
3939 
3940 // Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)
AddRootWindowToDrawData(ImGuiWindow * window)3941 static void AddRootWindowToDrawData(ImGuiWindow* window)
3942 {
3943     ImGuiContext& g = *GImGui;
3944     if (window->Flags & ImGuiWindowFlags_Tooltip)
3945         AddWindowToDrawData(&g.DrawDataBuilder.Layers[1], window);
3946     else
3947         AddWindowToDrawData(&g.DrawDataBuilder.Layers[0], window);
3948 }
3949 
FlattenIntoSingleLayer()3950 void ImDrawDataBuilder::FlattenIntoSingleLayer()
3951 {
3952     int n = Layers[0].Size;
3953     int size = n;
3954     for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
3955         size += Layers[i].Size;
3956     Layers[0].resize(size);
3957     for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
3958     {
3959         ImVector<ImDrawList*>& layer = Layers[layer_n];
3960         if (layer.empty())
3961             continue;
3962         memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
3963         n += layer.Size;
3964         layer.resize(0);
3965     }
3966 }
3967 
SetupDrawData(ImVector<ImDrawList * > * draw_lists,ImDrawData * draw_data)3968 static void SetupDrawData(ImVector<ImDrawList*>* draw_lists, ImDrawData* draw_data)
3969 {
3970     ImGuiIO& io = ImGui::GetIO();
3971     draw_data->Valid = true;
3972     draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
3973     draw_data->CmdListsCount = draw_lists->Size;
3974     draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
3975     draw_data->DisplayPos = ImVec2(0.0f, 0.0f);
3976     draw_data->DisplaySize = io.DisplaySize;
3977     draw_data->FramebufferScale = io.DisplayFramebufferScale;
3978     for (int n = 0; n < draw_lists->Size; n++)
3979     {
3980         draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size;
3981         draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size;
3982     }
3983 }
3984 
3985 // 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)3986 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
3987 {
3988     ImGuiWindow* window = GetCurrentWindow();
3989     window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
3990     window->ClipRect = window->DrawList->_ClipRectStack.back();
3991 }
3992 
PopClipRect()3993 void ImGui::PopClipRect()
3994 {
3995     ImGuiWindow* window = GetCurrentWindow();
3996     window->DrawList->PopClipRect();
3997     window->ClipRect = window->DrawList->_ClipRectStack.back();
3998 }
3999 
4000 // 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()4001 void ImGui::EndFrame()
4002 {
4003     ImGuiContext& g = *GImGui;
4004     IM_ASSERT(g.Initialized);
4005     if (g.FrameCountEnded == g.FrameCount)          // Don't process EndFrame() multiple times.
4006         return;
4007     IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?");
4008 
4009     // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
4010     if (g.IO.ImeSetInputScreenPosFn && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f))
4011     {
4012         g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y);
4013         g.PlatformImeLastPos = g.PlatformImePos;
4014     }
4015 
4016     ErrorCheckEndFrame();
4017 
4018     // Hide implicit/fallback "Debug" window if it hasn't been used
4019     g.WithinFrameScopeWithImplicitWindow = false;
4020     if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
4021         g.CurrentWindow->Active = false;
4022     End();
4023 
4024     // Show CTRL+TAB list window
4025     if (g.NavWindowingTarget != NULL)
4026         NavUpdateWindowingOverlay();
4027 
4028     // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
4029     if (g.DragDropActive)
4030     {
4031         bool is_delivered = g.DragDropPayload.Delivery;
4032         bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton));
4033         if (is_delivered || is_elapsed)
4034             ClearDragDrop();
4035     }
4036 
4037     // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.
4038     if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount)
4039     {
4040         g.DragDropWithinSourceOrTarget = true;
4041         SetTooltip("...");
4042         g.DragDropWithinSourceOrTarget = false;
4043     }
4044 
4045     // End frame
4046     g.WithinFrameScope = false;
4047     g.FrameCountEnded = g.FrameCount;
4048 
4049     // Initiate moving window + handle left-click and right-click focus
4050     UpdateMouseMovingWindowEndFrame();
4051 
4052     // Sort the window list so that all child windows are after their parent
4053     // We cannot do that on FocusWindow() because childs may not exist yet
4054     g.WindowsSortBuffer.resize(0);
4055     g.WindowsSortBuffer.reserve(g.Windows.Size);
4056     for (int i = 0; i != g.Windows.Size; i++)
4057     {
4058         ImGuiWindow* window = g.Windows[i];
4059         if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow))       // if a child is active its parent will add it
4060             continue;
4061         AddWindowToSortBuffer(&g.WindowsSortBuffer, window);
4062     }
4063 
4064     // 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.
4065     IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size);
4066     g.Windows.swap(g.WindowsSortBuffer);
4067     g.IO.MetricsActiveWindows = g.WindowsActiveCount;
4068 
4069     // Unlock font atlas
4070     g.IO.Fonts->Locked = false;
4071 
4072     // Clear Input data for next frame
4073     g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
4074     g.IO.InputQueueCharacters.resize(0);
4075     memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
4076 }
4077 
Render()4078 void ImGui::Render()
4079 {
4080     ImGuiContext& g = *GImGui;
4081     IM_ASSERT(g.Initialized);
4082 
4083     if (g.FrameCountEnded != g.FrameCount)
4084         EndFrame();
4085     g.FrameCountRendered = g.FrameCount;
4086 
4087     // Gather ImDrawList to render (for each active window)
4088     g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsRenderWindows = 0;
4089     g.DrawDataBuilder.Clear();
4090     if (!g.BackgroundDrawList.VtxBuffer.empty())
4091         AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.BackgroundDrawList);
4092 
4093     ImGuiWindow* windows_to_render_top_most[2];
4094     windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
4095     windows_to_render_top_most[1] = g.NavWindowingTarget ? g.NavWindowingList : NULL;
4096     for (int n = 0; n != g.Windows.Size; n++)
4097     {
4098         ImGuiWindow* window = g.Windows[n];
4099         if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])
4100             AddRootWindowToDrawData(window);
4101     }
4102     for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++)
4103         if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window
4104             AddRootWindowToDrawData(windows_to_render_top_most[n]);
4105     g.DrawDataBuilder.FlattenIntoSingleLayer();
4106 
4107     // Draw software mouse cursor if requested
4108     if (g.IO.MouseDrawCursor)
4109         RenderMouseCursor(&g.ForegroundDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
4110 
4111     if (!g.ForegroundDrawList.VtxBuffer.empty())
4112         AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.ForegroundDrawList);
4113 
4114     // Setup ImDrawData structure for end-user
4115     SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData);
4116     g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount;
4117     g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount;
4118 
4119     // (Legacy) Call the Render callback function. The current prefer way is to let the user retrieve GetDrawData() and call the render function themselves.
4120 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
4121     if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)
4122         g.IO.RenderDrawListsFn(&g.DrawData);
4123 #endif
4124 }
4125 
4126 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
4127 // 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)4128 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
4129 {
4130     ImGuiContext& g = *GImGui;
4131 
4132     const char* text_display_end;
4133     if (hide_text_after_double_hash)
4134         text_display_end = FindRenderedTextEnd(text, text_end);      // Hide anything after a '##' string
4135     else
4136         text_display_end = text_end;
4137 
4138     ImFont* font = g.Font;
4139     const float font_size = g.FontSize;
4140     if (text == text_display_end)
4141         return ImVec2(0.0f, font_size);
4142     ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
4143 
4144     // Round
4145 	// XXX(sturnclaw): ported from ocornut/imgui 4622f14b
4146 	// FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out.
4147     // FIXME: Investigate using ceilf or e.g.
4148     // - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c
4149     // - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html
4150     text_size.x = IM_FLOOR(text_size.x + 0.99999f);
4151 
4152     return text_size;
4153 }
4154 
4155 // Find window given position, search front-to-back
4156 // FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programatically
4157 // with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
4158 // called, aka before the next Begin(). Moving window isn't affected.
FindHoveredWindow()4159 static void FindHoveredWindow()
4160 {
4161     ImGuiContext& g = *GImGui;
4162 
4163     ImGuiWindow* hovered_window = NULL;
4164     if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
4165         hovered_window = g.MovingWindow;
4166 
4167     ImVec2 padding_regular = g.Style.TouchExtraPadding;
4168     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;
4169     for (int i = g.Windows.Size - 1; i >= 0; i--)
4170     {
4171         ImGuiWindow* window = g.Windows[i];
4172         if (!window->Active || window->Hidden)
4173             continue;
4174         if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
4175             continue;
4176 
4177         // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
4178         ImRect bb(window->OuterRectClipped);
4179         if (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize))
4180             bb.Expand(padding_regular);
4181         else
4182             bb.Expand(padding_for_resize_from_edges);
4183         if (!bb.Contains(g.IO.MousePos))
4184             continue;
4185 
4186         // Those seemingly unnecessary extra tests are because the code here is a little different in viewport/docking branches.
4187         if (hovered_window == NULL)
4188             hovered_window = window;
4189         if (hovered_window)
4190             break;
4191     }
4192 
4193     g.HoveredWindow = hovered_window;
4194     g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
4195 
4196 }
4197 
4198 // Test if mouse cursor is hovering given rectangle
4199 // NB- Rectangle is clipped by our current clip setting
4200 // 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)4201 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
4202 {
4203     ImGuiContext& g = *GImGui;
4204 
4205     // Clip
4206     ImRect rect_clipped(r_min, r_max);
4207     if (clip)
4208         rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
4209 
4210     // Expand for touch input
4211     const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
4212     if (!rect_for_touch.Contains(g.IO.MousePos))
4213         return false;
4214     return true;
4215 }
4216 
GetKeyIndex(ImGuiKey imgui_key)4217 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
4218 {
4219     IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
4220     ImGuiContext& g = *GImGui;
4221     return g.IO.KeyMap[imgui_key];
4222 }
4223 
4224 // 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)4225 bool ImGui::IsKeyDown(int user_key_index)
4226 {
4227     if (user_key_index < 0)
4228         return false;
4229     ImGuiContext& g = *GImGui;
4230     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4231     return g.IO.KeysDown[user_key_index];
4232 }
4233 
4234 // t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
4235 // t1 = current time (e.g.: g.Time)
4236 // An event is triggered at:
4237 //  t = 0.0f     t = repeat_delay,    t = repeat_delay + repeat_rate*N
CalcTypematicRepeatAmount(float t0,float t1,float repeat_delay,float repeat_rate)4238 int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)
4239 {
4240     if (t1 == 0.0f)
4241         return 1;
4242     if (t0 >= t1)
4243         return 0;
4244     if (repeat_rate <= 0.0f)
4245         return (t0 < repeat_delay) && (t1 >= repeat_delay);
4246     const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);
4247     const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);
4248     const int count = count_t1 - count_t0;
4249     return count;
4250 }
4251 
GetKeyPressedAmount(int key_index,float repeat_delay,float repeat_rate)4252 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
4253 {
4254     ImGuiContext& g = *GImGui;
4255     if (key_index < 0)
4256         return 0;
4257     IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4258     const float t = g.IO.KeysDownDuration[key_index];
4259     return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
4260 }
4261 
IsKeyPressed(int user_key_index,bool repeat)4262 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
4263 {
4264     ImGuiContext& g = *GImGui;
4265     if (user_key_index < 0)
4266         return false;
4267     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4268     const float t = g.IO.KeysDownDuration[user_key_index];
4269     if (t == 0.0f)
4270         return true;
4271     if (repeat && t > g.IO.KeyRepeatDelay)
4272         return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
4273     return false;
4274 }
4275 
IsKeyReleased(int user_key_index)4276 bool ImGui::IsKeyReleased(int user_key_index)
4277 {
4278     ImGuiContext& g = *GImGui;
4279     if (user_key_index < 0) return false;
4280     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4281     return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
4282 }
4283 
IsMouseDown(int button)4284 bool ImGui::IsMouseDown(int button)
4285 {
4286     ImGuiContext& g = *GImGui;
4287     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4288     return g.IO.MouseDown[button];
4289 }
4290 
IsAnyMouseDown()4291 bool ImGui::IsAnyMouseDown()
4292 {
4293     ImGuiContext& g = *GImGui;
4294     for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
4295         if (g.IO.MouseDown[n])
4296             return true;
4297     return false;
4298 }
4299 
IsMouseClicked(int button,bool repeat)4300 bool ImGui::IsMouseClicked(int button, bool repeat)
4301 {
4302     ImGuiContext& g = *GImGui;
4303     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4304     const float t = g.IO.MouseDownDuration[button];
4305     if (t == 0.0f)
4306         return true;
4307 
4308     if (repeat && t > g.IO.KeyRepeatDelay)
4309     {
4310         // 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.
4311         int amount = CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.50f);
4312         if (amount > 0)
4313             return true;
4314     }
4315 
4316     return false;
4317 }
4318 
IsMouseReleased(int button)4319 bool ImGui::IsMouseReleased(int button)
4320 {
4321     ImGuiContext& g = *GImGui;
4322     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4323     return g.IO.MouseReleased[button];
4324 }
4325 
IsMouseDoubleClicked(int button)4326 bool ImGui::IsMouseDoubleClicked(int button)
4327 {
4328     ImGuiContext& g = *GImGui;
4329     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4330     return g.IO.MouseDoubleClicked[button];
4331 }
4332 
4333 // [Internal] This doesn't test if the button is pressed
IsMouseDragPastThreshold(int button,float lock_threshold)4334 bool ImGui::IsMouseDragPastThreshold(int button, float lock_threshold)
4335 {
4336     ImGuiContext& g = *GImGui;
4337     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4338     if (lock_threshold < 0.0f)
4339         lock_threshold = g.IO.MouseDragThreshold;
4340     return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
4341 }
4342 
IsMouseDragging(int button,float lock_threshold)4343 bool ImGui::IsMouseDragging(int button, float lock_threshold)
4344 {
4345     ImGuiContext& g = *GImGui;
4346     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4347     if (!g.IO.MouseDown[button])
4348         return false;
4349     return IsMouseDragPastThreshold(button, lock_threshold);
4350 }
4351 
GetMousePos()4352 ImVec2 ImGui::GetMousePos()
4353 {
4354     return GImGui->IO.MousePos;
4355 }
4356 
4357 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()4358 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
4359 {
4360     ImGuiContext& g = *GImGui;
4361     if (g.BeginPopupStack.Size > 0)
4362         return g.OpenPopupStack[g.BeginPopupStack.Size-1].OpenMousePos;
4363     return g.IO.MousePos;
4364 }
4365 
4366 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
IsMousePosValid(const ImVec2 * mouse_pos)4367 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
4368 {
4369     // The assert is only to silence a false-positive in XCode Static Analysis.
4370     // 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).
4371     IM_ASSERT(GImGui != NULL);
4372     const float MOUSE_INVALID = -256000.0f;
4373     ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
4374     return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
4375 }
4376 
4377 // Return the delta from the initial clicking position while the mouse button is clicked or was just released.
4378 // This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
4379 // 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)4380 ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold)
4381 {
4382     ImGuiContext& g = *GImGui;
4383     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4384     if (lock_threshold < 0.0f)
4385         lock_threshold = g.IO.MouseDragThreshold;
4386     if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])
4387         if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
4388             if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))
4389                 return g.IO.MousePos - g.IO.MouseClickedPos[button];
4390     return ImVec2(0.0f, 0.0f);
4391 }
4392 
ResetMouseDragDelta(int button)4393 void ImGui::ResetMouseDragDelta(int button)
4394 {
4395     ImGuiContext& g = *GImGui;
4396     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4397     // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
4398     g.IO.MouseClickedPos[button] = g.IO.MousePos;
4399 }
4400 
GetMouseCursor()4401 ImGuiMouseCursor ImGui::GetMouseCursor()
4402 {
4403     return GImGui->MouseCursor;
4404 }
4405 
SetMouseCursor(ImGuiMouseCursor cursor_type)4406 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
4407 {
4408     GImGui->MouseCursor = cursor_type;
4409 }
4410 
CaptureKeyboardFromApp(bool capture)4411 void ImGui::CaptureKeyboardFromApp(bool capture)
4412 {
4413     GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
4414 }
4415 
CaptureMouseFromApp(bool capture)4416 void ImGui::CaptureMouseFromApp(bool capture)
4417 {
4418     GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
4419 }
4420 
IsItemActive()4421 bool ImGui::IsItemActive()
4422 {
4423     ImGuiContext& g = *GImGui;
4424     if (g.ActiveId)
4425     {
4426         ImGuiWindow* window = g.CurrentWindow;
4427         return g.ActiveId == window->DC.LastItemId;
4428     }
4429     return false;
4430 }
4431 
IsItemActivated()4432 bool ImGui::IsItemActivated()
4433 {
4434     ImGuiContext& g = *GImGui;
4435     if (g.ActiveId)
4436     {
4437         ImGuiWindow* window = g.CurrentWindow;
4438         if (g.ActiveId == window->DC.LastItemId && g.ActiveIdPreviousFrame != window->DC.LastItemId)
4439             return true;
4440     }
4441     return false;
4442 }
4443 
IsItemDeactivated()4444 bool ImGui::IsItemDeactivated()
4445 {
4446     ImGuiContext& g = *GImGui;
4447     ImGuiWindow* window = g.CurrentWindow;
4448     if (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDeactivated)
4449         return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Deactivated) != 0;
4450     return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId);
4451 }
4452 
IsItemDeactivatedAfterEdit()4453 bool ImGui::IsItemDeactivatedAfterEdit()
4454 {
4455     ImGuiContext& g = *GImGui;
4456     return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore));
4457 }
4458 
IsItemFocused()4459 bool ImGui::IsItemFocused()
4460 {
4461     ImGuiContext& g = *GImGui;
4462     ImGuiWindow* window = g.CurrentWindow;
4463 
4464     if (g.NavId == 0 || g.NavDisableHighlight || g.NavId != window->DC.LastItemId)
4465         return false;
4466     return true;
4467 }
4468 
IsItemClicked(int mouse_button)4469 bool ImGui::IsItemClicked(int mouse_button)
4470 {
4471     return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
4472 }
4473 
IsItemToggledOpen()4474 bool ImGui::IsItemToggledOpen()
4475 {
4476     ImGuiContext& g = *GImGui;
4477     return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false;
4478 }
4479 
IsItemToggledSelection()4480 bool ImGui::IsItemToggledSelection()
4481 {
4482     ImGuiContext& g = *GImGui;
4483     return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;
4484 }
4485 
IsAnyItemHovered()4486 bool ImGui::IsAnyItemHovered()
4487 {
4488     ImGuiContext& g = *GImGui;
4489     return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
4490 }
4491 
IsAnyItemActive()4492 bool ImGui::IsAnyItemActive()
4493 {
4494     ImGuiContext& g = *GImGui;
4495     return g.ActiveId != 0;
4496 }
4497 
IsAnyItemFocused()4498 bool ImGui::IsAnyItemFocused()
4499 {
4500     ImGuiContext& g = *GImGui;
4501     return g.NavId != 0 && !g.NavDisableHighlight;
4502 }
4503 
IsItemVisible()4504 bool ImGui::IsItemVisible()
4505 {
4506     ImGuiWindow* window = GetCurrentWindowRead();
4507     return window->ClipRect.Overlaps(window->DC.LastItemRect);
4508 }
4509 
IsItemEdited()4510 bool ImGui::IsItemEdited()
4511 {
4512     ImGuiWindow* window = GetCurrentWindowRead();
4513     return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0;
4514 }
4515 
4516 // 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()4517 void ImGui::SetItemAllowOverlap()
4518 {
4519     ImGuiContext& g = *GImGui;
4520     if (g.HoveredId == g.CurrentWindow->DC.LastItemId)
4521         g.HoveredIdAllowOverlap = true;
4522     if (g.ActiveId == g.CurrentWindow->DC.LastItemId)
4523         g.ActiveIdAllowOverlap = true;
4524 }
4525 
GetItemRectMin()4526 ImVec2 ImGui::GetItemRectMin()
4527 {
4528     ImGuiWindow* window = GetCurrentWindowRead();
4529     return window->DC.LastItemRect.Min;
4530 }
4531 
GetItemRectMax()4532 ImVec2 ImGui::GetItemRectMax()
4533 {
4534     ImGuiWindow* window = GetCurrentWindowRead();
4535     return window->DC.LastItemRect.Max;
4536 }
4537 
GetItemRectSize()4538 ImVec2 ImGui::GetItemRectSize()
4539 {
4540     ImGuiWindow* window = GetCurrentWindowRead();
4541     return window->DC.LastItemRect.GetSize();
4542 }
4543 
GetViewportRect()4544 static ImRect GetViewportRect()
4545 {
4546     ImGuiContext& g = *GImGui;
4547     return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
4548 }
4549 
BeginChildEx(const char * name,ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags flags)4550 static bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags)
4551 {
4552     ImGuiContext& g = *GImGui;
4553     ImGuiWindow* parent_window = g.CurrentWindow;
4554 
4555     flags |= ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
4556     flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove);  // Inherit the NoMove flag
4557 
4558     // Size
4559     const ImVec2 content_avail = GetContentRegionAvail();
4560     ImVec2 size = ImFloor(size_arg);
4561     const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
4562     if (size.x <= 0.0f)
4563         size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
4564     if (size.y <= 0.0f)
4565         size.y = ImMax(content_avail.y + size.y, 4.0f);
4566     SetNextWindowSize(size);
4567 
4568     // 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.
4569     char title[256];
4570     if (name)
4571         ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s_%08X", parent_window->Name, name, id);
4572     else
4573         ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id);
4574 
4575     const float backup_border_size = g.Style.ChildBorderSize;
4576     if (!border)
4577         g.Style.ChildBorderSize = 0.0f;
4578     bool ret = Begin(title, NULL, flags);
4579     g.Style.ChildBorderSize = backup_border_size;
4580 
4581     ImGuiWindow* child_window = g.CurrentWindow;
4582     child_window->ChildId = id;
4583     child_window->AutoFitChildAxises = (ImS8)auto_fit_axises;
4584 
4585     // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
4586     // While this is not really documented/defined, it seems that the expected thing to do.
4587     if (child_window->BeginCount == 1)
4588         parent_window->DC.CursorPos = child_window->Pos;
4589 
4590     // Process navigation-in immediately so NavInit can run on first frame
4591     if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll))
4592     {
4593         FocusWindow(child_window);
4594         NavInitWindow(child_window, false);
4595         SetActiveID(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item
4596         g.ActiveIdSource = ImGuiInputSource_Nav;
4597     }
4598     return ret;
4599 }
4600 
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4601 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4602 {
4603     ImGuiWindow* window = GetCurrentWindow();
4604     return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
4605 }
4606 
BeginChild(ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4607 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4608 {
4609     IM_ASSERT(id != 0);
4610     return BeginChildEx(NULL, id, size_arg, border, extra_flags);
4611 }
4612 
EndChild()4613 void ImGui::EndChild()
4614 {
4615     ImGuiContext& g = *GImGui;
4616     ImGuiWindow* window = g.CurrentWindow;
4617 
4618     IM_ASSERT(g.WithinEndChild == false);
4619     IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow);   // Mismatched BeginChild()/EndChild() callss
4620 
4621     g.WithinEndChild = true;
4622     if (window->BeginCount > 1)
4623     {
4624         End();
4625     }
4626     else
4627     {
4628         ImVec2 sz = window->Size;
4629         if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
4630             sz.x = ImMax(4.0f, sz.x);
4631         if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
4632             sz.y = ImMax(4.0f, sz.y);
4633         End();
4634 
4635         ImGuiWindow* parent_window = g.CurrentWindow;
4636         ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
4637         ItemSize(sz);
4638         if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
4639         {
4640             ItemAdd(bb, window->ChildId);
4641             RenderNavHighlight(bb, window->ChildId);
4642 
4643             // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
4644             if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow)
4645                 RenderNavHighlight(ImRect(bb.Min - ImVec2(2,2), bb.Max + ImVec2(2,2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
4646         }
4647         else
4648         {
4649             // Not navigable into
4650             ItemAdd(bb, 0);
4651         }
4652     }
4653     g.WithinEndChild = false;
4654 }
4655 
4656 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)4657 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
4658 {
4659     ImGuiContext& g = *GImGui;
4660     const ImGuiStyle& style = g.Style;
4661     PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
4662     PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
4663     PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
4664     PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
4665     bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
4666     PopStyleVar(3);
4667     PopStyleColor();
4668     return ret;
4669 }
4670 
EndChildFrame()4671 void ImGui::EndChildFrame()
4672 {
4673     EndChild();
4674 }
4675 
SetWindowConditionAllowFlags(ImGuiWindow * window,ImGuiCond flags,bool enabled)4676 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
4677 {
4678     window->SetWindowPosAllowFlags       = enabled ? (window->SetWindowPosAllowFlags       | flags) : (window->SetWindowPosAllowFlags       & ~flags);
4679     window->SetWindowSizeAllowFlags      = enabled ? (window->SetWindowSizeAllowFlags      | flags) : (window->SetWindowSizeAllowFlags      & ~flags);
4680     window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
4681 }
4682 
FindWindowByID(ImGuiID id)4683 ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
4684 {
4685     ImGuiContext& g = *GImGui;
4686     return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
4687 }
4688 
FindWindowByName(const char * name)4689 ImGuiWindow* ImGui::FindWindowByName(const char* name)
4690 {
4691     ImGuiID id = ImHashStr(name);
4692     return FindWindowByID(id);
4693 }
4694 
CreateNewWindow(const char * name,ImVec2 size,ImGuiWindowFlags flags)4695 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
4696 {
4697     ImGuiContext& g = *GImGui;
4698     //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags);
4699 
4700     // Create window the first time
4701     ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
4702     window->Flags = flags;
4703     g.WindowsById.SetVoidPtr(window->ID, window);
4704 
4705     // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
4706     window->Pos = ImVec2(60, 60);
4707 
4708     // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
4709     if (!(flags & ImGuiWindowFlags_NoSavedSettings))
4710         if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
4711         {
4712             // Retrieve settings from .ini file
4713             window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
4714             SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
4715             window->Pos = ImVec2(settings->Pos.x, settings->Pos.y);
4716             window->Collapsed = settings->Collapsed;
4717             if (settings->Size.x > 0 && settings->Size.y > 0)
4718                 size = ImVec2(settings->Size.x, settings->Size.y);
4719         }
4720     window->Size = window->SizeFull = ImFloor(size);
4721     window->DC.CursorStartPos = window->DC.CursorMaxPos = window->Pos; // So first call to CalcContentSize() doesn't return crazy values
4722 
4723     if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
4724     {
4725         window->AutoFitFramesX = window->AutoFitFramesY = 2;
4726         window->AutoFitOnlyGrows = false;
4727     }
4728     else
4729     {
4730         if (window->Size.x <= 0.0f)
4731             window->AutoFitFramesX = 2;
4732         if (window->Size.y <= 0.0f)
4733             window->AutoFitFramesY = 2;
4734         window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
4735     }
4736 
4737     g.WindowsFocusOrder.push_back(window);
4738     if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
4739         g.Windows.push_front(window); // Quite slow but rare and only once
4740     else
4741         g.Windows.push_back(window);
4742     return window;
4743 }
4744 
CalcWindowSizeAfterConstraint(ImGuiWindow * window,ImVec2 new_size)4745 static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size)
4746 {
4747     ImGuiContext& g = *GImGui;
4748     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)
4749     {
4750         // Using -1,-1 on either X/Y axis to preserve the current size.
4751         ImRect cr = g.NextWindowData.SizeConstraintRect;
4752         new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
4753         new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
4754         if (g.NextWindowData.SizeCallback)
4755         {
4756             ImGuiSizeCallbackData data;
4757             data.UserData = g.NextWindowData.SizeCallbackUserData;
4758             data.Pos = window->Pos;
4759             data.CurrentSize = window->SizeFull;
4760             data.DesiredSize = new_size;
4761             g.NextWindowData.SizeCallback(&data);
4762             new_size = data.DesiredSize;
4763         }
4764         new_size.x = IM_FLOOR(new_size.x);
4765         new_size.y = IM_FLOOR(new_size.y);
4766     }
4767 
4768     // Minimum size
4769     if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
4770     {
4771         new_size = ImMax(new_size, g.Style.WindowMinSize);
4772         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
4773     }
4774     return new_size;
4775 }
4776 
CalcWindowContentSize(ImGuiWindow * window)4777 static ImVec2 CalcWindowContentSize(ImGuiWindow* window)
4778 {
4779     if (window->Collapsed)
4780         if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
4781             return window->ContentSize;
4782     if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0)
4783         return window->ContentSize;
4784 
4785     ImVec2 sz;
4786     sz.x = IM_FLOOR((window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);
4787     sz.y = IM_FLOOR((window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);
4788     return sz;
4789 }
4790 
CalcWindowAutoFitSize(ImGuiWindow * window,const ImVec2 & size_contents)4791 static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents)
4792 {
4793     ImGuiContext& g = *GImGui;
4794     ImGuiStyle& style = g.Style;
4795     ImVec2 size_decorations = ImVec2(0.0f, window->TitleBarHeight() + window->MenuBarHeight());
4796     ImVec2 size_pad = window->WindowPadding * 2.0f;
4797     ImVec2 size_desired = size_contents + size_pad + size_decorations;
4798     if (window->Flags & ImGuiWindowFlags_Tooltip)
4799     {
4800         // Tooltip always resize
4801         return size_desired;
4802     }
4803     else
4804     {
4805         // Maximum window size is determined by the viewport size or monitor size
4806         const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0;
4807         const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0;
4808         ImVec2 size_min = style.WindowMinSize;
4809         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)
4810             size_min = ImMin(size_min, ImVec2(4.0f, 4.0f));
4811         ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, g.IO.DisplaySize - style.DisplaySafeAreaPadding * 2.0f));
4812 
4813         // When the window cannot fit all contents (either because of constraints, either because screen is too small),
4814         // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
4815         ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);
4816         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);
4817         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);
4818         if (will_have_scrollbar_x)
4819             size_auto_fit.y += style.ScrollbarSize;
4820         if (will_have_scrollbar_y)
4821             size_auto_fit.x += style.ScrollbarSize;
4822         return size_auto_fit;
4823     }
4824 }
4825 
CalcWindowExpectedSize(ImGuiWindow * window)4826 ImVec2 ImGui::CalcWindowExpectedSize(ImGuiWindow* window)
4827 {
4828     ImVec2 size_contents = CalcWindowContentSize(window);
4829     ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents);
4830     ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit);
4831     return size_final;
4832 }
4833 
GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)4834 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
4835 {
4836     if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
4837         return ImGuiCol_PopupBg;
4838     if (flags & ImGuiWindowFlags_ChildWindow)
4839         return ImGuiCol_ChildBg;
4840     return ImGuiCol_WindowBg;
4841 }
4842 
CalcResizePosSizeFromAnyCorner(ImGuiWindow * window,const ImVec2 & corner_target,const ImVec2 & corner_norm,ImVec2 * out_pos,ImVec2 * out_size)4843 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
4844 {
4845     ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm);                // Expected window upper-left
4846     ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
4847     ImVec2 size_expected = pos_max - pos_min;
4848     ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected);
4849     *out_pos = pos_min;
4850     if (corner_norm.x == 0.0f)
4851         out_pos->x -= (size_constrained.x - size_expected.x);
4852     if (corner_norm.y == 0.0f)
4853         out_pos->y -= (size_constrained.y - size_expected.y);
4854     *out_size = size_constrained;
4855 }
4856 
4857 struct ImGuiResizeGripDef
4858 {
4859     ImVec2  CornerPosN;
4860     ImVec2  InnerDir;
4861     int     AngleMin12, AngleMax12;
4862 };
4863 
4864 static const ImGuiResizeGripDef resize_grip_def[4] =
4865 {
4866     { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower-right
4867     { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower-left
4868     { ImVec2(0,0), ImVec2(+1,+1), 6, 9 }, // Upper-left (Unused)
4869     { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper-right (Unused)
4870 };
4871 
GetResizeBorderRect(ImGuiWindow * window,int border_n,float perp_padding,float thickness)4872 static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
4873 {
4874     ImRect rect = window->Rect();
4875     if (thickness == 0.0f) rect.Max -= ImVec2(1,1);
4876     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
4877     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
4878     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
4879     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
4880     IM_ASSERT(0);
4881     return ImRect();
4882 }
4883 
4884 // 0..3: corners (Lower-right, Lower-left, Unused, Unused)
4885 // 4..7: borders (Top, Right, Bottom, Left)
GetWindowResizeID(ImGuiWindow * window,int n)4886 ImGuiID ImGui::GetWindowResizeID(ImGuiWindow* window, int n)
4887 {
4888     IM_ASSERT(n >= 0 && n <= 7);
4889     ImGuiID id = window->ID;
4890     id = ImHashStr("#RESIZE", 0, id);
4891     id = ImHashData(&n, sizeof(int), id);
4892     return id;
4893 }
4894 
4895 // Handle resize for: Resize Grips, Borders, Gamepad
4896 // 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])4897 static bool ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4])
4898 {
4899     ImGuiContext& g = *GImGui;
4900     ImGuiWindowFlags flags = window->Flags;
4901 
4902     if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
4903         return false;
4904     if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window.
4905         return false;
4906 
4907     bool ret_auto_fit = false;
4908     const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0;
4909     const float grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
4910     const float grip_hover_inner_size = IM_FLOOR(grip_draw_size * 0.75f);
4911     const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS : 0.0f;
4912 
4913     ImVec2 pos_target(FLT_MAX, FLT_MAX);
4914     ImVec2 size_target(FLT_MAX, FLT_MAX);
4915 
4916     // Resize grips and borders are on layer 1
4917     window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
4918     window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu);
4919 
4920     // Manual resize grips
4921     PushID("#RESIZE");
4922     for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
4923     {
4924         const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
4925         const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
4926 
4927         // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
4928         ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size);
4929         if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
4930         if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
4931         bool hovered, held;
4932         ButtonBehavior(resize_rect, window->GetID(resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
4933         //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
4934         if (hovered || held)
4935             g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
4936 
4937         if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
4938         {
4939             // Manual auto-fit when double-clicking
4940             size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit);
4941             ret_auto_fit = true;
4942             ClearActiveID();
4943         }
4944         else if (held)
4945         {
4946             // Resize from any of the four corners
4947             // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
4948             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
4949             CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPosN, &pos_target, &size_target);
4950         }
4951         if (resize_grip_n == 0 || held || hovered)
4952             resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
4953     }
4954     for (int border_n = 0; border_n < resize_border_count; border_n++)
4955     {
4956         bool hovered, held;
4957         ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS);
4958         ButtonBehavior(border_rect, window->GetID(border_n + 4), &hovered, &held, ImGuiButtonFlags_FlattenChildren);
4959         //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
4960         if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held)
4961         {
4962             g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
4963             if (held)
4964                 *border_held = border_n;
4965         }
4966         if (held)
4967         {
4968             ImVec2 border_target = window->Pos;
4969             ImVec2 border_posn;
4970             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
4971             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
4972             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
4973             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
4974             CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target);
4975         }
4976     }
4977     PopID();
4978 
4979     // Navigation resize (keyboard/gamepad)
4980     if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
4981     {
4982         ImVec2 nav_resize_delta;
4983         if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift)
4984             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
4985         if (g.NavInputSource == ImGuiInputSource_NavGamepad)
4986             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down);
4987         if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f)
4988         {
4989             const float NAV_RESIZE_SPEED = 600.0f;
4990             nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
4991             g.NavWindowingToggleLayer = false;
4992             g.NavDisableMouseHover = true;
4993             resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
4994             // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
4995             size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + nav_resize_delta);
4996         }
4997     }
4998 
4999     // Apply back modified position/size to window
5000     if (size_target.x != FLT_MAX)
5001     {
5002         window->SizeFull = size_target;
5003         MarkIniSettingsDirty(window);
5004     }
5005     if (pos_target.x != FLT_MAX)
5006     {
5007         window->Pos = ImFloor(pos_target);
5008         MarkIniSettingsDirty(window);
5009     }
5010 
5011     // Resize nav layer
5012     window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5013     window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
5014 
5015     window->Size = window->SizeFull;
5016     return ret_auto_fit;
5017 }
5018 
ClampWindowRect(ImGuiWindow * window,const ImRect & rect,const ImVec2 & padding)5019 static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& rect, const ImVec2& padding)
5020 {
5021     ImGuiContext& g = *GImGui;
5022     ImVec2 size_for_clamping = (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) ? ImVec2(window->Size.x, window->TitleBarHeight()) : window->Size;
5023     window->Pos = ImMin(rect.Max - padding, ImMax(window->Pos + size_for_clamping, rect.Min + padding) - size_for_clamping);
5024 }
5025 
RenderWindowOuterBorders(ImGuiWindow * window)5026 static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
5027 {
5028     ImGuiContext& g = *GImGui;
5029     float rounding = window->WindowRounding;
5030     float border_size = window->WindowBorderSize;
5031     if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground))
5032         window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
5033 
5034     int border_held = window->ResizeBorderHeld;
5035     if (border_held != -1)
5036     {
5037         struct ImGuiResizeBorderDef
5038         {
5039             ImVec2 InnerDir;
5040             ImVec2 CornerPosN1, CornerPosN2;
5041             float  OuterAngle;
5042         };
5043         static const ImGuiResizeBorderDef resize_border_def[4] =
5044         {
5045             { ImVec2(0,+1), ImVec2(0,0), ImVec2(1,0), IM_PI*1.50f }, // Top
5046             { ImVec2(-1,0), ImVec2(1,0), ImVec2(1,1), IM_PI*0.00f }, // Right
5047             { ImVec2(0,-1), ImVec2(1,1), ImVec2(0,1), IM_PI*0.50f }, // Bottom
5048             { ImVec2(+1,0), ImVec2(0,1), ImVec2(0,0), IM_PI*1.00f }  // Left
5049         };
5050         const ImGuiResizeBorderDef& def = resize_border_def[border_held];
5051         ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f);
5052         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);
5053         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);
5054         window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), false, ImMax(2.0f, border_size)); // Thicker than usual
5055     }
5056     if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
5057     {
5058         float y = window->Pos.y + window->TitleBarHeight() - 1;
5059         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);
5060     }
5061 }
5062 
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)5063 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)
5064 {
5065     ImGuiContext& g = *GImGui;
5066     ImGuiStyle& style = g.Style;
5067     ImGuiWindowFlags flags = window->Flags;
5068 
5069     // Ensure that ScrollBar doesn't read last frame's SkipItems
5070     window->SkipItems = false;
5071 
5072     // Draw window + handle manual resize
5073     // 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.
5074     const float window_rounding = window->WindowRounding;
5075     const float window_border_size = window->WindowBorderSize;
5076     if (window->Collapsed)
5077     {
5078         // Title bar only
5079         float backup_border_size = style.FrameBorderSize;
5080         g.Style.FrameBorderSize = window->WindowBorderSize;
5081         ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
5082         RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
5083         g.Style.FrameBorderSize = backup_border_size;
5084     }
5085     else
5086     {
5087         // Window background
5088         if (!(flags & ImGuiWindowFlags_NoBackground))
5089         {
5090             ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
5091             float alpha = 1.0f;
5092             if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha)
5093                 alpha = g.NextWindowData.BgAlphaVal;
5094             if (alpha != 1.0f)
5095                 bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
5096             window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot);
5097         }
5098 
5099         // Title bar
5100         if (!(flags & ImGuiWindowFlags_NoTitleBar))
5101         {
5102             ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
5103             window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top);
5104         }
5105 
5106         // Menu bar
5107         if (flags & ImGuiWindowFlags_MenuBar)
5108         {
5109             ImRect menu_bar_rect = window->MenuBarRect();
5110             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.
5111             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);
5112             if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
5113                 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
5114         }
5115 
5116         // Scrollbars
5117         if (window->ScrollbarX)
5118             Scrollbar(ImGuiAxis_X);
5119         if (window->ScrollbarY)
5120             Scrollbar(ImGuiAxis_Y);
5121 
5122         // Render resize grips (after their input handling so we don't have a frame of latency)
5123         if (!(flags & ImGuiWindowFlags_NoResize))
5124         {
5125             for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5126             {
5127                 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5128                 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
5129                 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)));
5130                 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)));
5131                 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);
5132                 window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]);
5133             }
5134         }
5135 
5136         // Borders
5137         RenderWindowOuterBorders(window);
5138     }
5139 }
5140 
5141 // Render title text, collapse button, close button
RenderWindowTitleBarContents(ImGuiWindow * window,const ImRect & title_bar_rect,const char * name,bool * p_open)5142 void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)
5143 {
5144     ImGuiContext& g = *GImGui;
5145     ImGuiStyle& style = g.Style;
5146     ImGuiWindowFlags flags = window->Flags;
5147 
5148     const bool has_close_button = (p_open != NULL);
5149     const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None);
5150 
5151     // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)
5152     const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
5153     window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
5154     window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5155     window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu);
5156 
5157     // Layout buttons
5158     // FIXME: Would be nice to generalize the subtleties expressed here into reusable code.
5159     float pad_l = style.FramePadding.x;
5160     float pad_r = style.FramePadding.x;
5161     float button_sz = g.FontSize;
5162     ImVec2 close_button_pos;
5163     ImVec2 collapse_button_pos;
5164     if (has_close_button)
5165     {
5166         pad_r += button_sz;
5167         close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5168     }
5169     if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)
5170     {
5171         pad_r += button_sz;
5172         collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5173     }
5174     if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)
5175     {
5176         collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l - style.FramePadding.x, title_bar_rect.Min.y);
5177         pad_l += button_sz;
5178     }
5179 
5180     // Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
5181     if (has_collapse_button)
5182         if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos))
5183             window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function
5184 
5185     // Close button
5186     if (has_close_button)
5187         if (CloseButton(window->GetID("#CLOSE"), close_button_pos))
5188             *p_open = false;
5189 
5190     window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5191     window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
5192     window->DC.ItemFlags = item_flags_backup;
5193 
5194     // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
5195     // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
5196     const char* UNSAVED_DOCUMENT_MARKER = "*";
5197     const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f;
5198     const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
5199 
5200     // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
5201     // while uncentered title text will still reach edges correct.
5202     if (pad_l > style.FramePadding.x)
5203         pad_l += g.Style.ItemInnerSpacing.x;
5204     if (pad_r > style.FramePadding.x)
5205         pad_r += g.Style.ItemInnerSpacing.x;
5206     if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f)
5207     {
5208         float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center
5209         float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x);
5210         pad_l = ImMax(pad_l, pad_extend * centerness);
5211         pad_r = ImMax(pad_r, pad_extend * centerness);
5212     }
5213 
5214     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);
5215     ImRect clip_r(layout_r.Min.x, layout_r.Min.y, layout_r.Max.x + g.Style.ItemInnerSpacing.x, layout_r.Max.y);
5216     //if (g.IO.KeyCtrl) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
5217     RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r);
5218     if (flags & ImGuiWindowFlags_UnsavedDocument)
5219     {
5220         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);
5221         ImVec2 off = ImVec2(0.0f, IM_FLOOR(-g.FontSize * 0.25f));
5222         RenderTextClipped(marker_pos + off, layout_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_r);
5223     }
5224 }
5225 
UpdateWindowParentAndRootLinks(ImGuiWindow * window,ImGuiWindowFlags flags,ImGuiWindow * parent_window)5226 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
5227 {
5228     window->ParentWindow = parent_window;
5229     window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
5230     if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
5231         window->RootWindow = parent_window->RootWindow;
5232     if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
5233         window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
5234     while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
5235     {
5236         IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);
5237         window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
5238     }
5239 }
5240 
5241 // Push a new Dear ImGui window to add widgets to.
5242 // - 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.
5243 // - Begin/End can be called multiple times during the frame with the same window name to append content.
5244 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
5245 //   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.
5246 // - 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.
5247 // - 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)5248 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
5249 {
5250     ImGuiContext& g = *GImGui;
5251     const ImGuiStyle& style = g.Style;
5252     IM_ASSERT(name != NULL && name[0] != '\0');     // Window name required
5253     IM_ASSERT(g.WithinFrameScope);                  // Forgot to call ImGui::NewFrame()
5254     IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
5255 
5256     // Find or create
5257     ImGuiWindow* window = FindWindowByName(name);
5258     const bool window_just_created = (window == NULL);
5259     if (window_just_created)
5260     {
5261         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.
5262         window = CreateNewWindow(name, size_on_first_use, flags);
5263     }
5264 
5265     // Automatically disable manual moving/resizing when NoInputs is set
5266     if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
5267         flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
5268 
5269     if (flags & ImGuiWindowFlags_NavFlattened)
5270         IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
5271 
5272     const int current_frame = g.FrameCount;
5273     const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
5274 
5275     // Update the Appearing flag
5276     bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1);   // Not using !WasActive because the implicit "Debug" window would always toggle off->on
5277     const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);
5278     if (flags & ImGuiWindowFlags_Popup)
5279     {
5280         ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5281         window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
5282         window_just_activated_by_user |= (window != popup_ref.Window);
5283     }
5284     window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
5285     if (window->Appearing)
5286         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
5287 
5288     // Update Flags, LastFrameActive, BeginOrderXXX fields
5289     if (first_begin_of_the_frame)
5290     {
5291         window->Flags = (ImGuiWindowFlags)flags;
5292         window->LastFrameActive = current_frame;
5293         window->LastTimeActive = (float)g.Time;
5294         window->BeginOrderWithinParent = 0;
5295         window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
5296     }
5297     else
5298     {
5299         flags = window->Flags;
5300     }
5301 
5302     // 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
5303     ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
5304     ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
5305     IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
5306 
5307     // We allow window memory to be compacted so recreate the base stack when needed.
5308     if (window->IDStack.Size == 0)
5309         window->IDStack.push_back(window->ID);
5310 
5311     // Add to stack
5312     // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
5313     g.CurrentWindowStack.push_back(window);
5314     g.CurrentWindow = NULL;
5315     ErrorCheckBeginEndCompareStacksSize(window, true);
5316     if (flags & ImGuiWindowFlags_Popup)
5317     {
5318         ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5319         popup_ref.Window = window;
5320         g.BeginPopupStack.push_back(popup_ref);
5321         window->PopupId = popup_ref.PopupId;
5322     }
5323 
5324     if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow))
5325         window->NavLastIds[0] = 0;
5326 
5327     // Process SetNextWindow***() calls
5328     bool window_pos_set_by_api = false;
5329     bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
5330     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos)
5331     {
5332         window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
5333         if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
5334         {
5335             // May be processed on the next frame if this is our first frame and we are measuring size
5336             // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
5337             window->SetWindowPosVal = g.NextWindowData.PosVal;
5338             window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
5339             window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5340         }
5341         else
5342         {
5343             SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
5344         }
5345     }
5346     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)
5347     {
5348         window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
5349         window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
5350         SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
5351     }
5352     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize)
5353         window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;
5354     else if (first_begin_of_the_frame)
5355         window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);
5356     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed)
5357         SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
5358     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus)
5359         FocusWindow(window);
5360     if (window->Appearing)
5361         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
5362 
5363     // When reusing window again multiple times a frame, just append content (don't need to setup again)
5364     if (first_begin_of_the_frame)
5365     {
5366         // Initialize
5367         const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
5368         UpdateWindowParentAndRootLinks(window, flags, parent_window);
5369 
5370         window->Active = true;
5371         window->HasCloseButton = (p_open != NULL);
5372         window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX);
5373         window->IDStack.resize(1);
5374 
5375         // Restore buffer capacity when woken from a compacted state, to avoid
5376         if (window->MemoryCompacted)
5377             GcAwakeTransientWindowBuffers(window);
5378 
5379         // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
5380         // 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.
5381         bool window_title_visible_elsewhere = false;
5382         if (g.NavWindowingList != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0)   // Window titles visible when using CTRL+TAB
5383             window_title_visible_elsewhere = true;
5384         if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
5385         {
5386             size_t buf_len = (size_t)window->NameBufLen;
5387             window->Name = ImStrdupcpy(window->Name, &buf_len, name);
5388             window->NameBufLen = (int)buf_len;
5389         }
5390 
5391         // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
5392 
5393         // Update contents size from last frame for auto-fitting (or use explicit size)
5394         window->ContentSize = CalcWindowContentSize(window);
5395         if (window->HiddenFramesCanSkipItems > 0)
5396             window->HiddenFramesCanSkipItems--;
5397         if (window->HiddenFramesCannotSkipItems > 0)
5398             window->HiddenFramesCannotSkipItems--;
5399 
5400         // Hide new windows for one frame until they calculate their size
5401         if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
5402             window->HiddenFramesCannotSkipItems = 1;
5403 
5404         // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
5405         // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
5406         if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
5407         {
5408             window->HiddenFramesCannotSkipItems = 1;
5409             if (flags & ImGuiWindowFlags_AlwaysAutoResize)
5410             {
5411                 if (!window_size_x_set_by_api)
5412                     window->Size.x = window->SizeFull.x = 0.f;
5413                 if (!window_size_y_set_by_api)
5414                     window->Size.y = window->SizeFull.y = 0.f;
5415                 window->ContentSize = ImVec2(0.f, 0.f);
5416             }
5417         }
5418 
5419         // FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style)
5420         SetCurrentWindow(window);
5421 
5422         // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)
5423 
5424         if (flags & ImGuiWindowFlags_ChildWindow)
5425             window->WindowBorderSize = style.ChildBorderSize;
5426         else
5427             window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
5428         window->WindowPadding = style.WindowPadding;
5429         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
5430             window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
5431         window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
5432         window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
5433 
5434         // Collapse window by double-clicking on title bar
5435         // 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
5436         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
5437         {
5438             // 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.
5439             ImRect title_bar_rect = window->TitleBarRect();
5440             if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
5441                 window->WantCollapseToggle = true;
5442             if (window->WantCollapseToggle)
5443             {
5444                 window->Collapsed = !window->Collapsed;
5445                 MarkIniSettingsDirty(window);
5446                 FocusWindow(window);
5447             }
5448         }
5449         else
5450         {
5451             window->Collapsed = false;
5452         }
5453         window->WantCollapseToggle = false;
5454 
5455         // SIZE
5456 
5457         // Calculate auto-fit size, handle automatic resize
5458         const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSize);
5459         bool use_current_size_for_scrollbar_x = window_just_created;
5460         bool use_current_size_for_scrollbar_y = window_just_created;
5461         if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
5462         {
5463             // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
5464             if (!window_size_x_set_by_api)
5465             {
5466                 window->SizeFull.x = size_auto_fit.x;
5467                 use_current_size_for_scrollbar_x = true;
5468             }
5469             if (!window_size_y_set_by_api)
5470             {
5471                 window->SizeFull.y = size_auto_fit.y;
5472                 use_current_size_for_scrollbar_y = true;
5473             }
5474         }
5475         else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
5476         {
5477             // Auto-fit may only grow window during the first few frames
5478             // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
5479             if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
5480             {
5481                 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
5482                 use_current_size_for_scrollbar_x = true;
5483             }
5484             if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
5485             {
5486                 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
5487                 use_current_size_for_scrollbar_y = true;
5488             }
5489             if (!window->Collapsed)
5490                 MarkIniSettingsDirty(window);
5491         }
5492 
5493         // Apply minimum/maximum window size constraints and final size
5494         window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull);
5495         window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
5496 
5497         // Decoration size
5498         const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
5499 
5500         // POSITION
5501 
5502         // Popup latch its initial position, will position itself when it appears next frame
5503         if (window_just_activated_by_user)
5504         {
5505             window->AutoPosLastDirection = ImGuiDir_None;
5506             if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
5507                 window->Pos = g.BeginPopupStack.back().OpenPopupPos;
5508         }
5509 
5510         // Position child window
5511         if (flags & ImGuiWindowFlags_ChildWindow)
5512         {
5513             IM_ASSERT(parent_window && parent_window->Active);
5514             window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
5515             parent_window->DC.ChildWindows.push_back(window);
5516             if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
5517                 window->Pos = parent_window->DC.CursorPos;
5518         }
5519 
5520         const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0);
5521         if (window_pos_with_pivot)
5522             SetWindowPos(window, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering)
5523         else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
5524             window->Pos = FindBestWindowPosForPopup(window);
5525         else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
5526             window->Pos = FindBestWindowPosForPopup(window);
5527         else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
5528             window->Pos = FindBestWindowPosForPopup(window);
5529 
5530         // Clamp position/size so window stays visible within its viewport or monitor
5531 
5532         // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
5533         ImRect viewport_rect(GetViewportRect());
5534         if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5535         {
5536             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.
5537             {
5538                 ImVec2 clamp_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
5539                 ClampWindowRect(window, viewport_rect, clamp_padding);
5540             }
5541         }
5542         window->Pos = ImFloor(window->Pos);
5543 
5544         // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
5545         window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
5546 
5547         // Apply window focus (new and reactivated windows are moved to front)
5548         bool want_focus = false;
5549         if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
5550         {
5551             if (flags & ImGuiWindowFlags_Popup)
5552                 want_focus = true;
5553             else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0)
5554                 want_focus = true;
5555         }
5556 
5557         // Handle manual resize: Resize Grips, Borders, Gamepad
5558         int border_held = -1;
5559         ImU32 resize_grip_col[4] = {};
5560         const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it.
5561         const float resize_grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
5562         if (!window->Collapsed)
5563             if (UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]))
5564                 use_current_size_for_scrollbar_x = use_current_size_for_scrollbar_y = true;
5565         window->ResizeBorderHeld = (signed char)border_held;
5566 
5567         // SCROLLBAR VISIBILITY
5568 
5569         // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).
5570         if (!window->Collapsed)
5571         {
5572             // When reading the current size we need to read it after size constraints have been applied.
5573             // When we use InnerRect here we are intentionally reading last frame size, same for ScrollbarSizes values before we set them again.
5574             ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - decoration_up_height);
5575             ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + window->ScrollbarSizes;
5576             ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;
5577             float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;
5578             float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;
5579             //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?
5580             window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
5581             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));
5582             if (window->ScrollbarX && !window->ScrollbarY)
5583                 window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar);
5584             window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
5585         }
5586 
5587         // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)
5588         // Update various regions. Variables they depends on should be set above in this function.
5589         // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.
5590 
5591         // Outer rectangle
5592         // Not affected by window border size. Used by:
5593         // - FindHoveredWindow() (w/ extra padding when border resize is enabled)
5594         // - Begin() initial clipping rect for drawing window background and borders.
5595         // - Begin() clipping whole child
5596         const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;
5597         const ImRect outer_rect = window->Rect();
5598         const ImRect title_bar_rect = window->TitleBarRect();
5599         window->OuterRectClipped = outer_rect;
5600         window->OuterRectClipped.ClipWith(host_rect);
5601 
5602         // Inner rectangle
5603         // Not affected by window border size. Used by:
5604         // - InnerClipRect
5605         // - ScrollToBringRectIntoView()
5606         // - NavUpdatePageUpPageDown()
5607         // - Scrollbar()
5608         window->InnerRect.Min.x = window->Pos.x;
5609         window->InnerRect.Min.y = window->Pos.y + decoration_up_height;
5610         window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x;
5611         window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y;
5612 
5613         // Inner clipping rectangle.
5614         // Will extend a little bit outside the normal work region.
5615         // This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
5616         // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
5617         // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
5618         // Affected by window/frame border size. Used by:
5619         // - Begin() initial clip rect
5620         float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
5621         window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
5622         window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size);
5623         window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
5624         window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize);
5625         window->InnerClipRect.ClipWithFull(host_rect);
5626 
5627         // Default item width. Make it proportional to window size if window manually resizes
5628         if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
5629             window->ItemWidthDefault = ImFloor(window->Size.x * 0.65f);
5630         else
5631             window->ItemWidthDefault = ImFloor(g.FontSize * 16.0f);
5632 
5633         // SCROLLING
5634 
5635         // Lock down maximum scrolling
5636         // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate
5637         // for right/bottom aligned items without creating a scrollbar.
5638         window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth());
5639         window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());
5640 
5641         // Apply scrolling
5642         window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window, true);
5643         window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
5644 
5645         // DRAWING
5646 
5647         // Setup draw list and outer clipping rectangle
5648         window->DrawList->Clear();
5649         window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
5650         PushClipRect(host_rect.Min, host_rect.Max, false);
5651 
5652         // Draw modal window background (darkens what is behind them, all viewports)
5653         const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0;
5654         const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow);
5655         if (dim_bg_for_modal || dim_bg_for_window_list)
5656         {
5657             const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
5658             window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col);
5659         }
5660 
5661         // Draw navigation selection/windowing rectangle background
5662         if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim)
5663         {
5664             ImRect bb = window->Rect();
5665             bb.Expand(g.FontSize);
5666             if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway
5667                 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
5668         }
5669 
5670         // Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call.
5671         // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
5672         // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child.
5673         // We also disabled this when we have dimming overlay behind this specific one child.
5674         // 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.
5675         bool render_decorations_in_parent = false;
5676         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
5677             if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0)
5678                 render_decorations_in_parent = true;
5679         if (render_decorations_in_parent)
5680             window->DrawList = parent_window->DrawList;
5681 
5682         // Handle title bar, scrollbar, resize grips and resize borders
5683         const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
5684         const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
5685         RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, resize_grip_count, resize_grip_col, resize_grip_draw_size);
5686 
5687         if (render_decorations_in_parent)
5688             window->DrawList = &window->DrawListInst;
5689 
5690         // Draw navigation selection/windowing rectangle border
5691         if (g.NavWindowingTargetAnim == window)
5692         {
5693             float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
5694             ImRect bb = window->Rect();
5695             bb.Expand(g.FontSize);
5696             if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward
5697             {
5698                 bb.Expand(-g.FontSize - 1.0f);
5699                 rounding = window->WindowRounding;
5700             }
5701             window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f);
5702         }
5703 
5704         // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
5705 
5706         // Work rectangle.
5707         // Affected by window padding and border size. Used by:
5708         // - Columns() for right-most edge
5709         // - TreeNode(), CollapsingHeader() for right-most edge
5710         // - BeginTabBar() for right-most edge
5711         const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);
5712         const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);
5713         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));
5714         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));
5715         window->WorkRect.Min.x = ImFloor(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));
5716         window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
5717         window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
5718         window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
5719 
5720         // [LEGACY] Content Region
5721         // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
5722         // Used by:
5723         // - Mouse wheel scrolling + many other things
5724         window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
5725         window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + decoration_up_height;
5726         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));
5727         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));
5728 
5729         // Setup drawing context
5730         // (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.)
5731         window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x;
5732         window->DC.GroupOffset.x = 0.0f;
5733         window->DC.ColumnsOffset.x = 0.0f;
5734         window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, decoration_up_height + window->WindowPadding.y - window->Scroll.y);
5735         window->DC.CursorPos = window->DC.CursorStartPos;
5736         window->DC.CursorPosPrevLine = window->DC.CursorPos;
5737         window->DC.CursorMaxPos = window->DC.CursorStartPos;
5738         window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
5739         window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
5740         window->DC.NavHideHighlightOneFrame = false;
5741         window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f);
5742         window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext;
5743         window->DC.NavLayerActiveMaskNext = 0x00;
5744         window->DC.MenuBarAppending = false;
5745         window->DC.ChildWindows.resize(0);
5746         window->DC.LayoutType = ImGuiLayoutType_Vertical;
5747         window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
5748         window->DC.FocusCounterAll = window->DC.FocusCounterTab = -1;
5749         window->DC.ItemFlags = parent_window ? parent_window->DC.ItemFlags : ImGuiItemFlags_Default_;
5750         window->DC.ItemWidth = window->ItemWidthDefault;
5751         window->DC.TextWrapPos = -1.0f; // disabled
5752         window->DC.ItemFlagsStack.resize(0);
5753         window->DC.ItemWidthStack.resize(0);
5754         window->DC.TextWrapPosStack.resize(0);
5755         window->DC.CurrentColumns = NULL;
5756         window->DC.TreeDepth = 0;
5757         window->DC.TreeMayJumpToParentOnPopMask = 0x00;
5758         window->DC.StateStorage = &window->StateStorage;
5759         window->DC.GroupStack.resize(0);
5760         window->MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user);
5761 
5762         if ((flags & ImGuiWindowFlags_ChildWindow) && (window->DC.ItemFlags != parent_window->DC.ItemFlags))
5763         {
5764             window->DC.ItemFlags = parent_window->DC.ItemFlags;
5765             window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
5766         }
5767 
5768         if (window->AutoFitFramesX > 0)
5769             window->AutoFitFramesX--;
5770         if (window->AutoFitFramesY > 0)
5771             window->AutoFitFramesY--;
5772 
5773         // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
5774         if (want_focus)
5775         {
5776             FocusWindow(window);
5777             NavInitWindow(window, false);
5778         }
5779 
5780         // Title bar
5781         if (!(flags & ImGuiWindowFlags_NoTitleBar))
5782             RenderWindowTitleBarContents(window, title_bar_rect, name, p_open);
5783 
5784         // Pressing CTRL+C while holding on a window copy its content to the clipboard
5785         // 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.
5786         // Maybe we can support CTRL+C on every element?
5787         /*
5788         if (g.ActiveId == move_id)
5789             if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
5790                 LogToClipboard();
5791         */
5792 
5793         // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
5794         // This is useful to allow creating context menus on title bar only, etc.
5795         window->DC.LastItemId = window->MoveId;
5796         window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;
5797         window->DC.LastItemRect = title_bar_rect;
5798 #ifdef IMGUI_ENABLE_TEST_ENGINE
5799         if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
5800             IMGUI_TEST_ENGINE_ITEM_ADD(window->DC.LastItemRect, window->DC.LastItemId);
5801 #endif
5802     }
5803     else
5804     {
5805         // Append
5806         SetCurrentWindow(window);
5807     }
5808 
5809     PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
5810 
5811     // 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)
5812     if (first_begin_of_the_frame)
5813         window->WriteAccessed = false;
5814 
5815     window->BeginCount++;
5816     g.NextWindowData.ClearFlags();
5817 
5818     if (flags & ImGuiWindowFlags_ChildWindow)
5819     {
5820         // Child window can be out of sight and have "negative" clip windows.
5821         // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
5822         IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
5823         if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5824             if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
5825                 window->HiddenFramesCanSkipItems = 1;
5826 
5827         // Hide along with parent or if parent is collapsed
5828         if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))
5829             window->HiddenFramesCanSkipItems = 1;
5830         if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0))
5831             window->HiddenFramesCannotSkipItems = 1;
5832     }
5833 
5834     // 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)
5835     if (style.Alpha <= 0.0f)
5836         window->HiddenFramesCanSkipItems = 1;
5837 
5838     // Update the Hidden flag
5839     window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0);
5840 
5841     // Update the SkipItems flag, used to early out of all items functions (no layout required)
5842     bool skip_items = false;
5843     if (window->Collapsed || !window->Active || window->Hidden)
5844         if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0)
5845             skip_items = true;
5846     window->SkipItems = skip_items;
5847 
5848     return !skip_items;
5849 }
5850 
End()5851 void ImGui::End()
5852 {
5853     ImGuiContext& g = *GImGui;
5854     ImGuiWindow* window = g.CurrentWindow;
5855 
5856     // Error checking: verify that user hasn't called End() too many times!
5857     if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow)
5858     {
5859         IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!");
5860         return;
5861     }
5862     IM_ASSERT(g.CurrentWindowStack.Size > 0);
5863 
5864     // Error checking: verify that user doesn't directly call End() on a child window.
5865     if (window->Flags & ImGuiWindowFlags_ChildWindow)
5866         IM_ASSERT_USER_ERROR(g.WithinEndChild, "Must call EndChild() and not End()!");
5867 
5868     // Close anything that is open
5869     if (window->DC.CurrentColumns)
5870         EndColumns();
5871     PopClipRect();   // Inner window clip rectangle
5872 
5873     // Stop logging
5874     if (!(window->Flags & ImGuiWindowFlags_ChildWindow))    // FIXME: add more options for scope of logging
5875         LogFinish();
5876 
5877     // Pop from window stack
5878     g.CurrentWindowStack.pop_back();
5879     if (window->Flags & ImGuiWindowFlags_Popup)
5880         g.BeginPopupStack.pop_back();
5881     ErrorCheckBeginEndCompareStacksSize(window, false);
5882     SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
5883 }
5884 
BringWindowToFocusFront(ImGuiWindow * window)5885 void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
5886 {
5887     ImGuiContext& g = *GImGui;
5888     if (g.WindowsFocusOrder.back() == window)
5889         return;
5890     for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the top-most window
5891         if (g.WindowsFocusOrder[i] == window)
5892         {
5893             memmove(&g.WindowsFocusOrder[i], &g.WindowsFocusOrder[i + 1], (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*));
5894             g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window;
5895             break;
5896         }
5897 }
5898 
BringWindowToDisplayFront(ImGuiWindow * window)5899 void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
5900 {
5901     ImGuiContext& g = *GImGui;
5902     ImGuiWindow* current_front_window = g.Windows.back();
5903     if (current_front_window == window || current_front_window->RootWindow == window)
5904         return;
5905     for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window
5906         if (g.Windows[i] == window)
5907         {
5908             memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
5909             g.Windows[g.Windows.Size - 1] = window;
5910             break;
5911         }
5912 }
5913 
BringWindowToDisplayBack(ImGuiWindow * window)5914 void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
5915 {
5916     ImGuiContext& g = *GImGui;
5917     if (g.Windows[0] == window)
5918         return;
5919     for (int i = 0; i < g.Windows.Size; i++)
5920         if (g.Windows[i] == window)
5921         {
5922             memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
5923             g.Windows[0] = window;
5924             break;
5925         }
5926 }
5927 
5928 // Moving window to front of display and set focus (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)5929 void ImGui::FocusWindow(ImGuiWindow* window)
5930 {
5931     ImGuiContext& g = *GImGui;
5932 
5933     if (g.NavWindow != window)
5934     {
5935         g.NavWindow = window;
5936         if (window && g.NavDisableMouseHover)
5937             g.NavMousePosDirty = true;
5938         g.NavInitRequest = false;
5939         g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
5940         g.NavIdIsAlive = false;
5941         g.NavLayer = ImGuiNavLayer_Main;
5942         //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL);
5943     }
5944 
5945     // Close popups if any
5946     ClosePopupsOverWindow(window, false);
5947 
5948     // Passing NULL allow to disable keyboard focus
5949     if (!window)
5950         return;
5951 
5952     // Move the root window to the top of the pile
5953     if (window->RootWindow)
5954         window = window->RootWindow;
5955 
5956     // Steal focus on active widgets
5957     if (window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement should be unnecessary. Need further testing before removing it..
5958         if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != window)
5959             ClearActiveID();
5960 
5961     // Bring to front
5962     BringWindowToFocusFront(window);
5963     if (!(window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus))
5964         BringWindowToDisplayFront(window);
5965 }
5966 
FocusTopMostWindowUnderOne(ImGuiWindow * under_this_window,ImGuiWindow * ignore_window)5967 void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window)
5968 {
5969     ImGuiContext& g = *GImGui;
5970 
5971     int start_idx = g.WindowsFocusOrder.Size - 1;
5972     if (under_this_window != NULL)
5973     {
5974         int under_this_window_idx = FindWindowFocusIndex(under_this_window);
5975         if (under_this_window_idx != -1)
5976             start_idx = under_this_window_idx - 1;
5977     }
5978     for (int i = start_idx; i >= 0; i--)
5979     {
5980         // 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.
5981         ImGuiWindow* window = g.WindowsFocusOrder[i];
5982         if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow))
5983             if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
5984             {
5985                 ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window);
5986                 FocusWindow(focus_window);
5987                 return;
5988             }
5989     }
5990     FocusWindow(NULL);
5991 }
5992 
SetNextItemWidth(float item_width)5993 void ImGui::SetNextItemWidth(float item_width)
5994 {
5995     ImGuiContext& g = *GImGui;
5996     g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasWidth;
5997     g.NextItemData.Width = item_width;
5998 }
5999 
PushItemWidth(float item_width)6000 void ImGui::PushItemWidth(float item_width)
6001 {
6002     ImGuiContext& g = *GImGui;
6003     ImGuiWindow* window = g.CurrentWindow;
6004     window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
6005     window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
6006     g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
6007 }
6008 
PushMultiItemsWidths(int components,float w_full)6009 void ImGui::PushMultiItemsWidths(int components, float w_full)
6010 {
6011     ImGuiContext& g = *GImGui;
6012     ImGuiWindow* window = g.CurrentWindow;
6013     const ImGuiStyle& style = g.Style;
6014     const float w_item_one  = ImMax(1.0f, IM_FLOOR((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
6015     const float w_item_last = ImMax(1.0f, IM_FLOOR(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
6016     window->DC.ItemWidthStack.push_back(w_item_last);
6017     for (int i = 0; i < components-1; i++)
6018         window->DC.ItemWidthStack.push_back(w_item_one);
6019     window->DC.ItemWidth = window->DC.ItemWidthStack.back();
6020     g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
6021 }
6022 
PopItemWidth()6023 void ImGui::PopItemWidth()
6024 {
6025     ImGuiWindow* window = GetCurrentWindow();
6026     window->DC.ItemWidthStack.pop_back();
6027     window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
6028 }
6029 
6030 // Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().
6031 // The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()
CalcItemWidth()6032 float ImGui::CalcItemWidth()
6033 {
6034     ImGuiContext& g = *GImGui;
6035     ImGuiWindow* window = g.CurrentWindow;
6036     float w;
6037     if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth)
6038         w = g.NextItemData.Width;
6039     else
6040         w = window->DC.ItemWidth;
6041     if (w < 0.0f)
6042     {
6043         float region_max_x = GetContentRegionMaxAbs().x;
6044         w = ImMax(1.0f, region_max_x - window->DC.CursorPos.x + w);
6045     }
6046     w = IM_FLOOR(w);
6047     return w;
6048 }
6049 
6050 // [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().
6051 // Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.
6052 // Note that only CalcItemWidth() is publicly exposed.
6053 // 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)6054 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)
6055 {
6056     ImGuiWindow* window = GImGui->CurrentWindow;
6057 
6058     ImVec2 region_max;
6059     if (size.x < 0.0f || size.y < 0.0f)
6060         region_max = GetContentRegionMaxAbs();
6061 
6062     if (size.x == 0.0f)
6063         size.x = default_w;
6064     else if (size.x < 0.0f)
6065         size.x = ImMax(4.0f, region_max.x - window->DC.CursorPos.x + size.x);
6066 
6067     if (size.y == 0.0f)
6068         size.y = default_h;
6069     else if (size.y < 0.0f)
6070         size.y = ImMax(4.0f, region_max.y - window->DC.CursorPos.y + size.y);
6071 
6072     return size;
6073 }
6074 
SetCurrentFont(ImFont * font)6075 void ImGui::SetCurrentFont(ImFont* font)
6076 {
6077     ImGuiContext& g = *GImGui;
6078     IM_ASSERT(font && font->IsLoaded());    // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
6079     IM_ASSERT(font->Scale > 0.0f);
6080     g.Font = font;
6081     g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale);
6082     g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
6083 
6084     ImFontAtlas* atlas = g.Font->ContainerAtlas;
6085     g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
6086     g.DrawListSharedData.Font = g.Font;
6087     g.DrawListSharedData.FontSize = g.FontSize;
6088 }
6089 
PushFont(ImFont * font)6090 void ImGui::PushFont(ImFont* font)
6091 {
6092     ImGuiContext& g = *GImGui;
6093     if (!font)
6094         font = GetDefaultFont();
6095     SetCurrentFont(font);
6096     g.FontStack.push_back(font);
6097     g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
6098 }
6099 
PopFont()6100 void  ImGui::PopFont()
6101 {
6102     ImGuiContext& g = *GImGui;
6103     g.CurrentWindow->DrawList->PopTextureID();
6104     g.FontStack.pop_back();
6105     SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
6106 }
6107 
PushItemFlag(ImGuiItemFlags option,bool enabled)6108 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
6109 {
6110     ImGuiWindow* window = GetCurrentWindow();
6111     if (enabled)
6112         window->DC.ItemFlags |= option;
6113     else
6114         window->DC.ItemFlags &= ~option;
6115     window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
6116 }
6117 
PopItemFlag()6118 void ImGui::PopItemFlag()
6119 {
6120     ImGuiWindow* window = GetCurrentWindow();
6121     window->DC.ItemFlagsStack.pop_back();
6122     window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back();
6123 }
6124 
6125 // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system.
PushAllowKeyboardFocus(bool allow_keyboard_focus)6126 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
6127 {
6128     PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus);
6129 }
6130 
PopAllowKeyboardFocus()6131 void ImGui::PopAllowKeyboardFocus()
6132 {
6133     PopItemFlag();
6134 }
6135 
PushButtonRepeat(bool repeat)6136 void ImGui::PushButtonRepeat(bool repeat)
6137 {
6138     PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
6139 }
6140 
PopButtonRepeat()6141 void ImGui::PopButtonRepeat()
6142 {
6143     PopItemFlag();
6144 }
6145 
PushTextWrapPos(float wrap_pos_x)6146 void ImGui::PushTextWrapPos(float wrap_pos_x)
6147 {
6148     ImGuiWindow* window = GetCurrentWindow();
6149     window->DC.TextWrapPos = wrap_pos_x;
6150     window->DC.TextWrapPosStack.push_back(wrap_pos_x);
6151 }
6152 
PopTextWrapPos()6153 void ImGui::PopTextWrapPos()
6154 {
6155     ImGuiWindow* window = GetCurrentWindow();
6156     window->DC.TextWrapPosStack.pop_back();
6157     window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
6158 }
6159 
6160 // 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)6161 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
6162 {
6163     ImGuiContext& g = *GImGui;
6164     ImGuiColorMod backup;
6165     backup.Col = idx;
6166     backup.BackupValue = g.Style.Colors[idx];
6167     g.ColorModifiers.push_back(backup);
6168     g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
6169 }
6170 
PushStyleColor(ImGuiCol idx,const ImVec4 & col)6171 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
6172 {
6173     ImGuiContext& g = *GImGui;
6174     ImGuiColorMod backup;
6175     backup.Col = idx;
6176     backup.BackupValue = g.Style.Colors[idx];
6177     g.ColorModifiers.push_back(backup);
6178     g.Style.Colors[idx] = col;
6179 }
6180 
PopStyleColor(int count)6181 void ImGui::PopStyleColor(int count)
6182 {
6183     ImGuiContext& g = *GImGui;
6184     while (count > 0)
6185     {
6186         ImGuiColorMod& backup = g.ColorModifiers.back();
6187         g.Style.Colors[backup.Col] = backup.BackupValue;
6188         g.ColorModifiers.pop_back();
6189         count--;
6190     }
6191 }
6192 
6193 struct ImGuiStyleVarInfo
6194 {
6195     ImGuiDataType   Type;
6196     ImU32           Count;
6197     ImU32           Offset;
GetVarPtrImGuiStyleVarInfo6198     void*           GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
6199 };
6200 
6201 static const ImGuiStyleVarInfo GStyleVarInfo[] =
6202 {
6203     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) },               // ImGuiStyleVar_Alpha
6204     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) },       // ImGuiStyleVar_WindowPadding
6205     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) },      // ImGuiStyleVar_WindowRounding
6206     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) },    // ImGuiStyleVar_WindowBorderSize
6207     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) },       // ImGuiStyleVar_WindowMinSize
6208     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) },    // ImGuiStyleVar_WindowTitleAlign
6209     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) },       // ImGuiStyleVar_ChildRounding
6210     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) },     // ImGuiStyleVar_ChildBorderSize
6211     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) },       // ImGuiStyleVar_PopupRounding
6212     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) },     // ImGuiStyleVar_PopupBorderSize
6213     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) },        // ImGuiStyleVar_FramePadding
6214     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) },       // ImGuiStyleVar_FrameRounding
6215     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) },     // ImGuiStyleVar_FrameBorderSize
6216     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) },         // ImGuiStyleVar_ItemSpacing
6217     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) },    // ImGuiStyleVar_ItemInnerSpacing
6218     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) },       // ImGuiStyleVar_IndentSpacing
6219     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) },       // ImGuiStyleVar_ScrollbarSize
6220     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) },   // ImGuiStyleVar_ScrollbarRounding
6221     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) },         // ImGuiStyleVar_GrabMinSize
6222     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) },        // ImGuiStyleVar_GrabRounding
6223     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) },         // ImGuiStyleVar_TabRounding
6224     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) },     // ImGuiStyleVar_ButtonTextAlign
6225     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
6226 };
6227 
GetStyleVarInfo(ImGuiStyleVar idx)6228 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
6229 {
6230     IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
6231     IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
6232     return &GStyleVarInfo[idx];
6233 }
6234 
PushStyleVar(ImGuiStyleVar idx,float val)6235 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
6236 {
6237     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
6238     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
6239     {
6240         ImGuiContext& g = *GImGui;
6241         float* pvar = (float*)var_info->GetVarPtr(&g.Style);
6242         g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
6243         *pvar = val;
6244         return;
6245     }
6246     IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!");
6247 }
6248 
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)6249 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
6250 {
6251     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
6252     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
6253     {
6254         ImGuiContext& g = *GImGui;
6255         ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
6256         g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
6257         *pvar = val;
6258         return;
6259     }
6260     IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!");
6261 }
6262 
PopStyleVar(int count)6263 void ImGui::PopStyleVar(int count)
6264 {
6265     ImGuiContext& g = *GImGui;
6266     while (count > 0)
6267     {
6268         // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
6269         ImGuiStyleMod& backup = g.StyleModifiers.back();
6270         const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
6271         void* data = info->GetVarPtr(&g.Style);
6272         if (info->Type == ImGuiDataType_Float && info->Count == 1)      { ((float*)data)[0] = backup.BackupFloat[0]; }
6273         else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
6274         g.StyleModifiers.pop_back();
6275         count--;
6276     }
6277 }
6278 
GetStyleColorName(ImGuiCol idx)6279 const char* ImGui::GetStyleColorName(ImGuiCol idx)
6280 {
6281     // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
6282     switch (idx)
6283     {
6284     case ImGuiCol_Text: return "Text";
6285     case ImGuiCol_TextDisabled: return "TextDisabled";
6286     case ImGuiCol_WindowBg: return "WindowBg";
6287     case ImGuiCol_ChildBg: return "ChildBg";
6288     case ImGuiCol_PopupBg: return "PopupBg";
6289     case ImGuiCol_Border: return "Border";
6290     case ImGuiCol_BorderShadow: return "BorderShadow";
6291     case ImGuiCol_FrameBg: return "FrameBg";
6292     case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
6293     case ImGuiCol_FrameBgActive: return "FrameBgActive";
6294     case ImGuiCol_TitleBg: return "TitleBg";
6295     case ImGuiCol_TitleBgActive: return "TitleBgActive";
6296     case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
6297     case ImGuiCol_MenuBarBg: return "MenuBarBg";
6298     case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
6299     case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
6300     case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
6301     case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
6302     case ImGuiCol_CheckMark: return "CheckMark";
6303     case ImGuiCol_SliderGrab: return "SliderGrab";
6304     case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
6305     case ImGuiCol_Button: return "Button";
6306     case ImGuiCol_ButtonHovered: return "ButtonHovered";
6307     case ImGuiCol_ButtonActive: return "ButtonActive";
6308     case ImGuiCol_Header: return "Header";
6309     case ImGuiCol_HeaderHovered: return "HeaderHovered";
6310     case ImGuiCol_HeaderActive: return "HeaderActive";
6311     case ImGuiCol_Separator: return "Separator";
6312     case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
6313     case ImGuiCol_SeparatorActive: return "SeparatorActive";
6314     case ImGuiCol_ResizeGrip: return "ResizeGrip";
6315     case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
6316     case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
6317     case ImGuiCol_Tab: return "Tab";
6318     case ImGuiCol_TabHovered: return "TabHovered";
6319     case ImGuiCol_TabActive: return "TabActive";
6320     case ImGuiCol_TabUnfocused: return "TabUnfocused";
6321     case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive";
6322     case ImGuiCol_PlotLines: return "PlotLines";
6323     case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
6324     case ImGuiCol_PlotHistogram: return "PlotHistogram";
6325     case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
6326     case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
6327     case ImGuiCol_DragDropTarget: return "DragDropTarget";
6328     case ImGuiCol_NavHighlight: return "NavHighlight";
6329     case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
6330     case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
6331     case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
6332     }
6333     IM_ASSERT(0);
6334     return "Unknown";
6335 }
6336 
IsWindowChildOf(ImGuiWindow * window,ImGuiWindow * potential_parent)6337 bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
6338 {
6339     if (window->RootWindow == potential_parent)
6340         return true;
6341     while (window != NULL)
6342     {
6343         if (window == potential_parent)
6344             return true;
6345         window = window->ParentWindow;
6346     }
6347     return false;
6348 }
6349 
IsWindowHovered(ImGuiHoveredFlags flags)6350 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
6351 {
6352     IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0);   // Flags not supported by this function
6353     ImGuiContext& g = *GImGui;
6354 
6355     if (flags & ImGuiHoveredFlags_AnyWindow)
6356     {
6357         if (g.HoveredWindow == NULL)
6358             return false;
6359     }
6360     else
6361     {
6362         switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
6363         {
6364         case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
6365             if (g.HoveredRootWindow != g.CurrentWindow->RootWindow)
6366                 return false;
6367             break;
6368         case ImGuiHoveredFlags_RootWindow:
6369             if (g.HoveredWindow != g.CurrentWindow->RootWindow)
6370                 return false;
6371             break;
6372         case ImGuiHoveredFlags_ChildWindows:
6373             if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow))
6374                 return false;
6375             break;
6376         default:
6377             if (g.HoveredWindow != g.CurrentWindow)
6378                 return false;
6379             break;
6380         }
6381     }
6382 
6383     if (!IsWindowContentHoverable(g.HoveredWindow, flags))
6384         return false;
6385     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
6386         if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
6387             return false;
6388     return true;
6389 }
6390 
IsWindowFocused(ImGuiFocusedFlags flags)6391 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
6392 {
6393     ImGuiContext& g = *GImGui;
6394 
6395     if (flags & ImGuiFocusedFlags_AnyWindow)
6396         return g.NavWindow != NULL;
6397 
6398     IM_ASSERT(g.CurrentWindow);     // Not inside a Begin()/End()
6399     switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
6400     {
6401     case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
6402         return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
6403     case ImGuiFocusedFlags_RootWindow:
6404         return g.NavWindow == g.CurrentWindow->RootWindow;
6405     case ImGuiFocusedFlags_ChildWindows:
6406         return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
6407     default:
6408         return g.NavWindow == g.CurrentWindow;
6409     }
6410 }
6411 
6412 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
6413 // Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmaticaly.
6414 // If you want a window to never be focused, you may use the e.g. NoInputs flag.
IsWindowNavFocusable(ImGuiWindow * window)6415 bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
6416 {
6417     return window->Active && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
6418 }
6419 
GetWindowWidth()6420 float ImGui::GetWindowWidth()
6421 {
6422     ImGuiWindow* window = GImGui->CurrentWindow;
6423     return window->Size.x;
6424 }
6425 
GetWindowHeight()6426 float ImGui::GetWindowHeight()
6427 {
6428     ImGuiWindow* window = GImGui->CurrentWindow;
6429     return window->Size.y;
6430 }
6431 
GetWindowPos()6432 ImVec2 ImGui::GetWindowPos()
6433 {
6434     ImGuiContext& g = *GImGui;
6435     ImGuiWindow* window = g.CurrentWindow;
6436     return window->Pos;
6437 }
6438 
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiCond cond)6439 void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
6440 {
6441     // Test condition (NB: bit 0 is always true) and clear flags for next time
6442     if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
6443         return;
6444 
6445     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6446     window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6447     window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
6448 
6449     // Set
6450     const ImVec2 old_pos = window->Pos;
6451     window->Pos = ImFloor(pos);
6452     ImVec2 offset = window->Pos - old_pos;
6453     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
6454     window->DC.CursorMaxPos += offset;      // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.
6455     window->DC.CursorStartPos += offset;
6456 }
6457 
SetWindowPos(const ImVec2 & pos,ImGuiCond cond)6458 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
6459 {
6460     ImGuiWindow* window = GetCurrentWindowRead();
6461     SetWindowPos(window, pos, cond);
6462 }
6463 
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiCond cond)6464 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
6465 {
6466     if (ImGuiWindow* window = FindWindowByName(name))
6467         SetWindowPos(window, pos, cond);
6468 }
6469 
GetWindowSize()6470 ImVec2 ImGui::GetWindowSize()
6471 {
6472     ImGuiWindow* window = GetCurrentWindowRead();
6473     return window->Size;
6474 }
6475 
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiCond cond)6476 void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
6477 {
6478     // Test condition (NB: bit 0 is always true) and clear flags for next time
6479     if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
6480         return;
6481 
6482     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6483     window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6484 
6485     // Set
6486     if (size.x > 0.0f)
6487     {
6488         window->AutoFitFramesX = 0;
6489         window->SizeFull.x = IM_FLOOR(size.x);
6490     }
6491     else
6492     {
6493         window->AutoFitFramesX = 2;
6494         window->AutoFitOnlyGrows = false;
6495     }
6496     if (size.y > 0.0f)
6497     {
6498         window->AutoFitFramesY = 0;
6499         window->SizeFull.y = IM_FLOOR(size.y);
6500     }
6501     else
6502     {
6503         window->AutoFitFramesY = 2;
6504         window->AutoFitOnlyGrows = false;
6505     }
6506 }
6507 
SetWindowSize(const ImVec2 & size,ImGuiCond cond)6508 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
6509 {
6510     SetWindowSize(GImGui->CurrentWindow, size, cond);
6511 }
6512 
SetWindowSize(const char * name,const ImVec2 & size,ImGuiCond cond)6513 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
6514 {
6515     if (ImGuiWindow* window = FindWindowByName(name))
6516         SetWindowSize(window, size, cond);
6517 }
6518 
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiCond cond)6519 void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
6520 {
6521     // Test condition (NB: bit 0 is always true) and clear flags for next time
6522     if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
6523         return;
6524     window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6525 
6526     // Set
6527     window->Collapsed = collapsed;
6528 }
6529 
SetWindowCollapsed(bool collapsed,ImGuiCond cond)6530 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
6531 {
6532     SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
6533 }
6534 
IsWindowCollapsed()6535 bool ImGui::IsWindowCollapsed()
6536 {
6537     ImGuiWindow* window = GetCurrentWindowRead();
6538     return window->Collapsed;
6539 }
6540 
IsWindowAppearing()6541 bool ImGui::IsWindowAppearing()
6542 {
6543     ImGuiWindow* window = GetCurrentWindowRead();
6544     return window->Appearing;
6545 }
6546 
SetWindowCollapsed(const char * name,bool collapsed,ImGuiCond cond)6547 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
6548 {
6549     if (ImGuiWindow* window = FindWindowByName(name))
6550         SetWindowCollapsed(window, collapsed, cond);
6551 }
6552 
SetWindowFocus()6553 void ImGui::SetWindowFocus()
6554 {
6555     FocusWindow(GImGui->CurrentWindow);
6556 }
6557 
SetWindowFocus(const char * name)6558 void ImGui::SetWindowFocus(const char* name)
6559 {
6560     if (name)
6561     {
6562         if (ImGuiWindow* window = FindWindowByName(name))
6563             FocusWindow(window);
6564     }
6565     else
6566     {
6567         FocusWindow(NULL);
6568     }
6569 }
6570 
SetNextWindowPos(const ImVec2 & pos,ImGuiCond cond,const ImVec2 & pivot)6571 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
6572 {
6573     ImGuiContext& g = *GImGui;
6574     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6575     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos;
6576     g.NextWindowData.PosVal = pos;
6577     g.NextWindowData.PosPivotVal = pivot;
6578     g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
6579 }
6580 
SetNextWindowSize(const ImVec2 & size,ImGuiCond cond)6581 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
6582 {
6583     ImGuiContext& g = *GImGui;
6584     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6585     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize;
6586     g.NextWindowData.SizeVal = size;
6587     g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
6588 }
6589 
SetNextWindowSizeConstraints(const ImVec2 & size_min,const ImVec2 & size_max,ImGuiSizeCallback custom_callback,void * custom_callback_user_data)6590 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
6591 {
6592     ImGuiContext& g = *GImGui;
6593     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint;
6594     g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
6595     g.NextWindowData.SizeCallback = custom_callback;
6596     g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
6597 }
6598 
6599 // Content size = inner scrollable rectangle, padded with WindowPadding.
6600 // SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
SetNextWindowContentSize(const ImVec2 & size)6601 void ImGui::SetNextWindowContentSize(const ImVec2& size)
6602 {
6603     ImGuiContext& g = *GImGui;
6604     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize;
6605     g.NextWindowData.ContentSizeVal = size;
6606 }
6607 
SetNextWindowCollapsed(bool collapsed,ImGuiCond cond)6608 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
6609 {
6610     ImGuiContext& g = *GImGui;
6611     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6612     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed;
6613     g.NextWindowData.CollapsedVal = collapsed;
6614     g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
6615 }
6616 
SetNextWindowFocus()6617 void ImGui::SetNextWindowFocus()
6618 {
6619     ImGuiContext& g = *GImGui;
6620     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus;
6621 }
6622 
SetNextWindowBgAlpha(float alpha)6623 void ImGui::SetNextWindowBgAlpha(float alpha)
6624 {
6625     ImGuiContext& g = *GImGui;
6626     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha;
6627     g.NextWindowData.BgAlphaVal = alpha;
6628 }
6629 
6630 // FIXME: This is in window space (not screen space!). We should try to obsolete all those functions.
GetContentRegionMax()6631 ImVec2 ImGui::GetContentRegionMax()
6632 {
6633     ImGuiContext& g = *GImGui;
6634     ImGuiWindow* window = g.CurrentWindow;
6635     ImVec2 mx = window->ContentRegionRect.Max - window->Pos;
6636     if (window->DC.CurrentColumns)
6637         mx.x = window->WorkRect.Max.x - window->Pos.x;
6638     return mx;
6639 }
6640 
6641 // [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features.
GetContentRegionMaxAbs()6642 ImVec2 ImGui::GetContentRegionMaxAbs()
6643 {
6644     ImGuiContext& g = *GImGui;
6645     ImGuiWindow* window = g.CurrentWindow;
6646     ImVec2 mx = window->ContentRegionRect.Max;
6647     if (window->DC.CurrentColumns)
6648         mx.x = window->WorkRect.Max.x;
6649     return mx;
6650 }
6651 
GetContentRegionAvail()6652 ImVec2 ImGui::GetContentRegionAvail()
6653 {
6654     ImGuiWindow* window = GImGui->CurrentWindow;
6655     return GetContentRegionMaxAbs() - window->DC.CursorPos;
6656 }
6657 
6658 // In window space (not screen space!)
GetWindowContentRegionMin()6659 ImVec2 ImGui::GetWindowContentRegionMin()
6660 {
6661     ImGuiWindow* window = GImGui->CurrentWindow;
6662     return window->ContentRegionRect.Min - window->Pos;
6663 }
6664 
GetWindowContentRegionMax()6665 ImVec2 ImGui::GetWindowContentRegionMax()
6666 {
6667     ImGuiWindow* window = GImGui->CurrentWindow;
6668     return window->ContentRegionRect.Max - window->Pos;
6669 }
6670 
GetWindowContentRegionWidth()6671 float ImGui::GetWindowContentRegionWidth()
6672 {
6673     ImGuiWindow* window = GImGui->CurrentWindow;
6674     return window->ContentRegionRect.GetWidth();
6675 }
6676 
GetTextLineHeight()6677 float ImGui::GetTextLineHeight()
6678 {
6679     ImGuiContext& g = *GImGui;
6680     return g.FontSize;
6681 }
6682 
GetTextLineHeightWithSpacing()6683 float ImGui::GetTextLineHeightWithSpacing()
6684 {
6685     ImGuiContext& g = *GImGui;
6686     return g.FontSize + g.Style.ItemSpacing.y;
6687 }
6688 
GetFrameHeight()6689 float ImGui::GetFrameHeight()
6690 {
6691     ImGuiContext& g = *GImGui;
6692     return g.FontSize + g.Style.FramePadding.y * 2.0f;
6693 }
6694 
GetFrameHeightWithSpacing()6695 float ImGui::GetFrameHeightWithSpacing()
6696 {
6697     ImGuiContext& g = *GImGui;
6698     return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
6699 }
6700 
GetWindowDrawList()6701 ImDrawList* ImGui::GetWindowDrawList()
6702 {
6703     ImGuiWindow* window = GetCurrentWindow();
6704     return window->DrawList;
6705 }
6706 
GetFont()6707 ImFont* ImGui::GetFont()
6708 {
6709     return GImGui->Font;
6710 }
6711 
GetFontSize()6712 float ImGui::GetFontSize()
6713 {
6714     return GImGui->FontSize;
6715 }
6716 
GetFontTexUvWhitePixel()6717 ImVec2 ImGui::GetFontTexUvWhitePixel()
6718 {
6719     return GImGui->DrawListSharedData.TexUvWhitePixel;
6720 }
6721 
SetWindowFontScale(float scale)6722 void ImGui::SetWindowFontScale(float scale)
6723 {
6724     ImGuiContext& g = *GImGui;
6725     ImGuiWindow* window = GetCurrentWindow();
6726     window->FontWindowScale = scale;
6727     g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
6728 }
6729 
6730 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
6731 // 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()6732 ImVec2 ImGui::GetCursorPos()
6733 {
6734     ImGuiWindow* window = GetCurrentWindowRead();
6735     return window->DC.CursorPos - window->Pos + window->Scroll;
6736 }
6737 
GetCursorPosX()6738 float ImGui::GetCursorPosX()
6739 {
6740     ImGuiWindow* window = GetCurrentWindowRead();
6741     return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
6742 }
6743 
GetCursorPosY()6744 float ImGui::GetCursorPosY()
6745 {
6746     ImGuiWindow* window = GetCurrentWindowRead();
6747     return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
6748 }
6749 
SetCursorPos(const ImVec2 & local_pos)6750 void ImGui::SetCursorPos(const ImVec2& local_pos)
6751 {
6752     ImGuiWindow* window = GetCurrentWindow();
6753     window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
6754     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
6755 }
6756 
SetCursorPosX(float x)6757 void ImGui::SetCursorPosX(float x)
6758 {
6759     ImGuiWindow* window = GetCurrentWindow();
6760     window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
6761     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
6762 }
6763 
SetCursorPosY(float y)6764 void ImGui::SetCursorPosY(float y)
6765 {
6766     ImGuiWindow* window = GetCurrentWindow();
6767     window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
6768     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
6769 }
6770 
GetCursorStartPos()6771 ImVec2 ImGui::GetCursorStartPos()
6772 {
6773     ImGuiWindow* window = GetCurrentWindowRead();
6774     return window->DC.CursorStartPos - window->Pos;
6775 }
6776 
GetCursorScreenPos()6777 ImVec2 ImGui::GetCursorScreenPos()
6778 {
6779     ImGuiWindow* window = GetCurrentWindowRead();
6780     return window->DC.CursorPos;
6781 }
6782 
SetCursorScreenPos(const ImVec2 & pos)6783 void ImGui::SetCursorScreenPos(const ImVec2& pos)
6784 {
6785     ImGuiWindow* window = GetCurrentWindow();
6786     window->DC.CursorPos = pos;
6787     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
6788 }
6789 
ActivateItem(ImGuiID id)6790 void ImGui::ActivateItem(ImGuiID id)
6791 {
6792     ImGuiContext& g = *GImGui;
6793     g.NavNextActivateId = id;
6794 }
6795 
SetKeyboardFocusHere(int offset)6796 void ImGui::SetKeyboardFocusHere(int offset)
6797 {
6798     IM_ASSERT(offset >= -1);    // -1 is allowed but not below
6799     ImGuiContext& g = *GImGui;
6800     ImGuiWindow* window = g.CurrentWindow;
6801     g.FocusRequestNextWindow = window;
6802     g.FocusRequestNextCounterAll = window->DC.FocusCounterAll + 1 + offset;
6803     g.FocusRequestNextCounterTab = INT_MAX;
6804 }
6805 
SetItemDefaultFocus()6806 void ImGui::SetItemDefaultFocus()
6807 {
6808     ImGuiContext& g = *GImGui;
6809     ImGuiWindow* window = g.CurrentWindow;
6810     if (!window->Appearing)
6811         return;
6812     if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent)
6813     {
6814         g.NavInitRequest = false;
6815         g.NavInitResultId = g.NavWindow->DC.LastItemId;
6816         g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos);
6817         NavUpdateAnyRequestFlag();
6818         if (!IsItemVisible())
6819             SetScrollHereY();
6820     }
6821 }
6822 
SetStateStorage(ImGuiStorage * tree)6823 void ImGui::SetStateStorage(ImGuiStorage* tree)
6824 {
6825     ImGuiWindow* window = GImGui->CurrentWindow;
6826     window->DC.StateStorage = tree ? tree : &window->StateStorage;
6827 }
6828 
GetStateStorage()6829 ImGuiStorage* ImGui::GetStateStorage()
6830 {
6831     ImGuiWindow* window = GImGui->CurrentWindow;
6832     return window->DC.StateStorage;
6833 }
6834 
PushID(const char * str_id)6835 void ImGui::PushID(const char* str_id)
6836 {
6837     ImGuiWindow* window = GImGui->CurrentWindow;
6838     window->IDStack.push_back(window->GetIDNoKeepAlive(str_id));
6839 }
6840 
PushID(const char * str_id_begin,const char * str_id_end)6841 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
6842 {
6843     ImGuiWindow* window = GImGui->CurrentWindow;
6844     window->IDStack.push_back(window->GetIDNoKeepAlive(str_id_begin, str_id_end));
6845 }
6846 
PushID(const void * ptr_id)6847 void ImGui::PushID(const void* ptr_id)
6848 {
6849     ImGuiWindow* window = GImGui->CurrentWindow;
6850     window->IDStack.push_back(window->GetIDNoKeepAlive(ptr_id));
6851 }
6852 
PushID(int int_id)6853 void ImGui::PushID(int int_id)
6854 {
6855     ImGuiWindow* window = GImGui->CurrentWindow;
6856     window->IDStack.push_back(window->GetIDNoKeepAlive(int_id));
6857 }
6858 
6859 // Push a given id value ignoring the ID stack as a seed.
PushOverrideID(ImGuiID id)6860 void ImGui::PushOverrideID(ImGuiID id)
6861 {
6862     ImGuiWindow* window = GImGui->CurrentWindow;
6863     window->IDStack.push_back(id);
6864 }
6865 
PopID()6866 void ImGui::PopID()
6867 {
6868     ImGuiWindow* window = GImGui->CurrentWindow;
6869     window->IDStack.pop_back();
6870 }
6871 
GetID(const char * str_id)6872 ImGuiID ImGui::GetID(const char* str_id)
6873 {
6874     ImGuiWindow* window = GImGui->CurrentWindow;
6875     return window->GetID(str_id);
6876 }
6877 
GetID(const char * str_id_begin,const char * str_id_end)6878 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
6879 {
6880     ImGuiWindow* window = GImGui->CurrentWindow;
6881     return window->GetID(str_id_begin, str_id_end);
6882 }
6883 
GetID(const void * ptr_id)6884 ImGuiID ImGui::GetID(const void* ptr_id)
6885 {
6886     ImGuiWindow* window = GImGui->CurrentWindow;
6887     return window->GetID(ptr_id);
6888 }
6889 
IsRectVisible(const ImVec2 & size)6890 bool ImGui::IsRectVisible(const ImVec2& size)
6891 {
6892     ImGuiWindow* window = GImGui->CurrentWindow;
6893     return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
6894 }
6895 
IsRectVisible(const ImVec2 & rect_min,const ImVec2 & rect_max)6896 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
6897 {
6898     ImGuiWindow* window = GImGui->CurrentWindow;
6899     return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
6900 }
6901 
6902 // 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()6903 void ImGui::BeginGroup()
6904 {
6905     ImGuiContext& g = *GImGui;
6906     ImGuiWindow* window = GetCurrentWindow();
6907 
6908     window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
6909     ImGuiGroupData& group_data = window->DC.GroupStack.back();
6910     group_data.BackupCursorPos = window->DC.CursorPos;
6911     group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
6912     group_data.BackupIndent = window->DC.Indent;
6913     group_data.BackupGroupOffset = window->DC.GroupOffset;
6914     group_data.BackupCurrLineSize = window->DC.CurrLineSize;
6915     group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;
6916     group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
6917     group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
6918     group_data.EmitItem = true;
6919 
6920     window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
6921     window->DC.Indent = window->DC.GroupOffset;
6922     window->DC.CursorMaxPos = window->DC.CursorPos;
6923     window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
6924     if (g.LogEnabled)
6925         g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return
6926 }
6927 
EndGroup()6928 void ImGui::EndGroup()
6929 {
6930     ImGuiContext& g = *GImGui;
6931     ImGuiWindow* window = GetCurrentWindow();
6932     IM_ASSERT(!window->DC.GroupStack.empty());  // Mismatched BeginGroup()/EndGroup() calls
6933 
6934     ImGuiGroupData& group_data = window->DC.GroupStack.back();
6935 
6936     ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos));
6937 
6938     window->DC.CursorPos = group_data.BackupCursorPos;
6939     window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
6940     window->DC.Indent = group_data.BackupIndent;
6941     window->DC.GroupOffset = group_data.BackupGroupOffset;
6942     window->DC.CurrLineSize = group_data.BackupCurrLineSize;
6943     window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;
6944     if (g.LogEnabled)
6945         g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return
6946 
6947     if (!group_data.EmitItem)
6948     {
6949         window->DC.GroupStack.pop_back();
6950         return;
6951     }
6952 
6953     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.
6954     ItemSize(group_bb.GetSize());
6955     ItemAdd(group_bb, 0);
6956 
6957     // 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.
6958     // 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.
6959     // Also if you grep for LastItemId you'll notice it is only used in that context.
6960     // (The tests not symmetrical because ActiveIdIsAlive is an ID itself, in order to be able to handle ActiveId being overwritten during the frame.)
6961     const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId;
6962     const bool group_contains_prev_active_id = !group_data.BackupActiveIdPreviousFrameIsAlive && g.ActiveIdPreviousFrameIsAlive;
6963     if (group_contains_curr_active_id)
6964         window->DC.LastItemId = g.ActiveId;
6965     else if (group_contains_prev_active_id)
6966         window->DC.LastItemId = g.ActiveIdPreviousFrame;
6967     window->DC.LastItemRect = group_bb;
6968 
6969     // Forward Edited flag
6970     if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame)
6971         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
6972 
6973     // Forward Deactivated flag
6974     window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDeactivated;
6975     if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame)
6976         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Deactivated;
6977 
6978     window->DC.GroupStack.pop_back();
6979     //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255));   // [Debug]
6980 }
6981 
6982 // Gets back to previous line and continue with horizontal layout
6983 //      offset_from_start_x == 0 : follow right after previous item
6984 //      offset_from_start_x != 0 : align to specified x position (relative to window/group left)
6985 //      spacing_w < 0            : use default spacing if pos_x == 0, no spacing if pos_x != 0
6986 //      spacing_w >= 0           : enforce spacing amount
SameLine(float offset_from_start_x,float spacing_w)6987 void ImGui::SameLine(float offset_from_start_x, float spacing_w)
6988 {
6989     ImGuiWindow* window = GetCurrentWindow();
6990     if (window->SkipItems)
6991         return;
6992 
6993     ImGuiContext& g = *GImGui;
6994     if (offset_from_start_x != 0.0f)
6995     {
6996         if (spacing_w < 0.0f) spacing_w = 0.0f;
6997         window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
6998         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
6999     }
7000     else
7001     {
7002         if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
7003         window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
7004         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
7005     }
7006     window->DC.CurrLineSize = window->DC.PrevLineSize;
7007     window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
7008 }
7009 
Indent(float indent_w)7010 void ImGui::Indent(float indent_w)
7011 {
7012     ImGuiContext& g = *GImGui;
7013     ImGuiWindow* window = GetCurrentWindow();
7014     window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
7015     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
7016 }
7017 
Unindent(float indent_w)7018 void ImGui::Unindent(float indent_w)
7019 {
7020     ImGuiContext& g = *GImGui;
7021     ImGuiWindow* window = GetCurrentWindow();
7022     window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
7023     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
7024 }
7025 
7026 
7027 //-----------------------------------------------------------------------------
7028 // [SECTION] ERROR CHECKING
7029 //-----------------------------------------------------------------------------
7030 
ErrorCheckEndFrame()7031 static void ImGui::ErrorCheckEndFrame()
7032 {
7033     // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you
7034     // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API).
7035     ImGuiContext& g = *GImGui;
7036     if (g.CurrentWindowStack.Size != 1)
7037     {
7038         if (g.CurrentWindowStack.Size > 1)
7039         {
7040             IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?");
7041             while (g.CurrentWindowStack.Size > 1)
7042                 End();
7043         }
7044         else
7045         {
7046             IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?");
7047         }
7048     }
7049 
7050 }
7051 
7052 // Save and compare stack sizes on Begin()/End() to detect usage errors
7053 // Begin() calls this with write=true
7054 // End() calls this with write=false
ErrorCheckBeginEndCompareStacksSize(ImGuiWindow * window,bool write)7055 static void ImGui::ErrorCheckBeginEndCompareStacksSize(ImGuiWindow* window, bool write)
7056 {
7057     ImGuiContext& g = *GImGui;
7058     short* p = &window->DC.StackSizesBackup[0];
7059 
7060     // Window stacks
7061     // 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)
7062     { int n = window->IDStack.Size;       if (write) *p = (short)n; else IM_ASSERT(*p == n && "PushID/PopID or TreeNode/TreePop Mismatch!");   p++; }    // Too few or too many PopID()/TreePop()
7063     { int n = window->DC.GroupStack.Size; if (write) *p = (short)n; else IM_ASSERT(*p == n && "BeginGroup/EndGroup Mismatch!");                p++; }    // Too few or too many EndGroup()
7064 
7065     // Global stacks
7066     // 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.
7067     { int n = g.BeginPopupStack.Size;     if (write) *p = (short)n; else IM_ASSERT(*p == n && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch!"); p++; }// Too few or too many EndMenu()/EndPopup()
7068     { int n = g.ColorModifiers.Size;      if (write) *p = (short)n; else IM_ASSERT(*p >= n && "PushStyleColor/PopStyleColor Mismatch!");       p++; }    // Too few or too many PopStyleColor()
7069     { int n = g.StyleModifiers.Size;      if (write) *p = (short)n; else IM_ASSERT(*p >= n && "PushStyleVar/PopStyleVar Mismatch!");           p++; }    // Too few or too many PopStyleVar()
7070     { int n = g.FontStack.Size;           if (write) *p = (short)n; else IM_ASSERT(*p >= n && "PushFont/PopFont Mismatch!");                   p++; }    // Too few or too many PopFont()
7071     IM_ASSERT(p == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
7072 }
7073 
7074 
7075 //-----------------------------------------------------------------------------
7076 // [SECTION] SCROLLING
7077 //-----------------------------------------------------------------------------
7078 
CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow * window,bool snap_on_edges)7079 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges)
7080 {
7081     ImGuiContext& g = *GImGui;
7082     ImVec2 scroll = window->Scroll;
7083     if (window->ScrollTarget.x < FLT_MAX)
7084     {
7085         float cr_x = window->ScrollTargetCenterRatio.x;
7086         float target_x = window->ScrollTarget.x;
7087         if (snap_on_edges && cr_x <= 0.0f && target_x <= window->WindowPadding.x)
7088             target_x = 0.0f;
7089         else if (snap_on_edges && cr_x >= 1.0f && target_x >= window->ContentSize.x + window->WindowPadding.x + g.Style.ItemSpacing.x)
7090             target_x = window->ContentSize.x + window->WindowPadding.x * 2.0f;
7091         scroll.x = target_x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x);
7092     }
7093     if (window->ScrollTarget.y < FLT_MAX)
7094     {
7095         // '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.
7096         float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
7097         float cr_y = window->ScrollTargetCenterRatio.y;
7098         float target_y = window->ScrollTarget.y;
7099         if (snap_on_edges && cr_y <= 0.0f && target_y <= window->WindowPadding.y)
7100             target_y = 0.0f;
7101         if (snap_on_edges && cr_y >= 1.0f && target_y >= window->ContentSize.y + window->WindowPadding.y + g.Style.ItemSpacing.y)
7102             target_y = window->ContentSize.y + window->WindowPadding.y * 2.0f;
7103         scroll.y = target_y - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y - decoration_up_height);
7104     }
7105     scroll = ImMax(scroll, ImVec2(0.0f, 0.0f));
7106     if (!window->Collapsed && !window->SkipItems)
7107     {
7108         scroll.x = ImMin(scroll.x, window->ScrollMax.x);
7109         scroll.y = ImMin(scroll.y, window->ScrollMax.y);
7110     }
7111     return scroll;
7112 }
7113 
7114 // Scroll to keep newly navigated item fully into view
ScrollToBringRectIntoView(ImGuiWindow * window,const ImRect & item_rect)7115 ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect)
7116 {
7117     ImGuiContext& g = *GImGui;
7118     ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));
7119     //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
7120 
7121     ImVec2 delta_scroll;
7122     if (!window_rect.Contains(item_rect))
7123     {
7124         if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x)
7125             SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x + g.Style.ItemSpacing.x, 0.0f);
7126         else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x)
7127             SetScrollFromPosX(window, item_rect.Max.x - window->Pos.x + g.Style.ItemSpacing.x, 1.0f);
7128         if (item_rect.Min.y < window_rect.Min.y)
7129             SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y - g.Style.ItemSpacing.y, 0.0f);
7130         else if (item_rect.Max.y >= window_rect.Max.y)
7131             SetScrollFromPosY(window, item_rect.Max.y - window->Pos.y + g.Style.ItemSpacing.y, 1.0f);
7132 
7133         ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window, false);
7134         delta_scroll = next_scroll - window->Scroll;
7135     }
7136 
7137     // Also scroll parent window to keep us into view if necessary
7138     if (window->Flags & ImGuiWindowFlags_ChildWindow)
7139         delta_scroll += ScrollToBringRectIntoView(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll));
7140 
7141     return delta_scroll;
7142 }
7143 
GetScrollX()7144 float ImGui::GetScrollX()
7145 {
7146     ImGuiWindow* window = GImGui->CurrentWindow;
7147     return window->Scroll.x;
7148 }
7149 
GetScrollY()7150 float ImGui::GetScrollY()
7151 {
7152     ImGuiWindow* window = GImGui->CurrentWindow;
7153     return window->Scroll.y;
7154 }
7155 
GetScrollMaxX()7156 float ImGui::GetScrollMaxX()
7157 {
7158     ImGuiWindow* window = GImGui->CurrentWindow;
7159     return window->ScrollMax.x;
7160 }
7161 
GetScrollMaxY()7162 float ImGui::GetScrollMaxY()
7163 {
7164     ImGuiWindow* window = GImGui->CurrentWindow;
7165     return window->ScrollMax.y;
7166 }
7167 
SetScrollX(float scroll_x)7168 void ImGui::SetScrollX(float scroll_x)
7169 {
7170     ImGuiWindow* window = GetCurrentWindow();
7171     window->ScrollTarget.x = scroll_x;
7172     window->ScrollTargetCenterRatio.x = 0.0f;
7173 }
7174 
SetScrollY(float scroll_y)7175 void ImGui::SetScrollY(float scroll_y)
7176 {
7177     ImGuiWindow* window = GetCurrentWindow();
7178     window->ScrollTarget.y = scroll_y;
7179     window->ScrollTargetCenterRatio.y = 0.0f;
7180 }
7181 
SetScrollX(ImGuiWindow * window,float new_scroll_x)7182 void ImGui::SetScrollX(ImGuiWindow* window, float new_scroll_x)
7183 {
7184     window->ScrollTarget.x = new_scroll_x;
7185     window->ScrollTargetCenterRatio.x = 0.0f;
7186 }
7187 
SetScrollY(ImGuiWindow * window,float new_scroll_y)7188 void ImGui::SetScrollY(ImGuiWindow* window, float new_scroll_y)
7189 {
7190     window->ScrollTarget.y = new_scroll_y;
7191     window->ScrollTargetCenterRatio.y = 0.0f;
7192 }
7193 
7194 
SetScrollFromPosX(ImGuiWindow * window,float local_x,float center_x_ratio)7195 void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
7196 {
7197     // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
7198     IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);
7199     window->ScrollTarget.x = IM_FLOOR(local_x + window->Scroll.x);
7200     window->ScrollTargetCenterRatio.x = center_x_ratio;
7201 }
7202 
SetScrollFromPosY(ImGuiWindow * window,float local_y,float center_y_ratio)7203 void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
7204 {
7205     // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
7206     IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
7207     const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
7208     local_y -= decoration_up_height;
7209     window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y);
7210     window->ScrollTargetCenterRatio.y = center_y_ratio;
7211 }
7212 
SetScrollFromPosX(float local_x,float center_x_ratio)7213 void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)
7214 {
7215     ImGuiContext& g = *GImGui;
7216     SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio);
7217 }
7218 
SetScrollFromPosY(float local_y,float center_y_ratio)7219 void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
7220 {
7221     ImGuiContext& g = *GImGui;
7222     SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio);
7223 }
7224 
7225 // 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)7226 void ImGui::SetScrollHereX(float center_x_ratio)
7227 {
7228     ImGuiContext& g = *GImGui;
7229     ImGuiWindow* window = g.CurrentWindow;
7230     float target_x = window->DC.LastItemRect.Min.x - window->Pos.x; // Left of last item, in window space
7231     float last_item_width = window->DC.LastItemRect.GetWidth();
7232     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.
7233     SetScrollFromPosX(target_x, center_x_ratio);
7234 }
7235 
7236 // 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)7237 void ImGui::SetScrollHereY(float center_y_ratio)
7238 {
7239     ImGuiContext& g = *GImGui;
7240     ImGuiWindow* window = g.CurrentWindow;
7241     float target_y = window->DC.CursorPosPrevLine.y - window->Pos.y; // Top of last item, in window space
7242     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.
7243     SetScrollFromPosY(target_y, center_y_ratio);
7244 }
7245 
7246 //-----------------------------------------------------------------------------
7247 // [SECTION] TOOLTIPS
7248 //-----------------------------------------------------------------------------
7249 
BeginTooltip()7250 void ImGui::BeginTooltip()
7251 {
7252     ImGuiContext& g = *GImGui;
7253     if (g.DragDropWithinSourceOrTarget)
7254     {
7255         // 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)
7256         // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
7257         // 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.
7258         //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
7259         ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale);
7260         SetNextWindowPos(tooltip_pos);
7261         SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
7262         //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
7263         BeginTooltipEx(0, true);
7264     }
7265     else
7266     {
7267         BeginTooltipEx(0, false);
7268     }
7269 }
7270 
7271 // 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)7272 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip)
7273 {
7274     ImGuiContext& g = *GImGui;
7275     char window_name[16];
7276     ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
7277     if (override_previous_tooltip)
7278         if (ImGuiWindow* window = FindWindowByName(window_name))
7279             if (window->Active)
7280             {
7281                 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
7282                 window->Hidden = true;
7283                 window->HiddenFramesCanSkipItems = 1;
7284                 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
7285             }
7286     ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
7287     Begin(window_name, NULL, flags | extra_flags);
7288 }
7289 
EndTooltip()7290 void ImGui::EndTooltip()
7291 {
7292     IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip);   // Mismatched BeginTooltip()/EndTooltip() calls
7293     End();
7294 }
7295 
SetTooltipV(const char * fmt,va_list args)7296 void ImGui::SetTooltipV(const char* fmt, va_list args)
7297 {
7298     ImGuiContext& g = *GImGui;
7299     if (g.DragDropWithinSourceOrTarget)
7300         BeginTooltip();
7301     else
7302         BeginTooltipEx(0, true);
7303     TextV(fmt, args);
7304     EndTooltip();
7305 }
7306 
SetTooltip(const char * fmt,...)7307 void ImGui::SetTooltip(const char* fmt, ...)
7308 {
7309     va_list args;
7310     va_start(args, fmt);
7311     SetTooltipV(fmt, args);
7312     va_end(args);
7313 }
7314 
7315 //-----------------------------------------------------------------------------
7316 // [SECTION] POPUPS
7317 //-----------------------------------------------------------------------------
7318 
IsPopupOpen(ImGuiID id)7319 bool ImGui::IsPopupOpen(ImGuiID id)
7320 {
7321     ImGuiContext& g = *GImGui;
7322     return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
7323 }
7324 
IsPopupOpen(const char * str_id)7325 bool ImGui::IsPopupOpen(const char* str_id)
7326 {
7327     ImGuiContext& g = *GImGui;
7328     return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id);
7329 }
7330 
GetTopMostPopupModal()7331 ImGuiWindow* ImGui::GetTopMostPopupModal()
7332 {
7333     ImGuiContext& g = *GImGui;
7334     for (int n = g.OpenPopupStack.Size-1; n >= 0; n--)
7335         if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
7336             if (popup->Flags & ImGuiWindowFlags_Modal)
7337                 return popup;
7338     return NULL;
7339 }
7340 
OpenPopup(const char * str_id)7341 void ImGui::OpenPopup(const char* str_id)
7342 {
7343     ImGuiContext& g = *GImGui;
7344     OpenPopupEx(g.CurrentWindow->GetID(str_id));
7345 }
7346 
7347 // Mark popup as open (toggle toward open state).
7348 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
7349 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
7350 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
OpenPopupEx(ImGuiID id)7351 void ImGui::OpenPopupEx(ImGuiID id)
7352 {
7353     ImGuiContext& g = *GImGui;
7354     ImGuiWindow* parent_window = g.CurrentWindow;
7355     int current_stack_size = g.BeginPopupStack.Size;
7356     ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
7357     popup_ref.PopupId = id;
7358     popup_ref.Window = NULL;
7359     popup_ref.SourceWindow = g.NavWindow;
7360     popup_ref.OpenFrameCount = g.FrameCount;
7361     popup_ref.OpenParentId = parent_window->IDStack.back();
7362     popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
7363     popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
7364 
7365     //IMGUI_DEBUG_LOG("OpenPopupEx(0x%08X)\n", g.FrameCount, id);
7366     if (g.OpenPopupStack.Size < current_stack_size + 1)
7367     {
7368         g.OpenPopupStack.push_back(popup_ref);
7369     }
7370     else
7371     {
7372         // 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
7373         // 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
7374         // 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.
7375         if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
7376         {
7377             g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
7378         }
7379         else
7380         {
7381             // Close child popups if any, then flag popup for open/reopen
7382             g.OpenPopupStack.resize(current_stack_size + 1);
7383             g.OpenPopupStack[current_stack_size] = popup_ref;
7384         }
7385 
7386         // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
7387         // This is equivalent to what ClosePopupToLevel() does.
7388         //if (g.OpenPopupStack[current_stack_size].PopupId == id)
7389         //    FocusWindow(parent_window);
7390     }
7391 }
7392 
ClosePopupsOverWindow(ImGuiWindow * ref_window,bool restore_focus_to_window_under_popup)7393 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
7394 {
7395     ImGuiContext& g = *GImGui;
7396     if (g.OpenPopupStack.empty())
7397         return;
7398 
7399     // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
7400     // Don't close our own child popup windows.
7401     int popup_count_to_keep = 0;
7402     if (ref_window)
7403     {
7404         // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
7405         for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
7406         {
7407             ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];
7408             if (!popup.Window)
7409                 continue;
7410             IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
7411             if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
7412                 continue;
7413 
7414             // Trim the stack when popups are not direct descendant of the reference window (the reference window is often the NavWindow)
7415             bool popup_or_descendent_is_ref_window = false;
7416             for (int m = popup_count_to_keep; m < g.OpenPopupStack.Size && !popup_or_descendent_is_ref_window; m++)
7417                 if (ImGuiWindow* popup_window = g.OpenPopupStack[m].Window)
7418                     if (popup_window->RootWindow == ref_window->RootWindow)
7419                         popup_or_descendent_is_ref_window = true;
7420             if (!popup_or_descendent_is_ref_window)
7421                 break;
7422         }
7423     }
7424     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
7425     {
7426         //IMGUI_DEBUG_LOG("ClosePopupsOverWindow(%s) -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep);
7427         ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup);
7428     }
7429 }
7430 
ClosePopupToLevel(int remaining,bool restore_focus_to_window_under_popup)7431 void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
7432 {
7433     ImGuiContext& g = *GImGui;
7434     IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
7435     ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow;
7436     ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window;
7437     g.OpenPopupStack.resize(remaining);
7438 
7439     if (restore_focus_to_window_under_popup)
7440     {
7441         if (focus_window && !focus_window->WasActive && popup_window)
7442         {
7443             // Fallback
7444             FocusTopMostWindowUnderOne(popup_window, NULL);
7445         }
7446         else
7447         {
7448             if (g.NavLayer == 0 && focus_window)
7449                 focus_window = NavRestoreLastChildNavWindow(focus_window);
7450             FocusWindow(focus_window);
7451         }
7452     }
7453 }
7454 
7455 // Close the popup we have begin-ed into.
CloseCurrentPopup()7456 void ImGui::CloseCurrentPopup()
7457 {
7458     ImGuiContext& g = *GImGui;
7459     int popup_idx = g.BeginPopupStack.Size - 1;
7460     if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
7461         return;
7462 
7463     // Closing a menu closes its top-most parent popup (unless a modal)
7464     while (popup_idx > 0)
7465     {
7466         ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;
7467         ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
7468         bool close_parent = false;
7469         if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
7470             if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal))
7471                 close_parent = true;
7472         if (!close_parent)
7473             break;
7474         popup_idx--;
7475     }
7476     //IMGUI_DEBUG_LOG("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx);
7477     ClosePopupToLevel(popup_idx, true);
7478 
7479     // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
7480     // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
7481     // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
7482     if (ImGuiWindow* window = g.NavWindow)
7483         window->DC.NavHideHighlightOneFrame = true;
7484 }
7485 
BeginPopupEx(ImGuiID id,ImGuiWindowFlags extra_flags)7486 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags)
7487 {
7488     ImGuiContext& g = *GImGui;
7489     if (!IsPopupOpen(id))
7490     {
7491         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
7492         return false;
7493     }
7494 
7495     char name[20];
7496     if (extra_flags & ImGuiWindowFlags_ChildMenu)
7497         ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth
7498     else
7499         ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
7500 
7501     bool is_open = Begin(name, NULL, extra_flags | ImGuiWindowFlags_Popup);
7502     if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
7503         EndPopup();
7504 
7505     return is_open;
7506 }
7507 
BeginPopup(const char * str_id,ImGuiWindowFlags flags)7508 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
7509 {
7510     ImGuiContext& g = *GImGui;
7511     if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
7512     {
7513         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
7514         return false;
7515     }
7516     flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
7517     return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags);
7518 }
7519 
7520 // If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
7521 // 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)7522 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
7523 {
7524     ImGuiContext& g = *GImGui;
7525     ImGuiWindow* window = g.CurrentWindow;
7526     const ImGuiID id = window->GetID(name);
7527     if (!IsPopupOpen(id))
7528     {
7529         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
7530         return false;
7531     }
7532 
7533     // Center modal windows by default
7534     // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
7535     if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
7536         SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
7537 
7538     flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings;
7539     const bool is_open = Begin(name, p_open, flags);
7540     if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
7541     {
7542         EndPopup();
7543         if (is_open)
7544             ClosePopupToLevel(g.BeginPopupStack.Size, true);
7545         return false;
7546     }
7547     return is_open;
7548 }
7549 
EndPopup()7550 void ImGui::EndPopup()
7551 {
7552     ImGuiContext& g = *GImGui;
7553     ImGuiWindow* window = g.CurrentWindow;
7554     IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup);  // Mismatched BeginPopup()/EndPopup() calls
7555     IM_ASSERT(g.BeginPopupStack.Size > 0);
7556 
7557     // Make all menus and popups wrap around for now, may need to expose that policy.
7558     NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
7559 
7560     // Child-popups don't need to be layed out
7561     IM_ASSERT(g.WithinEndChild == false);
7562     if (window->Flags & ImGuiWindowFlags_ChildWindow)
7563         g.WithinEndChild = true;
7564     End();
7565     g.WithinEndChild = false;
7566 }
7567 
OpenPopupOnItemClick(const char * str_id,int mouse_button)7568 bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button)
7569 {
7570     ImGuiWindow* window = GImGui->CurrentWindow;
7571     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
7572     {
7573         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!
7574         IM_ASSERT(id != 0);                                                  // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
7575         OpenPopupEx(id);
7576         return true;
7577     }
7578     return false;
7579 }
7580 
7581 // This is a helper to handle the simplest case of associating one named popup to one given widget.
7582 // You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
7583 // You can pass a NULL str_id to use the identifier of the last item.
BeginPopupContextItem(const char * str_id,int mouse_button)7584 bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button)
7585 {
7586     ImGuiWindow* window = GImGui->CurrentWindow;
7587     if (window->SkipItems)
7588         return false;
7589     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!
7590     IM_ASSERT(id != 0);                                                  // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
7591     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
7592         OpenPopupEx(id);
7593     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
7594 }
7595 
BeginPopupContextWindow(const char * str_id,int mouse_button,bool also_over_items)7596 bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items)
7597 {
7598     if (!str_id)
7599         str_id = "window_context";
7600     ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
7601     if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
7602         if (also_over_items || !IsAnyItemHovered())
7603             OpenPopupEx(id);
7604     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
7605 }
7606 
BeginPopupContextVoid(const char * str_id,int mouse_button)7607 bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
7608 {
7609     if (!str_id)
7610         str_id = "void_context";
7611     ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
7612     if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
7613         OpenPopupEx(id);
7614     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
7615 }
7616 
7617 // 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.)
7618 // 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)7619 ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
7620 {
7621     ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
7622     //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
7623     //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
7624 
7625     // Combo Box policy (we want a connecting edge)
7626     if (policy == ImGuiPopupPositionPolicy_ComboBox)
7627     {
7628         const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
7629         for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
7630         {
7631             const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
7632             if (n != -1 && dir == *last_dir) // Already tried this direction?
7633                 continue;
7634             ImVec2 pos;
7635             if (dir == ImGuiDir_Down)  pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y);          // Below, Toward Right (default)
7636             if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
7637             if (dir == ImGuiDir_Left)  pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
7638             if (dir == ImGuiDir_Up)    pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
7639             if (!r_outer.Contains(ImRect(pos, pos + size)))
7640                 continue;
7641             *last_dir = dir;
7642             return pos;
7643         }
7644     }
7645 
7646     // Default popup policy
7647     const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
7648     for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
7649     {
7650         const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
7651         if (n != -1 && dir == *last_dir) // Already tried this direction?
7652             continue;
7653         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);
7654         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);
7655         if (avail_w < size.x || avail_h < size.y)
7656             continue;
7657         ImVec2 pos;
7658         pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
7659         pos.y = (dir == ImGuiDir_Up)   ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down)  ? r_avoid.Max.y : base_pos_clamped.y;
7660         *last_dir = dir;
7661         return pos;
7662     }
7663 
7664     // Fallback, try to keep within display
7665     *last_dir = ImGuiDir_None;
7666     ImVec2 pos = ref_pos;
7667     pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
7668     pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
7669     return pos;
7670 }
7671 
GetWindowAllowedExtentRect(ImGuiWindow * window)7672 ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window)
7673 {
7674     IM_UNUSED(window);
7675     ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding;
7676     ImRect r_screen = GetViewportRect();
7677     r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
7678     return r_screen;
7679 }
7680 
FindBestWindowPosForPopup(ImGuiWindow * window)7681 ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
7682 {
7683     ImGuiContext& g = *GImGui;
7684 
7685     ImRect r_outer = GetWindowAllowedExtentRect(window);
7686     if (window->Flags & ImGuiWindowFlags_ChildMenu)
7687     {
7688         // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
7689         // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
7690         IM_ASSERT(g.CurrentWindow == window);
7691         ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2];
7692         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).
7693         ImRect r_avoid;
7694         if (parent_window->DC.MenuBarAppending)
7695             r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight());
7696         else
7697             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);
7698         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
7699     }
7700     if (window->Flags & ImGuiWindowFlags_Popup)
7701     {
7702         ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
7703         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
7704     }
7705     if (window->Flags & ImGuiWindowFlags_Tooltip)
7706     {
7707         // Position tooltip (always follows mouse)
7708         float sc = g.Style.MouseCursorScale;
7709         ImVec2 ref_pos = NavCalcPreferredRefPos();
7710         ImRect r_avoid;
7711         if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
7712             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
7713         else
7714             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.
7715         ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
7716         if (window->AutoPosLastDirection == ImGuiDir_None)
7717             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.
7718         return pos;
7719     }
7720     IM_ASSERT(0);
7721     return window->Pos;
7722 }
7723 
7724 
7725 //-----------------------------------------------------------------------------
7726 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
7727 //-----------------------------------------------------------------------------
7728 
ImGetDirQuadrantFromDelta(float dx,float dy)7729 ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
7730 {
7731     if (ImFabs(dx) > ImFabs(dy))
7732         return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
7733     return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
7734 }
7735 
NavScoreItemDistInterval(float a0,float a1,float b0,float b1)7736 static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
7737 {
7738     if (a1 < b0)
7739         return a1 - b0;
7740     if (b1 < a0)
7741         return a0 - b1;
7742     return 0.0f;
7743 }
7744 
NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir,ImRect & r,const ImRect & clip_rect)7745 static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect)
7746 {
7747     if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
7748     {
7749         r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y);
7750         r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y);
7751     }
7752     else
7753     {
7754         r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x);
7755         r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x);
7756     }
7757 }
7758 
7759 // Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057
NavScoreItem(ImGuiNavMoveResult * result,ImRect cand)7760 static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
7761 {
7762     ImGuiContext& g = *GImGui;
7763     ImGuiWindow* window = g.CurrentWindow;
7764     if (g.NavLayer != window->DC.NavLayerCurrent)
7765         return false;
7766 
7767     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)
7768     g.NavScoringCount++;
7769 
7770     // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
7771     if (window->ParentWindow == g.NavWindow)
7772     {
7773         IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened);
7774         if (!window->ClipRect.Overlaps(cand))
7775             return false;
7776         cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
7777     }
7778 
7779     // 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)
7780     // For example, this ensure that items in one column are not reached when moving vertically from items in another column.
7781     NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect);
7782 
7783     // Compute distance between boxes
7784     // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
7785     float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
7786     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
7787     if (dby != 0.0f && dbx != 0.0f)
7788        dbx = (dbx/1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
7789     float dist_box = ImFabs(dbx) + ImFabs(dby);
7790 
7791     // 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)
7792     float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
7793     float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
7794     float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
7795 
7796     // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
7797     ImGuiDir quadrant;
7798     float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
7799     if (dbx != 0.0f || dby != 0.0f)
7800     {
7801         // For non-overlapping boxes, use distance between boxes
7802         dax = dbx;
7803         day = dby;
7804         dist_axial = dist_box;
7805         quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
7806     }
7807     else if (dcx != 0.0f || dcy != 0.0f)
7808     {
7809         // For overlapping boxes with different centers, use distance between centers
7810         dax = dcx;
7811         day = dcy;
7812         dist_axial = dist_center;
7813         quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
7814     }
7815     else
7816     {
7817         // 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)
7818         quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
7819     }
7820 
7821 #if IMGUI_DEBUG_NAV_SCORING
7822     char buf[128];
7823     if (IsMouseHoveringRect(cand.Min, cand.Max))
7824     {
7825         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]);
7826         ImDrawList* draw_list = GetForegroundDrawList(window);
7827         draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
7828         draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
7829         draw_list->AddRectFilled(cand.Max - ImVec2(4,4), cand.Max + CalcTextSize(buf) + ImVec2(4,4), IM_COL32(40,0,0,150));
7830         draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
7831     }
7832     else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
7833     {
7834         if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; }
7835         if (quadrant == g.NavMoveDir)
7836         {
7837             ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
7838             ImDrawList* draw_list = GetForegroundDrawList(window);
7839             draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
7840             draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
7841         }
7842     }
7843  #endif
7844 
7845     // Is it in the quadrant we're interesting in moving to?
7846     bool new_best = false;
7847     if (quadrant == g.NavMoveDir)
7848     {
7849         // Does it beat the current best candidate?
7850         if (dist_box < result->DistBox)
7851         {
7852             result->DistBox = dist_box;
7853             result->DistCenter = dist_center;
7854             return true;
7855         }
7856         if (dist_box == result->DistBox)
7857         {
7858             // Try using distance between center points to break ties
7859             if (dist_center < result->DistCenter)
7860             {
7861                 result->DistCenter = dist_center;
7862                 new_best = true;
7863             }
7864             else if (dist_center == result->DistCenter)
7865             {
7866                 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
7867                 // (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),
7868                 // 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.
7869                 if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
7870                     new_best = true;
7871             }
7872         }
7873     }
7874 
7875     // 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
7876     // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
7877     // 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.
7878     // 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.
7879     // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
7880     if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial)  // Check axial match
7881         if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
7882             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))
7883             {
7884                 result->DistAxial = dist_axial;
7885                 new_best = true;
7886             }
7887 
7888     return new_best;
7889 }
7890 
7891 // 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)7892 static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
7893 {
7894     ImGuiContext& g = *GImGui;
7895     //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.
7896     //    return;
7897 
7898     const ImGuiItemFlags item_flags = window->DC.ItemFlags;
7899     const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
7900 
7901     // Process Init Request
7902     if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
7903     {
7904         // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
7905         if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0)
7906         {
7907             g.NavInitResultId = id;
7908             g.NavInitResultRectRel = nav_bb_rel;
7909         }
7910         if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus))
7911         {
7912             g.NavInitRequest = false; // Found a match, clear request
7913             NavUpdateAnyRequestFlag();
7914         }
7915     }
7916 
7917     // Process Move Request (scoring for navigation)
7918     // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
7919     if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled|ImGuiItemFlags_NoNav)))
7920     {
7921         ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
7922 #if IMGUI_DEBUG_NAV_SCORING
7923         // [DEBUG] Score all items in NavWindow at all times
7924         if (!g.NavMoveRequest)
7925             g.NavMoveDir = g.NavMoveDirLast;
7926         bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest;
7927 #else
7928         bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb);
7929 #endif
7930         if (new_best)
7931         {
7932             result->ID = id;
7933             result->SelectScopeId = g.MultiSelectScopeId;
7934             result->Window = window;
7935             result->RectRel = nav_bb_rel;
7936         }
7937 
7938         const float VISIBLE_RATIO = 0.70f;
7939         if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
7940             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)
7941                 if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb))
7942                 {
7943                     result = &g.NavMoveResultLocalVisibleSet;
7944                     result->ID = id;
7945                     result->SelectScopeId = g.MultiSelectScopeId;
7946                     result->Window = window;
7947                     result->RectRel = nav_bb_rel;
7948                 }
7949     }
7950 
7951     // Update window-relative bounding box of navigated item
7952     if (g.NavId == id)
7953     {
7954         g.NavWindow = window;                                           // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
7955         g.NavLayer = window->DC.NavLayerCurrent;
7956         g.NavIdIsAlive = true;
7957         g.NavIdTabCounter = window->DC.FocusCounterTab;
7958         window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel;    // Store item bounding box (relative to window position)
7959     }
7960 }
7961 
NavMoveRequestButNoResultYet()7962 bool ImGui::NavMoveRequestButNoResultYet()
7963 {
7964     ImGuiContext& g = *GImGui;
7965     return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
7966 }
7967 
NavMoveRequestCancel()7968 void ImGui::NavMoveRequestCancel()
7969 {
7970     ImGuiContext& g = *GImGui;
7971     g.NavMoveRequest = false;
7972     NavUpdateAnyRequestFlag();
7973 }
7974 
NavMoveRequestForward(ImGuiDir move_dir,ImGuiDir clip_dir,const ImRect & bb_rel,ImGuiNavMoveFlags move_flags)7975 void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags)
7976 {
7977     ImGuiContext& g = *GImGui;
7978     IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None);
7979     NavMoveRequestCancel();
7980     g.NavMoveDir = move_dir;
7981     g.NavMoveClipDir = clip_dir;
7982     g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
7983     g.NavMoveRequestFlags = move_flags;
7984     g.NavWindow->NavRectRel[g.NavLayer] = bb_rel;
7985 }
7986 
NavMoveRequestTryWrapping(ImGuiWindow * window,ImGuiNavMoveFlags move_flags)7987 void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
7988 {
7989     ImGuiContext& g = *GImGui;
7990     if (g.NavWindow != window || !NavMoveRequestButNoResultYet() || g.NavMoveRequestForward != ImGuiNavForward_None || g.NavLayer != 0)
7991         return;
7992     IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
7993     ImRect bb_rel = window->NavRectRel[0];
7994 
7995     ImGuiDir clip_dir = g.NavMoveDir;
7996     if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
7997     {
7998         bb_rel.Min.x = bb_rel.Max.x = ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x;
7999         if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(-bb_rel.GetHeight()); clip_dir = ImGuiDir_Up; }
8000         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
8001     }
8002     if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
8003     {
8004         bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
8005         if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(+bb_rel.GetHeight()); clip_dir = ImGuiDir_Down; }
8006         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
8007     }
8008     if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
8009     {
8010         bb_rel.Min.y = bb_rel.Max.y = ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y;
8011         if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(-bb_rel.GetWidth()); clip_dir = ImGuiDir_Left; }
8012         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
8013     }
8014     if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
8015     {
8016         bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
8017         if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(+bb_rel.GetWidth()); clip_dir = ImGuiDir_Right; }
8018         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
8019     }
8020 }
8021 
8022 // FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
8023 // This way we could find the last focused window among our children. It would be much less confusing this way?
NavSaveLastChildNavWindowIntoParent(ImGuiWindow * nav_window)8024 static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window)
8025 {
8026     ImGuiWindow* parent_window = nav_window;
8027     while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
8028         parent_window = parent_window->ParentWindow;
8029     if (parent_window && parent_window != nav_window)
8030         parent_window->NavLastChildNavWindow = nav_window;
8031 }
8032 
8033 // Restore the last focused child.
8034 // Call when we are expected to land on the Main Layer (0) after FocusWindow()
NavRestoreLastChildNavWindow(ImGuiWindow * window)8035 static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
8036 {
8037     return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window;
8038 }
8039 
NavRestoreLayer(ImGuiNavLayer layer)8040 static void NavRestoreLayer(ImGuiNavLayer layer)
8041 {
8042     ImGuiContext& g = *GImGui;
8043     g.NavLayer = layer;
8044     if (layer == 0)
8045         g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow);
8046     if (layer == 0 && g.NavWindow->NavLastIds[0] != 0)
8047         ImGui::SetNavIDWithRectRel(g.NavWindow->NavLastIds[0], layer, g.NavWindow->NavRectRel[0]);
8048     else
8049         ImGui::NavInitWindow(g.NavWindow, true);
8050 }
8051 
NavUpdateAnyRequestFlag()8052 static inline void ImGui::NavUpdateAnyRequestFlag()
8053 {
8054     ImGuiContext& g = *GImGui;
8055     g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
8056     if (g.NavAnyRequest)
8057         IM_ASSERT(g.NavWindow != NULL);
8058 }
8059 
8060 // This needs to be called before we submit any widget (aka in or before Begin)
NavInitWindow(ImGuiWindow * window,bool force_reinit)8061 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
8062 {
8063     ImGuiContext& g = *GImGui;
8064     IM_ASSERT(window == g.NavWindow);
8065     bool init_for_nav = false;
8066     if (!(window->Flags & ImGuiWindowFlags_NoNavInputs))
8067         if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
8068             init_for_nav = true;
8069     //IMGUI_DEBUG_LOG("[Nav] NavInitWindow() init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer);
8070     if (init_for_nav)
8071     {
8072         SetNavID(0, g.NavLayer);
8073         g.NavInitRequest = true;
8074         g.NavInitRequestFromMove = false;
8075         g.NavInitResultId = 0;
8076         g.NavInitResultRectRel = ImRect();
8077         NavUpdateAnyRequestFlag();
8078     }
8079     else
8080     {
8081         g.NavId = window->NavLastIds[0];
8082     }
8083 }
8084 
NavCalcPreferredRefPos()8085 static ImVec2 ImGui::NavCalcPreferredRefPos()
8086 {
8087     ImGuiContext& g = *GImGui;
8088     if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow)
8089     {
8090         // Mouse (we need a fallback in case the mouse becomes invalid after being used)
8091         if (IsMousePosValid(&g.IO.MousePos))
8092             return g.IO.MousePos;
8093         return g.LastValidMousePos;
8094     }
8095     else
8096     {
8097         // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item.
8098         const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
8099         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()));
8100         ImRect visible_rect = GetViewportRect();
8101         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.
8102     }
8103 }
8104 
GetNavInputAmount(ImGuiNavInput n,ImGuiInputReadMode mode)8105 float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode)
8106 {
8107     ImGuiContext& g = *GImGui;
8108     if (mode == ImGuiInputReadMode_Down)
8109         return g.IO.NavInputs[n];                         // Instant, read analog input (0.0f..1.0f, as provided by user)
8110 
8111     const float t = g.IO.NavInputsDownDuration[n];
8112     if (t < 0.0f && mode == ImGuiInputReadMode_Released)  // Return 1.0f when just released, no repeat, ignore analog input.
8113         return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
8114     if (t < 0.0f)
8115         return 0.0f;
8116     if (mode == ImGuiInputReadMode_Pressed)               // Return 1.0f when just pressed, no repeat, ignore analog input.
8117         return (t == 0.0f) ? 1.0f : 0.0f;
8118     if (mode == ImGuiInputReadMode_Repeat)
8119         return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.80f);
8120     if (mode == ImGuiInputReadMode_RepeatSlow)
8121         return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 1.25f, g.IO.KeyRepeatRate * 2.00f);
8122     if (mode == ImGuiInputReadMode_RepeatFast)
8123         return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.30f);
8124     return 0.0f;
8125 }
8126 
GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources,ImGuiInputReadMode mode,float slow_factor,float fast_factor)8127 ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor)
8128 {
8129     ImVec2 delta(0.0f, 0.0f);
8130     if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
8131         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode)   - GetNavInputAmount(ImGuiNavInput_KeyLeft_,   mode), GetNavInputAmount(ImGuiNavInput_KeyDown_,   mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_,   mode));
8132     if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
8133         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode)   - GetNavInputAmount(ImGuiNavInput_DpadLeft,   mode), GetNavInputAmount(ImGuiNavInput_DpadDown,   mode) - GetNavInputAmount(ImGuiNavInput_DpadUp,   mode));
8134     if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
8135         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode));
8136     if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow))
8137         delta *= slow_factor;
8138     if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast))
8139         delta *= fast_factor;
8140     return delta;
8141 }
8142 
NavUpdate()8143 static void ImGui::NavUpdate()
8144 {
8145     ImGuiContext& g = *GImGui;
8146     g.IO.WantSetMousePos = false;
8147 #if 0
8148     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);
8149 #endif
8150 
8151     // Set input source as Gamepad when buttons are pressed before we map Keyboard (some features differs when used with Gamepad vs Keyboard)
8152     bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
8153     bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
8154     if (nav_gamepad_active)
8155         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)
8156             g.NavInputSource = ImGuiInputSource_NavGamepad;
8157 
8158     // Update Keyboard->Nav inputs mapping
8159     if (nav_keyboard_active)
8160     {
8161         #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)
8162         NAV_MAP_KEY(ImGuiKey_Space,     ImGuiNavInput_Activate );
8163         NAV_MAP_KEY(ImGuiKey_Enter,     ImGuiNavInput_Input    );
8164         NAV_MAP_KEY(ImGuiKey_Escape,    ImGuiNavInput_Cancel   );
8165         NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ );
8166         NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
8167         NAV_MAP_KEY(ImGuiKey_UpArrow,   ImGuiNavInput_KeyUp_   );
8168         NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
8169         if (g.IO.KeyCtrl)
8170             g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
8171         if (g.IO.KeyShift)
8172             g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
8173         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.
8174             g.IO.NavInputs[ImGuiNavInput_KeyMenu_]  = 1.0f;
8175         #undef NAV_MAP_KEY
8176     }
8177     memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration));
8178     for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++)
8179         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;
8180 
8181     // Process navigation init request (select first/default focus)
8182     // 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)
8183     if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove) && g.NavWindow)
8184     {
8185         // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
8186         //IMGUI_DEBUG_LOG("[Nav] Apply NavInitRequest result: 0x%08X Layer %d in \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name);
8187         if (g.NavInitRequestFromMove)
8188             SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, g.NavInitResultRectRel);
8189         else
8190             SetNavID(g.NavInitResultId, g.NavLayer);
8191         g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel;
8192     }
8193     g.NavInitRequest = false;
8194     g.NavInitRequestFromMove = false;
8195     g.NavInitResultId = 0;
8196     g.NavJustMovedToId = 0;
8197 
8198     // Process navigation move request
8199     if (g.NavMoveRequest)
8200         NavUpdateMoveResult();
8201 
8202     // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
8203     if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
8204     {
8205         IM_ASSERT(g.NavMoveRequest);
8206         if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
8207             g.NavDisableHighlight = false;
8208         g.NavMoveRequestForward = ImGuiNavForward_None;
8209     }
8210 
8211     // Apply application mouse position movement, after we had a chance to process move request result.
8212     if (g.NavMousePosDirty && g.NavIdIsAlive)
8213     {
8214         // Set mouse position given our knowledge of the navigated item position from last frame
8215         if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
8216         {
8217             if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
8218             {
8219                 g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos();
8220                 g.IO.WantSetMousePos = true;
8221             }
8222         }
8223         g.NavMousePosDirty = false;
8224     }
8225     g.NavIdIsAlive = false;
8226     g.NavJustTabbedId = 0;
8227     IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
8228 
8229     // 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
8230     if (g.NavWindow)
8231         NavSaveLastChildNavWindowIntoParent(g.NavWindow);
8232     if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0)
8233         g.NavWindow->NavLastChildNavWindow = NULL;
8234 
8235     // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
8236     NavUpdateWindowing();
8237 
8238     // Set output flags for user application
8239     g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
8240     g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL);
8241 
8242     // Process NavCancel input (to close a popup, get back to parent, clear focus)
8243     if (IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
8244     {
8245         if (g.ActiveId != 0)
8246         {
8247             if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel))
8248                 ClearActiveID();
8249         }
8250         else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
8251         {
8252             // Exit child window
8253             ImGuiWindow* child_window = g.NavWindow;
8254             ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
8255             IM_ASSERT(child_window->ChildId != 0);
8256             FocusWindow(parent_window);
8257             SetNavID(child_window->ChildId, 0);
8258             g.NavIdIsAlive = false;
8259             if (g.NavDisableMouseHover)
8260                 g.NavMousePosDirty = true;
8261         }
8262         else if (g.OpenPopupStack.Size > 0)
8263         {
8264             // Close open popup/menu
8265             if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
8266                 ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
8267         }
8268         else if (g.NavLayer != 0)
8269         {
8270             // Leave the "menu" layer
8271             NavRestoreLayer(ImGuiNavLayer_Main);
8272         }
8273         else
8274         {
8275             // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
8276             if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
8277                 g.NavWindow->NavLastIds[0] = 0;
8278             g.NavId = 0;
8279         }
8280     }
8281 
8282     // Process manual activation request
8283     g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
8284     if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8285     {
8286         bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
8287         bool activate_pressed = activate_down && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
8288         if (g.ActiveId == 0 && activate_pressed)
8289             g.NavActivateId = g.NavId;
8290         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
8291             g.NavActivateDownId = g.NavId;
8292         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
8293             g.NavActivatePressedId = g.NavId;
8294         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
8295             g.NavInputId = g.NavId;
8296     }
8297     if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8298         g.NavDisableHighlight = true;
8299     if (g.NavActivateId != 0)
8300         IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
8301     g.NavMoveRequest = false;
8302 
8303     // Process programmatic activation request
8304     if (g.NavNextActivateId != 0)
8305         g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
8306     g.NavNextActivateId = 0;
8307 
8308     // Initiate directional inputs request
8309     if (g.NavMoveRequestForward == ImGuiNavForward_None)
8310     {
8311         g.NavMoveDir = ImGuiDir_None;
8312         g.NavMoveRequestFlags = ImGuiNavMoveFlags_None;
8313         if (g.NavWindow && !g.NavWindowingTarget && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8314         {
8315             const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat;
8316             if (!IsActiveIdUsingNavDir(ImGuiDir_Left)  && (IsNavInputTest(ImGuiNavInput_DpadLeft,  read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_,  read_mode))) { g.NavMoveDir = ImGuiDir_Left; }
8317             if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; }
8318             if (!IsActiveIdUsingNavDir(ImGuiDir_Up)    && (IsNavInputTest(ImGuiNavInput_DpadUp,    read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_,    read_mode))) { g.NavMoveDir = ImGuiDir_Up; }
8319             if (!IsActiveIdUsingNavDir(ImGuiDir_Down)  && (IsNavInputTest(ImGuiNavInput_DpadDown,  read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_,  read_mode))) { g.NavMoveDir = ImGuiDir_Down; }
8320         }
8321         g.NavMoveClipDir = g.NavMoveDir;
8322     }
8323     else
8324     {
8325         // 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)
8326         // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function)
8327         IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
8328         IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
8329         g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
8330     }
8331 
8332     // Update PageUp/PageDown/Home/End scroll
8333     // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
8334     float nav_scoring_rect_offset_y = 0.0f;
8335     if (nav_keyboard_active)
8336         nav_scoring_rect_offset_y = NavUpdatePageUpPageDown();
8337 
8338     // 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
8339     if (g.NavMoveDir != ImGuiDir_None)
8340     {
8341         g.NavMoveRequest = true;
8342         g.NavMoveDirLast = g.NavMoveDir;
8343     }
8344     if (g.NavMoveRequest && g.NavId == 0)
8345     {
8346         //IMGUI_DEBUG_LOG("[Nav] NavInitRequest from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer);
8347         g.NavInitRequest = g.NavInitRequestFromMove = true;
8348         g.NavInitResultId = 0;
8349         g.NavDisableHighlight = false;
8350     }
8351     NavUpdateAnyRequestFlag();
8352 
8353     // Scrolling
8354     if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
8355     {
8356         // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
8357         ImGuiWindow* window = g.NavWindow;
8358         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.
8359         if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
8360         {
8361             if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
8362                 SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
8363             if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
8364                 SetScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
8365         }
8366 
8367         // *Normal* Manual scroll with NavScrollXXX keys
8368         // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
8369         ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f);
8370         if (scroll_dir.x != 0.0f && window->ScrollbarX)
8371         {
8372             SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
8373             g.NavMoveFromClampedRefRect = true;
8374         }
8375         if (scroll_dir.y != 0.0f)
8376         {
8377             SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
8378             g.NavMoveFromClampedRefRect = true;
8379         }
8380     }
8381 
8382     // Reset search results
8383     g.NavMoveResultLocal.Clear();
8384     g.NavMoveResultLocalVisibleSet.Clear();
8385     g.NavMoveResultOther.Clear();
8386 
8387     // 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
8388     if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0)
8389     {
8390         ImGuiWindow* window = g.NavWindow;
8391         ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1,1), window->InnerRect.Max - window->Pos + ImVec2(1,1));
8392         if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
8393         {
8394             float pad = window->CalcFontSize() * 0.5f;
8395             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
8396             window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel);
8397             g.NavId = 0;
8398         }
8399         g.NavMoveFromClampedRefRect = false;
8400     }
8401 
8402     // 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)
8403     ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0);
8404     g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect();
8405     g.NavScoringRectScreen.TranslateY(nav_scoring_rect_offset_y);
8406     g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x);
8407     g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x;
8408     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().
8409     //GetForegroundDrawList()->AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
8410     g.NavScoringCount = 0;
8411 #if IMGUI_DEBUG_NAV_RECTS
8412     if (g.NavWindow)
8413     {
8414         ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow);
8415         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]
8416         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); }
8417     }
8418 #endif
8419 }
8420 
8421 // Apply result from previous frame navigation directional move request
NavUpdateMoveResult()8422 static void ImGui::NavUpdateMoveResult()
8423 {
8424     ImGuiContext& g = *GImGui;
8425     if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
8426     {
8427         // 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)
8428         if (g.NavId != 0)
8429         {
8430             g.NavDisableHighlight = false;
8431             g.NavDisableMouseHover = true;
8432         }
8433         return;
8434     }
8435 
8436     // Select which result to use
8437     ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
8438 
8439     // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
8440     if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
8441         if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId)
8442             result = &g.NavMoveResultLocalVisibleSet;
8443 
8444     // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
8445     if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
8446         if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
8447             result = &g.NavMoveResultOther;
8448     IM_ASSERT(g.NavWindow && result->Window);
8449 
8450     // Scroll to keep newly navigated item fully into view.
8451     if (g.NavLayer == 0)
8452     {
8453         ImVec2 delta_scroll;
8454         if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_ScrollToEdge)
8455         {
8456             float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
8457             delta_scroll.y = result->Window->Scroll.y - scroll_target;
8458             SetScrollY(result->Window, scroll_target);
8459         }
8460         else
8461         {
8462             ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos);
8463             delta_scroll = ScrollToBringRectIntoView(result->Window, rect_abs);
8464         }
8465 
8466         // Offset our result position so mouse position can be applied immediately after in NavUpdate()
8467         result->RectRel.TranslateX(-delta_scroll.x);
8468         result->RectRel.TranslateY(-delta_scroll.y);
8469     }
8470 
8471     ClearActiveID();
8472     g.NavWindow = result->Window;
8473     if (g.NavId != result->ID)
8474     {
8475         // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
8476         g.NavJustMovedToId = result->ID;
8477         g.NavJustMovedToMultiSelectScopeId = result->SelectScopeId;
8478     }
8479     SetNavIDWithRectRel(result->ID, g.NavLayer, result->RectRel);
8480     g.NavMoveFromClampedRefRect = false;
8481 }
8482 
8483 // Handle PageUp/PageDown/Home/End keys
NavUpdatePageUpPageDown()8484 static float ImGui::NavUpdatePageUpPageDown()
8485 {
8486     ImGuiContext& g = *GImGui;
8487     if (g.NavMoveDir != ImGuiDir_None || g.NavWindow == NULL)
8488         return 0.0f;
8489     if ((g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != 0)
8490         return 0.0f;
8491 
8492     ImGuiWindow* window = g.NavWindow;
8493     const bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp);
8494     const bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown);
8495     const bool home_pressed = IsKeyPressed(g.IO.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home);
8496     const bool end_pressed = IsKeyPressed(g.IO.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End);
8497     if (page_up_held != page_down_held || home_pressed != end_pressed) // If either (not both) are pressed
8498     {
8499         if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll)
8500         {
8501             // Fallback manual-scroll when window has no navigable item
8502             if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
8503                 SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
8504             else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
8505                 SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
8506             else if (home_pressed)
8507                 SetScrollY(window, 0.0f);
8508             else if (end_pressed)
8509                 SetScrollY(window, window->ScrollMax.y);
8510         }
8511         else
8512         {
8513             ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
8514             const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
8515             float nav_scoring_rect_offset_y = 0.0f;
8516             if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
8517             {
8518                 nav_scoring_rect_offset_y = -page_offset_y;
8519                 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)
8520                 g.NavMoveClipDir = ImGuiDir_Up;
8521                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
8522             }
8523             else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
8524             {
8525                 nav_scoring_rect_offset_y = +page_offset_y;
8526                 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)
8527                 g.NavMoveClipDir = ImGuiDir_Down;
8528                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
8529             }
8530             else if (home_pressed)
8531             {
8532                 // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
8533                 // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result.
8534                 // Preserve current horizontal position if we have any.
8535                 nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y;
8536                 if (nav_rect_rel.IsInverted())
8537                     nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
8538                 g.NavMoveDir = ImGuiDir_Down;
8539                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
8540             }
8541             else if (end_pressed)
8542             {
8543                 nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y;
8544                 if (nav_rect_rel.IsInverted())
8545                     nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
8546                 g.NavMoveDir = ImGuiDir_Up;
8547                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
8548             }
8549             return nav_scoring_rect_offset_y;
8550         }
8551     }
8552     return 0.0f;
8553 }
8554 
FindWindowFocusIndex(ImGuiWindow * window)8555 static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N)
8556 {
8557     ImGuiContext& g = *GImGui;
8558     for (int i = g.WindowsFocusOrder.Size-1; i >= 0; i--)
8559         if (g.WindowsFocusOrder[i] == window)
8560             return i;
8561     return -1;
8562 }
8563 
FindWindowNavFocusable(int i_start,int i_stop,int dir)8564 static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
8565 {
8566     ImGuiContext& g = *GImGui;
8567     for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
8568         if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
8569             return g.WindowsFocusOrder[i];
8570     return NULL;
8571 }
8572 
NavUpdateWindowingHighlightWindow(int focus_change_dir)8573 static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
8574 {
8575     ImGuiContext& g = *GImGui;
8576     IM_ASSERT(g.NavWindowingTarget);
8577     if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
8578         return;
8579 
8580     const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget);
8581     ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
8582     if (!window_target)
8583         window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
8584     if (window_target) // Don't reset windowing target if there's a single window in the list
8585         g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
8586     g.NavWindowingToggleLayer = false;
8587 }
8588 
8589 // Windowing management mode
8590 // Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer)
8591 // Gamepad:  Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)
NavUpdateWindowing()8592 static void ImGui::NavUpdateWindowing()
8593 {
8594     ImGuiContext& g = *GImGui;
8595     ImGuiWindow* apply_focus_window = NULL;
8596     bool apply_toggle_layer = false;
8597 
8598     ImGuiWindow* modal_window = GetTopMostPopupModal();
8599     if (modal_window != NULL)
8600     {
8601         g.NavWindowingTarget = NULL;
8602         return;
8603     }
8604 
8605     // Fade out
8606     if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
8607     {
8608         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f);
8609         if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
8610             g.NavWindowingTargetAnim = NULL;
8611     }
8612 
8613     // Start CTRL-TAB or Square+L/R window selection
8614     bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
8615     bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
8616     if (start_windowing_with_gamepad || start_windowing_with_keyboard)
8617         if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
8618         {
8619             g.NavWindowingTarget = g.NavWindowingTargetAnim = window;
8620             g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
8621             g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true;
8622             g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad;
8623         }
8624 
8625     // Gamepad update
8626     g.NavWindowingTimer += g.IO.DeltaTime;
8627     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad)
8628     {
8629         // 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
8630         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
8631 
8632         // Select window to focus
8633         const int focus_change_dir = (int)IsNavInputTest(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputTest(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow);
8634         if (focus_change_dir != 0)
8635         {
8636             NavUpdateWindowingHighlightWindow(focus_change_dir);
8637             g.NavWindowingHighlightAlpha = 1.0f;
8638         }
8639 
8640         // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)
8641         if (!IsNavInputDown(ImGuiNavInput_Menu))
8642         {
8643             g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
8644             if (g.NavWindowingToggleLayer && g.NavWindow)
8645                 apply_toggle_layer = true;
8646             else if (!g.NavWindowingToggleLayer)
8647                 apply_focus_window = g.NavWindowingTarget;
8648             g.NavWindowingTarget = NULL;
8649         }
8650     }
8651 
8652     // Keyboard: Focus
8653     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard)
8654     {
8655         // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
8656         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
8657         if (IsKeyPressedMap(ImGuiKey_Tab, true))
8658             NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1);
8659         if (!g.IO.KeyCtrl)
8660             apply_focus_window = g.NavWindowingTarget;
8661     }
8662 
8663     // Keyboard: Press and Release ALT to toggle menu layer
8664     // 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
8665     if (IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Pressed))
8666         g.NavWindowingToggleLayer = true;
8667     if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && g.NavWindowingToggleLayer && IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released))
8668         if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev))
8669             apply_toggle_layer = true;
8670 
8671     // Move window
8672     if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
8673     {
8674         ImVec2 move_delta;
8675         if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift)
8676             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
8677         if (g.NavInputSource == ImGuiInputSource_NavGamepad)
8678             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down);
8679         if (move_delta.x != 0.0f || move_delta.y != 0.0f)
8680         {
8681             const float NAV_MOVE_SPEED = 800.0f;
8682             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
8683             SetWindowPos(g.NavWindowingTarget->RootWindow, g.NavWindowingTarget->RootWindow->Pos + move_delta * move_speed, ImGuiCond_Always);
8684             g.NavDisableMouseHover = true;
8685             MarkIniSettingsDirty(g.NavWindowingTarget);
8686         }
8687     }
8688 
8689     // Apply final focus
8690     if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
8691     {
8692         ClearActiveID();
8693         g.NavDisableHighlight = false;
8694         g.NavDisableMouseHover = true;
8695         apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
8696         ClosePopupsOverWindow(apply_focus_window, false);
8697         FocusWindow(apply_focus_window);
8698         if (apply_focus_window->NavLastIds[0] == 0)
8699             NavInitWindow(apply_focus_window, false);
8700 
8701         // If the window only has a menu layer, select it directly
8702         if (apply_focus_window->DC.NavLayerActiveMask == (1 << ImGuiNavLayer_Menu))
8703             g.NavLayer = ImGuiNavLayer_Menu;
8704     }
8705     if (apply_focus_window)
8706         g.NavWindowingTarget = NULL;
8707 
8708     // Apply menu/layer toggle
8709     if (apply_toggle_layer && g.NavWindow)
8710     {
8711         // Move to parent menu if necessary
8712         ImGuiWindow* new_nav_window = g.NavWindow;
8713         while (new_nav_window->ParentWindow
8714             && (new_nav_window->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) == 0
8715             && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
8716             && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
8717             new_nav_window = new_nav_window->ParentWindow;
8718         if (new_nav_window != g.NavWindow)
8719         {
8720             ImGuiWindow* old_nav_window = g.NavWindow;
8721             FocusWindow(new_nav_window);
8722             new_nav_window->NavLastChildNavWindow = old_nav_window;
8723         }
8724         g.NavDisableHighlight = false;
8725         g.NavDisableMouseHover = true;
8726 
8727         // When entering a regular menu bar with the Alt key, we always reinitialize the navigation ID.
8728         const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;
8729         NavRestoreLayer(new_nav_layer);
8730     }
8731 }
8732 
8733 // Window has already passed the IsWindowNavFocusable()
GetFallbackWindowNameForWindowingList(ImGuiWindow * window)8734 static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
8735 {
8736     if (window->Flags & ImGuiWindowFlags_Popup)
8737         return "(Popup)";
8738     if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
8739         return "(Main menu bar)";
8740     return "(Untitled)";
8741 }
8742 
8743 // Overlay displayed when using CTRL+TAB. Called by EndFrame().
NavUpdateWindowingOverlay()8744 void ImGui::NavUpdateWindowingOverlay()
8745 {
8746     ImGuiContext& g = *GImGui;
8747     IM_ASSERT(g.NavWindowingTarget != NULL);
8748 
8749     if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
8750         return;
8751 
8752     if (g.NavWindowingList == NULL)
8753         g.NavWindowingList = FindWindowByName("###NavWindowingList");
8754     SetNextWindowSizeConstraints(ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
8755     SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
8756     PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
8757     Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
8758     for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
8759     {
8760         ImGuiWindow* window = g.WindowsFocusOrder[n];
8761         if (!IsWindowNavFocusable(window))
8762             continue;
8763         const char* label = window->Name;
8764         if (label == FindRenderedTextEnd(label))
8765             label = GetFallbackWindowNameForWindowingList(window);
8766         Selectable(label, g.NavWindowingTarget == window);
8767     }
8768     End();
8769     PopStyleVar();
8770 }
8771 
8772 
8773 //-----------------------------------------------------------------------------
8774 // [SECTION] DRAG AND DROP
8775 //-----------------------------------------------------------------------------
8776 
ClearDragDrop()8777 void ImGui::ClearDragDrop()
8778 {
8779     ImGuiContext& g = *GImGui;
8780     g.DragDropActive = false;
8781     g.DragDropPayload.Clear();
8782     g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
8783     g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
8784     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
8785     g.DragDropAcceptFrameCount = -1;
8786 
8787     g.DragDropPayloadBufHeap.clear();
8788     memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
8789 }
8790 
8791 // Call when current ID is active.
8792 // 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)8793 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
8794 {
8795     ImGuiContext& g = *GImGui;
8796     ImGuiWindow* window = g.CurrentWindow;
8797 
8798     bool source_drag_active = false;
8799     ImGuiID source_id = 0;
8800     ImGuiID source_parent_id = 0;
8801     int mouse_button = 0;
8802     if (!(flags & ImGuiDragDropFlags_SourceExtern))
8803     {
8804         source_id = window->DC.LastItemId;
8805         if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case
8806             return false;
8807         if (g.IO.MouseDown[mouse_button] == false)
8808             return false;
8809 
8810         if (source_id == 0)
8811         {
8812             // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
8813             // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
8814             if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
8815             {
8816                 IM_ASSERT(0);
8817                 return false;
8818             }
8819 
8820             // Early out
8821             if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
8822                 return false;
8823 
8824             // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
8825             // We build a throwaway ID based on current ID stack + relative AABB of items in window.
8826             // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
8827             // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
8828             source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect);
8829             bool is_hovered = ItemHoverable(window->DC.LastItemRect, source_id);
8830             if (is_hovered && g.IO.MouseClicked[mouse_button])
8831             {
8832                 SetActiveID(source_id, window);
8833                 FocusWindow(window);
8834             }
8835             if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
8836                 g.ActiveIdAllowOverlap = is_hovered;
8837         }
8838         else
8839         {
8840             g.ActiveIdAllowOverlap = false;
8841         }
8842         if (g.ActiveId != source_id)
8843             return false;
8844         source_parent_id = window->IDStack.back();
8845         source_drag_active = IsMouseDragging(mouse_button);
8846     }
8847     else
8848     {
8849         window = NULL;
8850         source_id = ImHashStr("#SourceExtern");
8851         source_drag_active = true;
8852     }
8853 
8854     if (source_drag_active)
8855     {
8856         if (!g.DragDropActive)
8857         {
8858             IM_ASSERT(source_id != 0);
8859             ClearDragDrop();
8860             ImGuiPayload& payload = g.DragDropPayload;
8861             payload.SourceId = source_id;
8862             payload.SourceParentId = source_parent_id;
8863             g.DragDropActive = true;
8864             g.DragDropSourceFlags = flags;
8865             g.DragDropMouseButton = mouse_button;
8866         }
8867         g.DragDropSourceFrameCount = g.FrameCount;
8868         g.DragDropWithinSourceOrTarget = true;
8869 
8870         if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
8871         {
8872             // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
8873             // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
8874             BeginTooltip();
8875             if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
8876             {
8877                 ImGuiWindow* tooltip_window = g.CurrentWindow;
8878                 tooltip_window->SkipItems = true;
8879                 tooltip_window->HiddenFramesCanSkipItems = 1;
8880             }
8881         }
8882 
8883         if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
8884             window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
8885 
8886         return true;
8887     }
8888     return false;
8889 }
8890 
EndDragDropSource()8891 void ImGui::EndDragDropSource()
8892 {
8893     ImGuiContext& g = *GImGui;
8894     IM_ASSERT(g.DragDropActive);
8895     IM_ASSERT(g.DragDropWithinSourceOrTarget && "Not after a BeginDragDropSource()?");
8896 
8897     if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
8898         EndTooltip();
8899 
8900     // Discard the drag if have not called SetDragDropPayload()
8901     if (g.DragDropPayload.DataFrameCount == -1)
8902         ClearDragDrop();
8903     g.DragDropWithinSourceOrTarget = false;
8904 }
8905 
8906 // 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)8907 bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
8908 {
8909     ImGuiContext& g = *GImGui;
8910     ImGuiPayload& payload = g.DragDropPayload;
8911     if (cond == 0)
8912         cond = ImGuiCond_Always;
8913 
8914     IM_ASSERT(type != NULL);
8915     IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
8916     IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
8917     IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
8918     IM_ASSERT(payload.SourceId != 0);                               // Not called between BeginDragDropSource() and EndDragDropSource()
8919 
8920     if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
8921     {
8922         // Copy payload
8923         ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
8924         g.DragDropPayloadBufHeap.resize(0);
8925         if (data_size > sizeof(g.DragDropPayloadBufLocal))
8926         {
8927             // Store in heap
8928             g.DragDropPayloadBufHeap.resize((int)data_size);
8929             payload.Data = g.DragDropPayloadBufHeap.Data;
8930             memcpy(payload.Data, data, data_size);
8931         }
8932         else if (data_size > 0)
8933         {
8934             // Store locally
8935             memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
8936             payload.Data = g.DragDropPayloadBufLocal;
8937             memcpy(payload.Data, data, data_size);
8938         }
8939         else
8940         {
8941             payload.Data = NULL;
8942         }
8943         payload.DataSize = (int)data_size;
8944     }
8945     payload.DataFrameCount = g.FrameCount;
8946 
8947     return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
8948 }
8949 
BeginDragDropTargetCustom(const ImRect & bb,ImGuiID id)8950 bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
8951 {
8952     ImGuiContext& g = *GImGui;
8953     if (!g.DragDropActive)
8954         return false;
8955 
8956     ImGuiWindow* window = g.CurrentWindow;
8957     if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
8958         return false;
8959     IM_ASSERT(id != 0);
8960     if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
8961         return false;
8962     if (window->SkipItems)
8963         return false;
8964 
8965     IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
8966     g.DragDropTargetRect = bb;
8967     g.DragDropTargetId = id;
8968     g.DragDropWithinSourceOrTarget = true;
8969     return true;
8970 }
8971 
8972 // We don't use BeginDragDropTargetCustom() and duplicate its code because:
8973 // 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
8974 // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
8975 // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
BeginDragDropTarget()8976 bool ImGui::BeginDragDropTarget()
8977 {
8978     ImGuiContext& g = *GImGui;
8979     if (!g.DragDropActive)
8980         return false;
8981 
8982     ImGuiWindow* window = g.CurrentWindow;
8983     if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
8984         return false;
8985     if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
8986         return false;
8987 
8988     const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect;
8989     ImGuiID id = window->DC.LastItemId;
8990     if (id == 0)
8991         id = window->GetIDFromRectangle(display_rect);
8992     if (g.DragDropPayload.SourceId == id)
8993         return false;
8994 
8995     IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
8996     g.DragDropTargetRect = display_rect;
8997     g.DragDropTargetId = id;
8998     g.DragDropWithinSourceOrTarget = true;
8999     return true;
9000 }
9001 
IsDragDropPayloadBeingAccepted()9002 bool ImGui::IsDragDropPayloadBeingAccepted()
9003 {
9004     ImGuiContext& g = *GImGui;
9005     return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
9006 }
9007 
AcceptDragDropPayload(const char * type,ImGuiDragDropFlags flags)9008 const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
9009 {
9010     ImGuiContext& g = *GImGui;
9011     ImGuiWindow* window = g.CurrentWindow;
9012     ImGuiPayload& payload = g.DragDropPayload;
9013     IM_ASSERT(g.DragDropActive);                        // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
9014     IM_ASSERT(payload.DataFrameCount != -1);            // Forgot to call EndDragDropTarget() ?
9015     if (type != NULL && !payload.IsDataType(type))
9016         return NULL;
9017 
9018     // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
9019     // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
9020     const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
9021     ImRect r = g.DragDropTargetRect;
9022     float r_surface = r.GetWidth() * r.GetHeight();
9023     if (r_surface < g.DragDropAcceptIdCurrRectSurface)
9024     {
9025         g.DragDropAcceptFlags = flags;
9026         g.DragDropAcceptIdCurr = g.DragDropTargetId;
9027         g.DragDropAcceptIdCurrRectSurface = r_surface;
9028     }
9029 
9030     // Render default drop visuals
9031     payload.Preview = was_accepted_previously;
9032     flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
9033     if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
9034     {
9035         // FIXME-DRAG: Settle on a proper default visuals for drop target.
9036         r.Expand(3.5f);
9037         bool push_clip_rect = !window->ClipRect.Contains(r);
9038         if (push_clip_rect) window->DrawList->PushClipRect(r.Min-ImVec2(1,1), r.Max+ImVec2(1,1));
9039         window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f);
9040         if (push_clip_rect) window->DrawList->PopClipRect();
9041     }
9042 
9043     g.DragDropAcceptFrameCount = g.FrameCount;
9044     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()
9045     if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
9046         return NULL;
9047 
9048     return &payload;
9049 }
9050 
GetDragDropPayload()9051 const ImGuiPayload* ImGui::GetDragDropPayload()
9052 {
9053     ImGuiContext& g = *GImGui;
9054     return g.DragDropActive ? &g.DragDropPayload : NULL;
9055 }
9056 
9057 // We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
EndDragDropTarget()9058 void ImGui::EndDragDropTarget()
9059 {
9060     ImGuiContext& g = *GImGui;
9061     IM_ASSERT(g.DragDropActive);
9062     IM_ASSERT(g.DragDropWithinSourceOrTarget);
9063     g.DragDropWithinSourceOrTarget = false;
9064 }
9065 
9066 
9067 //-----------------------------------------------------------------------------
9068 // [SECTION] LOGGING/CAPTURING
9069 //-----------------------------------------------------------------------------
9070 // All text output from the interface can be captured into tty/file/clipboard.
9071 // By default, tree nodes are automatically opened during logging.
9072 //-----------------------------------------------------------------------------
9073 
9074 // Pass text data straight to log (without being displayed)
LogText(const char * fmt,...)9075 void ImGui::LogText(const char* fmt, ...)
9076 {
9077     ImGuiContext& g = *GImGui;
9078     if (!g.LogEnabled)
9079         return;
9080 
9081     va_list args;
9082     va_start(args, fmt);
9083     if (g.LogFile)
9084     {
9085         g.LogBuffer.Buf.resize(0);
9086         g.LogBuffer.appendfv(fmt, args);
9087         ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile);
9088     }
9089     else
9090     {
9091         g.LogBuffer.appendfv(fmt, args);
9092     }
9093     va_end(args);
9094 }
9095 
9096 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
9097 // We split text into individual lines to add current tree level padding
LogRenderedText(const ImVec2 * ref_pos,const char * text,const char * text_end)9098 void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
9099 {
9100     ImGuiContext& g = *GImGui;
9101     ImGuiWindow* window = g.CurrentWindow;
9102 
9103     if (!text_end)
9104         text_end = FindRenderedTextEnd(text, text_end);
9105 
9106     const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + 1);
9107     if (ref_pos)
9108         g.LogLinePosY = ref_pos->y;
9109     if (log_new_line)
9110         g.LogLineFirstItem = true;
9111 
9112     const char* text_remaining = text;
9113     if (g.LogDepthRef > window->DC.TreeDepth)  // Re-adjust padding if we have popped out of our starting depth
9114         g.LogDepthRef = window->DC.TreeDepth;
9115     const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);
9116     for (;;)
9117     {
9118         // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
9119         // We don't add a trailing \n to allow a subsequent item on the same line to be captured.
9120         const char* line_start = text_remaining;
9121         const char* line_end = ImStreolRange(line_start, text_end);
9122         const bool is_first_line = (line_start == text);
9123         const bool is_last_line = (line_end == text_end);
9124         if (!is_last_line || (line_start != line_end))
9125         {
9126             const int char_count = (int)(line_end - line_start);
9127             if (log_new_line || !is_first_line)
9128                 LogText(IM_NEWLINE "%*s%.*s", tree_depth * 4, "", char_count, line_start);
9129             else if (g.LogLineFirstItem)
9130                 LogText("%*s%.*s", tree_depth * 4, "", char_count, line_start);
9131             else
9132                 LogText(" %.*s", char_count, line_start);
9133             g.LogLineFirstItem = false;
9134         }
9135         else if (log_new_line)
9136         {
9137             // An empty "" string at a different Y position should output a carriage return.
9138             LogText(IM_NEWLINE);
9139             break;
9140         }
9141 
9142         if (is_last_line)
9143             break;
9144         text_remaining = line_end + 1;
9145     }
9146 }
9147 
9148 // Start logging/capturing text output
LogBegin(ImGuiLogType type,int auto_open_depth)9149 void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth)
9150 {
9151     ImGuiContext& g = *GImGui;
9152     ImGuiWindow* window = g.CurrentWindow;
9153     IM_ASSERT(g.LogEnabled == false);
9154     IM_ASSERT(g.LogFile == NULL);
9155     IM_ASSERT(g.LogBuffer.empty());
9156     g.LogEnabled = true;
9157     g.LogType = type;
9158     g.LogDepthRef = window->DC.TreeDepth;
9159     g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);
9160     g.LogLinePosY = FLT_MAX;
9161     g.LogLineFirstItem = true;
9162 }
9163 
LogToTTY(int auto_open_depth)9164 void ImGui::LogToTTY(int auto_open_depth)
9165 {
9166     ImGuiContext& g = *GImGui;
9167     if (g.LogEnabled)
9168         return;
9169     IM_UNUSED(auto_open_depth);
9170 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
9171     LogBegin(ImGuiLogType_TTY, auto_open_depth);
9172     g.LogFile = stdout;
9173 #endif
9174 }
9175 
9176 // Start logging/capturing text output to given file
LogToFile(int auto_open_depth,const char * filename)9177 void ImGui::LogToFile(int auto_open_depth, const char* filename)
9178 {
9179     ImGuiContext& g = *GImGui;
9180     if (g.LogEnabled)
9181         return;
9182 
9183     // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still
9184     // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE.
9185     // By opening the file in binary mode "ab" we have consistent output everywhere.
9186     if (!filename)
9187         filename = g.IO.LogFilename;
9188     if (!filename || !filename[0])
9189         return;
9190     ImFileHandle f = ImFileOpen(filename, "ab");
9191     if (!f)
9192     {
9193         IM_ASSERT(0);
9194         return;
9195     }
9196 
9197     LogBegin(ImGuiLogType_File, auto_open_depth);
9198     g.LogFile = f;
9199 }
9200 
9201 // Start logging/capturing text output to clipboard
LogToClipboard(int auto_open_depth)9202 void ImGui::LogToClipboard(int auto_open_depth)
9203 {
9204     ImGuiContext& g = *GImGui;
9205     if (g.LogEnabled)
9206         return;
9207     LogBegin(ImGuiLogType_Clipboard, auto_open_depth);
9208 }
9209 
LogToBuffer(int auto_open_depth)9210 void ImGui::LogToBuffer(int auto_open_depth)
9211 {
9212     ImGuiContext& g = *GImGui;
9213     if (g.LogEnabled)
9214         return;
9215     LogBegin(ImGuiLogType_Buffer, auto_open_depth);
9216 }
9217 
LogFinish()9218 void ImGui::LogFinish()
9219 {
9220     ImGuiContext& g = *GImGui;
9221     if (!g.LogEnabled)
9222         return;
9223 
9224     LogText(IM_NEWLINE);
9225     switch (g.LogType)
9226     {
9227     case ImGuiLogType_TTY:
9228 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
9229         fflush(g.LogFile);
9230 #endif
9231         break;
9232     case ImGuiLogType_File:
9233         ImFileClose(g.LogFile);
9234         break;
9235     case ImGuiLogType_Buffer:
9236         break;
9237     case ImGuiLogType_Clipboard:
9238         if (!g.LogBuffer.empty())
9239             SetClipboardText(g.LogBuffer.begin());
9240         break;
9241     case ImGuiLogType_None:
9242         IM_ASSERT(0);
9243         break;
9244     }
9245 
9246     g.LogEnabled = false;
9247     g.LogType = ImGuiLogType_None;
9248     g.LogFile = NULL;
9249     g.LogBuffer.clear();
9250 }
9251 
9252 // Helper to display logging buttons
9253 // FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!)
LogButtons()9254 void ImGui::LogButtons()
9255 {
9256     ImGuiContext& g = *GImGui;
9257 
9258     PushID("LogButtons");
9259 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
9260     const bool log_to_tty = Button("Log To TTY"); SameLine();
9261 #else
9262     const bool log_to_tty = false;
9263 #endif
9264     const bool log_to_file = Button("Log To File"); SameLine();
9265     const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
9266     PushAllowKeyboardFocus(false);
9267     SetNextItemWidth(80.0f);
9268     SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL);
9269     PopAllowKeyboardFocus();
9270     PopID();
9271 
9272     // Start logging at the end of the function so that the buttons don't appear in the log
9273     if (log_to_tty)
9274         LogToTTY();
9275     if (log_to_file)
9276         LogToFile();
9277     if (log_to_clipboard)
9278         LogToClipboard();
9279 }
9280 
9281 //-----------------------------------------------------------------------------
9282 // [SECTION] SETTINGS
9283 //-----------------------------------------------------------------------------
9284 
MarkIniSettingsDirty()9285 void ImGui::MarkIniSettingsDirty()
9286 {
9287     ImGuiContext& g = *GImGui;
9288     if (g.SettingsDirtyTimer <= 0.0f)
9289         g.SettingsDirtyTimer = g.IO.IniSavingRate;
9290 }
9291 
MarkIniSettingsDirty(ImGuiWindow * window)9292 void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
9293 {
9294     ImGuiContext& g = *GImGui;
9295     if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
9296         if (g.SettingsDirtyTimer <= 0.0f)
9297             g.SettingsDirtyTimer = g.IO.IniSavingRate;
9298 }
9299 
CreateNewWindowSettings(const char * name)9300 ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
9301 {
9302     ImGuiContext& g = *GImGui;
9303 
9304 #if !IMGUI_DEBUG_INI_SETTINGS
9305     // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
9306     // Preserve the full string when IMGUI_DEBUG_INI_SETTINGS is set to make .ini inspection easier.
9307     if (const char* p = strstr(name, "###"))
9308         name = p;
9309 #endif
9310     const size_t name_len = strlen(name);
9311 
9312     // Allocate chunk
9313     const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1;
9314     ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size);
9315     IM_PLACEMENT_NEW(settings) ImGuiWindowSettings();
9316     settings->ID = ImHashStr(name, name_len);
9317     memcpy(settings->GetName(), name, name_len + 1);   // Store with zero terminator
9318 
9319     return settings;
9320 }
9321 
FindWindowSettings(ImGuiID id)9322 ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
9323 {
9324     ImGuiContext& g = *GImGui;
9325     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
9326         if (settings->ID == id)
9327             return settings;
9328     return NULL;
9329 }
9330 
FindOrCreateWindowSettings(const char * name)9331 ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name)
9332 {
9333     if (ImGuiWindowSettings* settings = FindWindowSettings(ImHashStr(name)))
9334         return settings;
9335     return CreateNewWindowSettings(name);
9336 }
9337 
LoadIniSettingsFromDisk(const char * ini_filename)9338 void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
9339 {
9340     size_t file_data_size = 0;
9341     char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
9342     if (!file_data)
9343         return;
9344     LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
9345     IM_FREE(file_data);
9346 }
9347 
FindSettingsHandler(const char * type_name)9348 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
9349 {
9350     ImGuiContext& g = *GImGui;
9351     const ImGuiID type_hash = ImHashStr(type_name);
9352     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
9353         if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
9354             return &g.SettingsHandlers[handler_n];
9355     return NULL;
9356 }
9357 
9358 // Zero-tolerance, no error reporting, cheap .ini parsing
LoadIniSettingsFromMemory(const char * ini_data,size_t ini_size)9359 void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
9360 {
9361     ImGuiContext& g = *GImGui;
9362     IM_ASSERT(g.Initialized);
9363     IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
9364 
9365     // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
9366     // 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..
9367     if (ini_size == 0)
9368         ini_size = strlen(ini_data);
9369     char* buf = (char*)IM_ALLOC(ini_size + 1);
9370     char* buf_end = buf + ini_size;
9371     memcpy(buf, ini_data, ini_size);
9372     buf[ini_size] = 0;
9373 
9374     void* entry_data = NULL;
9375     ImGuiSettingsHandler* entry_handler = NULL;
9376 
9377     char* line_end = NULL;
9378     for (char* line = buf; line < buf_end; line = line_end + 1)
9379     {
9380         // Skip new lines markers, then find end of the line
9381         while (*line == '\n' || *line == '\r')
9382             line++;
9383         line_end = line;
9384         while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
9385             line_end++;
9386         line_end[0] = 0;
9387         if (line[0] == ';')
9388             continue;
9389         if (line[0] == '[' && line_end > line && line_end[-1] == ']')
9390         {
9391             // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
9392             line_end[-1] = 0;
9393             const char* name_end = line_end - 1;
9394             const char* type_start = line + 1;
9395             char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']');
9396             const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
9397             if (!type_end || !name_start)
9398                 continue;
9399             *type_end = 0; // Overwrite first ']'
9400             name_start++;  // Skip second '['
9401             entry_handler = FindSettingsHandler(type_start);
9402             entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
9403         }
9404         else if (entry_handler != NULL && entry_data != NULL)
9405         {
9406             // Let type handler parse the line
9407             entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
9408         }
9409     }
9410     IM_FREE(buf);
9411     g.SettingsLoaded = true;
9412 }
9413 
SaveIniSettingsToDisk(const char * ini_filename)9414 void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
9415 {
9416     ImGuiContext& g = *GImGui;
9417     g.SettingsDirtyTimer = 0.0f;
9418     if (!ini_filename)
9419         return;
9420 
9421     size_t ini_data_size = 0;
9422     const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
9423     ImFileHandle f = ImFileOpen(ini_filename, "wt");
9424     if (!f)
9425         return;
9426     ImFileWrite(ini_data, sizeof(char), ini_data_size, f);
9427     ImFileClose(f);
9428 }
9429 
9430 // Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
SaveIniSettingsToMemory(size_t * out_size)9431 const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
9432 {
9433     ImGuiContext& g = *GImGui;
9434     g.SettingsDirtyTimer = 0.0f;
9435     g.SettingsIniData.Buf.resize(0);
9436     g.SettingsIniData.Buf.push_back(0);
9437     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
9438     {
9439         ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
9440         handler->WriteAllFn(&g, handler, &g.SettingsIniData);
9441     }
9442     if (out_size)
9443         *out_size = (size_t)g.SettingsIniData.size();
9444     return g.SettingsIniData.c_str();
9445 }
9446 
WindowSettingsHandler_ReadOpen(ImGuiContext *,ImGuiSettingsHandler *,const char * name)9447 static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
9448 {
9449     ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHashStr(name));
9450     if (!settings)
9451         settings = ImGui::CreateNewWindowSettings(name);
9452     return (void*)settings;
9453 }
9454 
WindowSettingsHandler_ReadLine(ImGuiContext *,ImGuiSettingsHandler *,void * entry,const char * line)9455 static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
9456 {
9457     ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
9458     int x, y;
9459     int i;
9460     if (sscanf(line, "Pos=%i,%i", &x, &y) == 2)         settings->Pos = ImVec2ih((short)x, (short)y);
9461     else if (sscanf(line, "Size=%i,%i", &x, &y) == 2)   settings->Size = ImVec2ih((short)x, (short)y);
9462     else if (sscanf(line, "Collapsed=%d", &i) == 1)     settings->Collapsed = (i != 0);
9463 }
9464 
WindowSettingsHandler_WriteAll(ImGuiContext * ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)9465 static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
9466 {
9467     // Gather data from windows that were active during this session
9468     // (if a window wasn't opened in this session we preserve its settings)
9469     ImGuiContext& g = *ctx;
9470     for (int i = 0; i != g.Windows.Size; i++)
9471     {
9472         ImGuiWindow* window = g.Windows[i];
9473         if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
9474             continue;
9475 
9476         ImGuiWindowSettings* settings = (window->SettingsOffset != -1) ? g.SettingsWindows.ptr_from_offset(window->SettingsOffset) : ImGui::FindWindowSettings(window->ID);
9477         if (!settings)
9478         {
9479             settings = ImGui::CreateNewWindowSettings(window->Name);
9480             window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
9481         }
9482         IM_ASSERT(settings->ID == window->ID);
9483         settings->Pos = ImVec2ih((short)window->Pos.x, (short)window->Pos.y);
9484         settings->Size = ImVec2ih((short)window->SizeFull.x, (short)window->SizeFull.y);
9485         settings->Collapsed = window->Collapsed;
9486     }
9487 
9488     // Write to text buffer
9489     buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve
9490     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
9491     {
9492         const char* settings_name = settings->GetName();
9493         buf->appendf("[%s][%s]\n", handler->TypeName, settings_name);
9494         buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
9495         buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
9496         buf->appendf("Collapsed=%d\n", settings->Collapsed);
9497         buf->append("\n");
9498     }
9499 }
9500 
9501 
9502 //-----------------------------------------------------------------------------
9503 // [SECTION] VIEWPORTS, PLATFORM WINDOWS
9504 //-----------------------------------------------------------------------------
9505 
9506 // (this section is filled in the 'docking' branch)
9507 
9508 
9509 //-----------------------------------------------------------------------------
9510 // [SECTION] DOCKING
9511 //-----------------------------------------------------------------------------
9512 
9513 // (this section is filled in the 'docking' branch)
9514 
9515 
9516 //-----------------------------------------------------------------------------
9517 // [SECTION] PLATFORM DEPENDENT HELPERS
9518 //-----------------------------------------------------------------------------
9519 
9520 #if defined(_WIN32) && !defined(_WINDOWS_) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS))
9521 #ifndef WIN32_LEAN_AND_MEAN
9522 #define WIN32_LEAN_AND_MEAN
9523 #endif
9524 #ifndef __MINGW32__
9525 #include <Windows.h>
9526 #else
9527 #include <windows.h>
9528 #endif
9529 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) // UWP doesn't have Win32 functions
9530 #define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
9531 #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
9532 #endif
9533 #elif defined(__APPLE__)
9534 #include <TargetConditionals.h>
9535 #endif
9536 
9537 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
9538 
9539 #ifdef _MSC_VER
9540 #pragma comment(lib, "user32")
9541 #endif
9542 
9543 // Win32 clipboard implementation
GetClipboardTextFn_DefaultImpl(void *)9544 static const char* GetClipboardTextFn_DefaultImpl(void*)
9545 {
9546     static ImVector<char> buf_local;
9547     buf_local.clear();
9548     if (!::OpenClipboard(NULL))
9549         return NULL;
9550     HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
9551     if (wbuf_handle == NULL)
9552     {
9553         ::CloseClipboard();
9554         return NULL;
9555     }
9556     if (ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle))
9557     {
9558         int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
9559         buf_local.resize(buf_len);
9560         ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL);
9561     }
9562     ::GlobalUnlock(wbuf_handle);
9563     ::CloseClipboard();
9564     return buf_local.Data;
9565 }
9566 
SetClipboardTextFn_DefaultImpl(void *,const char * text)9567 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9568 {
9569     if (!::OpenClipboard(NULL))
9570         return;
9571     const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
9572     HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
9573     if (wbuf_handle == NULL)
9574     {
9575         ::CloseClipboard();
9576         return;
9577     }
9578     ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle);
9579     ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
9580     ::GlobalUnlock(wbuf_handle);
9581     ::EmptyClipboard();
9582     if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
9583         ::GlobalFree(wbuf_handle);
9584     ::CloseClipboard();
9585 }
9586 
9587 #elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS)
9588 
9589 #include <Carbon/Carbon.h>  // Use old API to avoid need for separate .mm file
9590 static PasteboardRef main_clipboard = 0;
9591 
9592 // OSX clipboard implementation
9593 // If you enable this you will need to add '-framework ApplicationServices' to your linker command-line!
SetClipboardTextFn_DefaultImpl(void *,const char * text)9594 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9595 {
9596     if (!main_clipboard)
9597         PasteboardCreate(kPasteboardClipboard, &main_clipboard);
9598     PasteboardClear(main_clipboard);
9599     CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text));
9600     if (cf_data)
9601     {
9602         PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0);
9603         CFRelease(cf_data);
9604     }
9605 }
9606 
GetClipboardTextFn_DefaultImpl(void *)9607 static const char* GetClipboardTextFn_DefaultImpl(void*)
9608 {
9609     if (!main_clipboard)
9610         PasteboardCreate(kPasteboardClipboard, &main_clipboard);
9611     PasteboardSynchronize(main_clipboard);
9612 
9613     ItemCount item_count = 0;
9614     PasteboardGetItemCount(main_clipboard, &item_count);
9615     for (ItemCount i = 0; i < item_count; i++)
9616     {
9617         PasteboardItemID item_id = 0;
9618         PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id);
9619         CFArrayRef flavor_type_array = 0;
9620         PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array);
9621         for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++)
9622         {
9623             CFDataRef cf_data;
9624             if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr)
9625             {
9626                 static ImVector<char> clipboard_text;
9627                 int length = (int)CFDataGetLength(cf_data);
9628                 clipboard_text.resize(length + 1);
9629                 CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)clipboard_text.Data);
9630                 clipboard_text[length] = 0;
9631                 CFRelease(cf_data);
9632                 return clipboard_text.Data;
9633             }
9634         }
9635     }
9636     return NULL;
9637 }
9638 
9639 #else
9640 
9641 // Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers.
GetClipboardTextFn_DefaultImpl(void *)9642 static const char* GetClipboardTextFn_DefaultImpl(void*)
9643 {
9644     ImGuiContext& g = *GImGui;
9645     return g.PrivateClipboard.empty() ? NULL : g.PrivateClipboard.begin();
9646 }
9647 
SetClipboardTextFn_DefaultImpl(void *,const char * text)9648 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9649 {
9650     ImGuiContext& g = *GImGui;
9651     g.PrivateClipboard.clear();
9652     const char* text_end = text + strlen(text);
9653     g.PrivateClipboard.resize((int)(text_end - text) + 1);
9654     memcpy(&g.PrivateClipboard[0], text, (size_t)(text_end - text));
9655     g.PrivateClipboard[(int)(text_end - text)] = 0;
9656 }
9657 
9658 #endif
9659 
9660 // Win32 API IME support (for Asian languages, etc.)
9661 #if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
9662 
9663 #include <imm.h>
9664 #ifdef _MSC_VER
9665 #pragma comment(lib, "imm32")
9666 #endif
9667 
ImeSetInputScreenPosFn_DefaultImpl(int x,int y)9668 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
9669 {
9670     // Notify OS Input Method Editor of text input position
9671     ImGuiIO& io = ImGui::GetIO();
9672     if (HWND hwnd = (HWND)io.ImeWindowHandle)
9673         if (HIMC himc = ::ImmGetContext(hwnd))
9674         {
9675             COMPOSITIONFORM cf;
9676             cf.ptCurrentPos.x = x;
9677             cf.ptCurrentPos.y = y;
9678             cf.dwStyle = CFS_FORCE_POSITION;
9679             ::ImmSetCompositionWindow(himc, &cf);
9680             ::ImmReleaseContext(hwnd, himc);
9681         }
9682 }
9683 
9684 #else
9685 
ImeSetInputScreenPosFn_DefaultImpl(int,int)9686 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
9687 
9688 #endif
9689 
9690 //-----------------------------------------------------------------------------
9691 // [SECTION] METRICS/DEBUG WINDOW
9692 //-----------------------------------------------------------------------------
9693 
9694 #ifndef IMGUI_DISABLE_METRICS_WINDOW
9695 // Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds.
MetricsHelpMarker(const char * desc)9696 static void MetricsHelpMarker(const char* desc)
9697 {
9698     ImGui::TextDisabled("(?)");
9699     if (ImGui::IsItemHovered())
9700     {
9701         ImGui::BeginTooltip();
9702         ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
9703         ImGui::TextUnformatted(desc);
9704         ImGui::PopTextWrapPos();
9705         ImGui::EndTooltip();
9706     }
9707 }
9708 
ShowMetricsWindow(bool * p_open)9709 void ImGui::ShowMetricsWindow(bool* p_open)
9710 {
9711     if (!ImGui::Begin("Dear ImGui Metrics", p_open))
9712     {
9713         ImGui::End();
9714         return;
9715     }
9716 
9717     // State
9718     enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type
9719     const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentRegionRect" };
9720     static bool show_windows_rects = false;
9721     static int  show_windows_rect_type = WRT_WorkRect;
9722     static bool show_windows_begin_order = false;
9723     static bool show_drawcmd_details = true;
9724 
9725     // Basic info
9726     ImGuiContext& g = *GImGui;
9727     ImGuiIO& io = ImGui::GetIO();
9728     ImGui::Text("Dear ImGui %s", ImGui::GetVersion());
9729     ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
9730     ImGui::Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
9731     ImGui::Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
9732     ImGui::Text("%d active allocations", io.MetricsActiveAllocations);
9733     ImGui::Separator();
9734 
9735     // Helper functions to display common structures:
9736     // - NodeDrawList
9737     // - NodeColumns
9738     // - NodeWindow
9739     // - NodeWindows
9740     // - NodeTabBar
9741     struct Funcs
9742     {
9743         static ImRect GetWindowRect(ImGuiWindow* window, int rect_type)
9744         {
9745             if (rect_type == WRT_OuterRect)                 { return window->Rect(); }
9746             else if (rect_type == WRT_OuterRectClipped)     { return window->OuterRectClipped; }
9747             else if (rect_type == WRT_InnerRect)            { return window->InnerRect; }
9748             else if (rect_type == WRT_InnerClipRect)        { return window->InnerClipRect; }
9749             else if (rect_type == WRT_WorkRect)             { return window->WorkRect; }
9750             else if (rect_type == WRT_Content)              { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
9751             else if (rect_type == WRT_ContentRegionRect)    { return window->ContentRegionRect; }
9752             IM_ASSERT(0);
9753             return ImRect();
9754         }
9755 
9756         static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label)
9757         {
9758             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);
9759             if (draw_list == ImGui::GetWindowDrawList())
9760             {
9761                 ImGui::SameLine();
9762                 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)
9763                 if (node_open) ImGui::TreePop();
9764                 return;
9765             }
9766 
9767             ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list
9768             if (window && IsItemHovered())
9769                 fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
9770             if (!node_open)
9771                 return;
9772 
9773             if (window && !window->WasActive)
9774                 ImGui::TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!");
9775 
9776             unsigned int elem_offset = 0;
9777             for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
9778             {
9779                 if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0)
9780                     continue;
9781                 if (pcmd->UserCallback)
9782                 {
9783                     ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
9784                     continue;
9785                 }
9786 
9787                 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
9788                 char buf[300];
9789                 ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd: %4d triangles, Tex 0x%p, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
9790                     pcmd->ElemCount/3, (void*)(intptr_t)pcmd->TextureId,
9791                     pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
9792                 bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf);
9793                 if (show_drawcmd_details && fg_draw_list && ImGui::IsItemHovered())
9794                 {
9795                     ImRect clip_rect = pcmd->ClipRect;
9796                     ImRect vtxs_rect;
9797                     for (unsigned int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++)
9798                         vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos);
9799                     fg_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255,0,255,255));
9800                     fg_draw_list->AddRect(ImFloor(vtxs_rect.Min), ImFloor(vtxs_rect.Max), IM_COL32(255,255,0,255));
9801                 }
9802                 if (!pcmd_node_open)
9803                     continue;
9804 
9805                 // Calculate approximate coverage area (touched pixel count)
9806                 // This will be in pixels squared as long there's no post-scaling happening to the renderer output.
9807                 float total_area = 0.0f;
9808                 for (unsigned int base_idx = elem_offset; base_idx < (elem_offset + pcmd->ElemCount); base_idx += 3)
9809                 {
9810                     ImVec2 triangle[3];
9811                     for (int n = 0; n < 3; n++)
9812                         triangle[n] = draw_list->VtxBuffer[idx_buffer ? idx_buffer[base_idx + n] : (base_idx + n)].pos;
9813                     total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]);
9814                 }
9815 
9816                 // Display vertex information summary. Hover to get all triangles drawn in wire-frame
9817                 ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area);
9818                 ImGui::Selectable(buf);
9819                 if (fg_draw_list && ImGui::IsItemHovered() && show_drawcmd_details)
9820                 {
9821                     // Draw wire-frame version of everything
9822                     ImDrawListFlags backup_flags = fg_draw_list->Flags;
9823                     fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
9824                     ImRect clip_rect = pcmd->ClipRect;
9825                     fg_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255, 0, 255, 255));
9826                     for (unsigned int base_idx = elem_offset; base_idx < (elem_offset + pcmd->ElemCount); base_idx += 3)
9827                     {
9828                         ImVec2 triangle[3];
9829                         for (int n = 0; n < 3; n++)
9830                             triangle[n] = draw_list->VtxBuffer[idx_buffer ? idx_buffer[base_idx + n] : (base_idx + n)].pos;
9831                         fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), true, 1.0f);
9832                     }
9833                     fg_draw_list->Flags = backup_flags;
9834                 }
9835 
9836                 // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
9837                 ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
9838                 while (clipper.Step())
9839                     for (int prim = clipper.DisplayStart, idx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++)
9840                     {
9841                         char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf);
9842                         ImVec2 triangle[3];
9843                         for (int n = 0; n < 3; n++, idx_i++)
9844                         {
9845                             ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[idx_i] : idx_i];
9846                             triangle[n] = v.pos;
9847                             buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n",
9848                                 (n == 0) ? "Vert:" : "     ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
9849                         }
9850 
9851                         ImGui::Selectable(buf, false);
9852                         if (fg_draw_list && ImGui::IsItemHovered())
9853                         {
9854                             ImDrawListFlags backup_flags = fg_draw_list->Flags;
9855                             fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
9856                             fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255,255,0,255), true, 1.0f);
9857                             fg_draw_list->Flags = backup_flags;
9858                         }
9859                     }
9860                 ImGui::TreePop();
9861             }
9862             ImGui::TreePop();
9863         }
9864 
9865         static void NodeColumns(const ImGuiColumns* columns)
9866         {
9867             if (!ImGui::TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
9868                 return;
9869             ImGui::BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX);
9870             for (int column_n = 0; column_n < columns->Columns.Size; column_n++)
9871                 ImGui::BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, GetColumnOffsetFromNorm(columns, columns->Columns[column_n].OffsetNorm));
9872             ImGui::TreePop();
9873         }
9874 
9875         static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label)
9876         {
9877             if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size))
9878                 return;
9879             for (int i = 0; i < windows.Size; i++)
9880                 Funcs::NodeWindow(windows[i], "Window");
9881             ImGui::TreePop();
9882         }
9883 
9884         static void NodeWindow(ImGuiWindow* window, const char* label)
9885         {
9886             if (window == NULL)
9887             {
9888                 ImGui::BulletText("%s: NULL", label);
9889                 return;
9890             }
9891             bool open = ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, (window->Active || window->WasActive), window);
9892             if (ImGui::IsItemHovered() && window->WasActive)
9893                 ImGui::GetForegroundDrawList()->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
9894             if (!open)
9895                 return;
9896             ImGuiWindowFlags flags = window->Flags;
9897             NodeDrawList(window, window->DrawList, "DrawList");
9898             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);
9899             ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
9900                 (flags & ImGuiWindowFlags_ChildWindow)  ? "Child " : "",      (flags & ImGuiWindowFlags_Tooltip)     ? "Tooltip "   : "",  (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
9901                 (flags & ImGuiWindowFlags_Modal)        ? "Modal " : "",      (flags & ImGuiWindowFlags_ChildMenu)   ? "ChildMenu " : "",  (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
9902                 (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
9903             ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y, window->ScrollbarX ? "X" : "", window->ScrollbarY ? "Y" : "");
9904             ImGui::BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
9905             ImGui::BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems);
9906             ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask);
9907             ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
9908             if (!window->NavRectRel[0].IsInverted())
9909                 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);
9910             else
9911                 ImGui::BulletText("NavRectRel[0]: <None>");
9912             if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
9913             if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow");
9914             if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
9915             if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
9916             {
9917                 for (int n = 0; n < window->ColumnsStorage.Size; n++)
9918                     NodeColumns(&window->ColumnsStorage[n]);
9919                 ImGui::TreePop();
9920             }
9921             NodeStorage(&window->StateStorage, "Storage");
9922             ImGui::TreePop();
9923         }
9924 
9925         static void NodeTabBar(ImGuiTabBar* tab_bar)
9926         {
9927             // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
9928             char buf[256];
9929             char* p = buf;
9930             const char* buf_end = buf + IM_ARRAYSIZE(buf);
9931             ImFormatString(p, buf_end - p, "TabBar (%d tabs)%s", tab_bar->Tabs.Size, (tab_bar->PrevFrameVisible < ImGui::GetFrameCount() - 2) ? " *Inactive*" : "");
9932             if (ImGui::TreeNode(tab_bar, "%s", buf))
9933             {
9934                 for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
9935                 {
9936                     const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
9937                     ImGui::PushID(tab);
9938                     if (ImGui::SmallButton("<")) { TabBarQueueChangeTabOrder(tab_bar, tab, -1); } ImGui::SameLine(0, 2);
9939                     if (ImGui::SmallButton(">")) { TabBarQueueChangeTabOrder(tab_bar, tab, +1); } ImGui::SameLine();
9940                     ImGui::Text("%02d%c Tab 0x%08X '%s'", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "");
9941                     ImGui::PopID();
9942                 }
9943                 ImGui::TreePop();
9944             }
9945         }
9946 
9947         static void NodeStorage(ImGuiStorage* storage, const char* label)
9948         {
9949             if (!ImGui::TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes()))
9950                 return;
9951             for (int n = 0; n < storage->Data.Size; n++)
9952             {
9953                 const ImGuiStorage::ImGuiStoragePair& p = storage->Data[n];
9954                 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.
9955             }
9956             ImGui::TreePop();
9957         }
9958     };
9959 
9960     Funcs::NodeWindows(g.Windows, "Windows");
9961     if (ImGui::TreeNode("DrawLists", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size))
9962     {
9963         for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++)
9964             Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList");
9965         ImGui::TreePop();
9966     }
9967 
9968     if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
9969     {
9970         for (int i = 0; i < g.OpenPopupStack.Size; i++)
9971         {
9972             ImGuiWindow* window = g.OpenPopupStack[i].Window;
9973             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" : "");
9974         }
9975         ImGui::TreePop();
9976     }
9977 
9978     if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetSize()))
9979     {
9980         for (int n = 0; n < g.TabBars.GetSize(); n++)
9981             Funcs::NodeTabBar(g.TabBars.GetByIndex(n));
9982         ImGui::TreePop();
9983     }
9984 
9985 #if 0
9986     if (ImGui::TreeNode("Docking"))
9987     {
9988         ImGui::TreePop();
9989     }
9990 #endif
9991 
9992 #if 0
9993     if (ImGui::TreeNode("Tables", "Tables (%d)", g.Tables.GetSize()))
9994     {
9995         ImGui::TreePop();
9996     }
9997 #endif
9998 
9999     if (ImGui::TreeNode("Internal state"))
10000     {
10001         const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
10002         ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
10003         ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
10004         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
10005         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]);
10006         ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
10007         ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
10008         ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
10009         ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
10010         ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
10011         ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
10012         ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
10013         ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
10014         ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
10015         ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
10016         ImGui::TreePop();
10017     }
10018 
10019     if (ImGui::TreeNode("Tools"))
10020     {
10021         // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
10022         if (ImGui::Button("Item Picker.."))
10023             ImGui::DebugStartItemPicker();
10024         ImGui::SameLine();
10025         MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash.");
10026 
10027         ImGui::Checkbox("Show windows begin order", &show_windows_begin_order);
10028         ImGui::Checkbox("Show windows rectangles", &show_windows_rects);
10029         ImGui::SameLine();
10030         ImGui::SetNextItemWidth(ImGui::GetFontSize() * 12);
10031         show_windows_rects |= ImGui::Combo("##show_windows_rect_type", &show_windows_rect_type, wrt_rects_names, WRT_Count);
10032         if (show_windows_rects && g.NavWindow)
10033         {
10034             ImGui::BulletText("'%s':", g.NavWindow->Name);
10035             ImGui::Indent();
10036             for (int rect_n = 0; rect_n < WRT_Count; rect_n++)
10037             {
10038                 ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n);
10039                 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]);
10040             }
10041             ImGui::Unindent();
10042         }
10043         ImGui::Checkbox("Show details when hovering ImDrawCmd node", &show_drawcmd_details);
10044         ImGui::TreePop();
10045     }
10046 
10047     // Tool: Display windows Rectangles and Begin Order
10048     if (show_windows_rects || show_windows_begin_order)
10049     {
10050         for (int n = 0; n < g.Windows.Size; n++)
10051         {
10052             ImGuiWindow* window = g.Windows[n];
10053             if (!window->WasActive)
10054                 continue;
10055             ImDrawList* draw_list = GetForegroundDrawList(window);
10056             if (show_windows_rects)
10057             {
10058                 ImRect r = Funcs::GetWindowRect(window, show_windows_rect_type);
10059                 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
10060             }
10061             if (show_windows_begin_order && !(window->Flags & ImGuiWindowFlags_ChildWindow))
10062             {
10063                 char buf[32];
10064                 ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
10065                 float font_size = ImGui::GetFontSize();
10066                 draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
10067                 draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf);
10068             }
10069         }
10070     }
10071     ImGui::End();
10072 }
10073 
10074 #else
10075 
ShowMetricsWindow(bool *)10076 void ImGui::ShowMetricsWindow(bool*) { }
10077 
10078 #endif
10079 
10080 //-----------------------------------------------------------------------------
10081 
10082 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
10083 // 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.
10084 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
10085 #include "imgui_user.inl"
10086 #endif
10087 
10088 //-----------------------------------------------------------------------------
10089