1 // dear imgui, v1.85 WIP
2 // (main code and documentation)
3 
4 // Help:
5 // - Read FAQ at http://dearimgui.org/faq
6 // - Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase.
7 // - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
8 // Read imgui.cpp for details, links and comments.
9 
10 // Resources:
11 // - FAQ                   http://dearimgui.org/faq
12 // - Homepage & latest     https://github.com/ocornut/imgui
13 // - Releases & changelog  https://github.com/ocornut/imgui/releases
14 // - Gallery               https://github.com/ocornut/imgui/issues/4451 (please post your screenshots/video there!)
15 // - Wiki                  https://github.com/ocornut/imgui/wiki (lots of good stuff there)
16 // - Glossary              https://github.com/ocornut/imgui/wiki/Glossary
17 // - Issues & support      https://github.com/ocornut/imgui/issues
18 
19 // Getting Started?
20 // - For first-time users having issues compiling/linking/running or issues loading fonts:
21 //   please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above.
22 
23 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
24 // See LICENSE.txt for copyright and licensing details (standard MIT License).
25 // This library is free but needs your support to sustain development and maintenance.
26 // Businesses: you can support continued development via invoiced technical support, maintenance and sponsoring contracts. Please reach out to "contact AT dearimgui.com".
27 // Individuals: you can support continued development via donations. See docs/README or web page.
28 
29 // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
30 // Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
31 // modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
32 // come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
33 // to a better solution or official support for them.
34 
35 /*
36 
37 Index of this file:
38 
39 DOCUMENTATION
40 
41 - MISSION STATEMENT
42 - END-USER GUIDE
43 - PROGRAMMER GUIDE
44   - READ FIRST
45   - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
46   - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
47   - HOW A SIMPLE APPLICATION MAY LOOK LIKE
48   - HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
49   - USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
50 - API BREAKING CHANGES (read me when you update!)
51 - FREQUENTLY ASKED QUESTIONS (FAQ)
52   - Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer)
53 
54 CODE
55 (search for "[SECTION]" in the code to find them)
56 
57 // [SECTION] INCLUDES
58 // [SECTION] FORWARD DECLARATIONS
59 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
60 // [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
61 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
62 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
63 // [SECTION] MISC HELPERS/UTILITIES (File functions)
64 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
65 // [SECTION] MISC HELPERS/UTILITIES (Color functions)
66 // [SECTION] ImGuiStorage
67 // [SECTION] ImGuiTextFilter
68 // [SECTION] ImGuiTextBuffer
69 // [SECTION] ImGuiListClipper
70 // [SECTION] STYLING
71 // [SECTION] RENDER HELPERS
72 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
73 // [SECTION] ERROR CHECKING
74 // [SECTION] LAYOUT
75 // [SECTION] SCROLLING
76 // [SECTION] TOOLTIPS
77 // [SECTION] POPUPS
78 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
79 // [SECTION] DRAG AND DROP
80 // [SECTION] LOGGING/CAPTURING
81 // [SECTION] SETTINGS
82 // [SECTION] VIEWPORTS
83 // [SECTION] PLATFORM DEPENDENT HELPERS
84 // [SECTION] METRICS/DEBUGGER WINDOW
85 
86 */
87 
88 //-----------------------------------------------------------------------------
89 // DOCUMENTATION
90 //-----------------------------------------------------------------------------
91 
92 /*
93 
94  MISSION STATEMENT
95  =================
96 
97  - Easy to use to create code-driven and data-driven tools.
98  - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
99  - Easy to hack and improve.
100  - Minimize setup and maintenance.
101  - Minimize state storage on user side.
102  - Portable, minimize dependencies, run on target (consoles, phones, etc.).
103  - Efficient runtime and memory consumption.
104 
105  Designed for developers and content-creators, not the typical end-user! Some of the current weaknesses includes:
106 
107  - Doesn't look fancy, doesn't animate.
108  - Limited layout features, intricate layouts are typically crafted in code.
109 
110 
111  END-USER GUIDE
112  ==============
113 
114  - Double-click on title bar to collapse window.
115  - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin().
116  - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents).
117  - Click and drag on any empty space to move window.
118  - TAB/SHIFT+TAB to cycle through keyboard editable fields.
119  - CTRL+Click on a slider or drag box to input value as text.
120  - Use mouse wheel to scroll.
121  - Text editor:
122    - Hold SHIFT or use mouse to select text.
123    - CTRL+Left/Right to word jump.
124    - CTRL+Shift+Left/Right to select words.
125    - CTRL+A our Double-Click to select all.
126    - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
127    - CTRL+Z,CTRL+Y to undo/redo.
128    - ESCAPE to revert text to its original value.
129    - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
130    - Controls are automatically adjusted for OSX to match standard OSX text editing operations.
131  - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard.
132  - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://dearimgui.org/controls_sheets
133 
134 
135  PROGRAMMER GUIDE
136  ================
137 
138  READ FIRST
139  ----------
140  - Remember to check the wonderful Wiki (https://github.com/ocornut/imgui/wiki)
141  - 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 or
142    destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, fewer bugs.
143  - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
144  - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
145  - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
146    You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in Wiki.
147  - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
148    For every application frame, your UI code will be called only once. This is in contrast to e.g. Unity's implementation of an IMGUI,
149    where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
150  - Our origin is on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
151  - This codebase is also optimized to yield decent performances with typical "Debug" builds settings.
152  - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).
153    If you get an assert, read the messages and comments around the assert.
154  - 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.
155  - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
156    See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
157    However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase.
158  - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!).
159 
160 
161  HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
162  ----------------------------------------------
163  - Overwrite all the sources files except for imconfig.h (if you have modified your copy of imconfig.h)
164  - Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over "master".
165  - You can also use '#define IMGUI_USER_CONFIG "my_config_file.h" to redirect configuration to your own file.
166  - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
167    If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
168    from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
169    likely be a comment about it. Please report any issue to the GitHub page!
170  - To find out usage of old API, you can add '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in your configuration file.
171  - Try to keep your copy of Dear ImGui reasonably up to date.
172 
173 
174  GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
175  ---------------------------------------------------------------
176  - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
177  - In the majority of cases you should be able to use unmodified backends files available in the backends/ folder.
178  - Add the Dear ImGui source files + selected backend source files to your projects or using your preferred build system.
179    It is recommended you build and statically link the .cpp files as part of your project and NOT as a shared library (DLL).
180  - 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.
181  - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
182  - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
183    Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
184    phases of your own application. All rendering information is stored into command-lists that you will retrieve after calling ImGui::Render().
185  - Refer to the backends and demo applications in the examples/ folder for instruction on how to setup your code.
186  - 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.
187 
188 
189  HOW A SIMPLE APPLICATION MAY LOOK LIKE
190  --------------------------------------
191  EXHIBIT 1: USING THE EXAMPLE BACKENDS (= imgui_impl_XXX.cpp files from the backends/ folder).
192  The sub-folders in examples/ contain examples applications following this structure.
193 
194      // Application init: create a dear imgui context, setup some options, load fonts
195      ImGui::CreateContext();
196      ImGuiIO& io = ImGui::GetIO();
197      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
198      // TODO: Fill optional fields of the io structure later.
199      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
200 
201      // Initialize helper Platform and Renderer backends (here we are using imgui_impl_win32.cpp and imgui_impl_dx11.cpp)
202      ImGui_ImplWin32_Init(hwnd);
203      ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
204 
205      // Application main loop
206      while (true)
207      {
208          // Feed inputs to dear imgui, start new frame
209          ImGui_ImplDX11_NewFrame();
210          ImGui_ImplWin32_NewFrame();
211          ImGui::NewFrame();
212 
213          // Any application code here
214          ImGui::Text("Hello, world!");
215 
216          // Render dear imgui into screen
217          ImGui::Render();
218          ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
219          g_pSwapChain->Present(1, 0);
220      }
221 
222      // Shutdown
223      ImGui_ImplDX11_Shutdown();
224      ImGui_ImplWin32_Shutdown();
225      ImGui::DestroyContext();
226 
227  EXHIBIT 2: IMPLEMENTING CUSTOM BACKEND / CUSTOM ENGINE
228 
229      // Application init: create a dear imgui context, setup some options, load fonts
230      ImGui::CreateContext();
231      ImGuiIO& io = ImGui::GetIO();
232      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
233      // TODO: Fill optional fields of the io structure later.
234      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
235 
236      // Build and load the texture atlas into a texture
237      // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
238      int width, height;
239      unsigned char* pixels = NULL;
240      io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
241 
242      // At this point you've got the texture data and you need to upload that to your graphic system:
243      // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
244      // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID.
245      MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
246      io.Fonts->SetTexID((void*)texture);
247 
248      // Application main loop
249      while (true)
250      {
251         // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
252         // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform Backends)
253         io.DeltaTime = 1.0f/60.0f;              // set the time elapsed since the previous frame (in seconds)
254         io.DisplaySize.x = 1920.0f;             // set the current display width
255         io.DisplaySize.y = 1280.0f;             // set the current display height here
256         io.MousePos = my_mouse_pos;             // set the mouse position
257         io.MouseDown[0] = my_mouse_buttons[0];  // set the mouse button states
258         io.MouseDown[1] = my_mouse_buttons[1];
259 
260         // Call NewFrame(), after this point you can use ImGui::* functions anytime
261         // (So you want to try calling NewFrame() as early as you can in your main loop to be able to use Dear ImGui everywhere)
262         ImGui::NewFrame();
263 
264         // Most of your application code here
265         ImGui::Text("Hello, world!");
266         MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
267         MyGameRender(); // may use any Dear ImGui functions as well!
268 
269         // Render dear imgui, swap buffers
270         // (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)
271         ImGui::EndFrame();
272         ImGui::Render();
273         ImDrawData* draw_data = ImGui::GetDrawData();
274         MyImGuiRenderFunction(draw_data);
275         SwapBuffers();
276      }
277 
278      // Shutdown
279      ImGui::DestroyContext();
280 
281  To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest of your application,
282  you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
283  Please read the FAQ and example applications for details about this!
284 
285 
286  HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
287  ---------------------------------------------
288  The backends in impl_impl_XXX.cpp files contain many working implementations of a rendering function.
289 
290     void void MyImGuiRenderFunction(ImDrawData* draw_data)
291     {
292        // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
293        // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
294        // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
295        // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
296        for (int n = 0; n < draw_data->CmdListsCount; n++)
297        {
298           const ImDrawList* cmd_list = draw_data->CmdLists[n];
299           const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;  // vertex buffer generated by Dear ImGui
300           const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;   // index buffer generated by Dear ImGui
301           for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
302           {
303              const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
304              if (pcmd->UserCallback)
305              {
306                  pcmd->UserCallback(cmd_list, pcmd);
307              }
308              else
309              {
310                  // The texture for the draw call is specified by pcmd->GetTexID().
311                  // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization.
312                  MyEngineBindTexture((MyTexture*)pcmd->GetTexID());
313 
314                  // We are using scissoring to clip some objects. All low-level graphics API should support it.
315                  // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
316                  //   (some elements visible outside their bounds) but you can fix that once everything else works!
317                  // - Clipping coordinates are provided in imgui coordinates space:
318                  //   - For a given viewport, draw_data->DisplayPos == viewport->Pos and draw_data->DisplaySize == viewport->Size
319                  //   - In a single viewport application, draw_data->DisplayPos == (0,0) and draw_data->DisplaySize == io.DisplaySize, but always use GetMainViewport()->Pos/Size instead of hardcoding those values.
320                  //   - In the interest of supporting multi-viewport applications (see 'docking' branch on github),
321                  //     always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
322                  // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
323                  ImVec2 pos = draw_data->DisplayPos;
324                  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));
325 
326                  // Render 'pcmd->ElemCount/3' indexed triangles.
327                  // 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.
328                  MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
329              }
330              idx_buffer += pcmd->ElemCount;
331           }
332        }
333     }
334 
335 
336  USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
337  ------------------------------------------
338  - The gamepad/keyboard navigation is fairly functional and keeps being improved.
339  - Gamepad support is particularly useful to use Dear ImGui on a console system (e.g. PS4, Switch, XB1) without a mouse!
340  - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787
341  - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
342  - Keyboard:
343     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
344       NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
345     - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
346       will be set. For more advanced uses, you may want to read from:
347        - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
348        - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
349        - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions.
350       Please reach out if you think the game vs navigation input sharing could be improved.
351  - Gamepad:
352     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
353     - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
354       Note that io.NavInputs[] is cleared by EndFrame().
355     - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
356          0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
357     - We use a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
358       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.).
359     - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://dearimgui.org/controls_sheets
360     - 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
361       to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
362  - Mouse:
363     - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
364     - 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.
365     - 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.
366       Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
367       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.
368       When that happens your backend NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the backends in examples/ do that.
369       (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, imgui will misbehave as it will see your mouse moving back and forth!)
370       (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
371        to set a boolean to ignore your other external mouse positions until the external source is moved again.)
372 
373 
374  API BREAKING CHANGES
375  ====================
376 
377  Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
378  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.
379  When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
380  You can read releases logs https://github.com/ocornut/imgui/releases for more details.
381 
382  - 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead.
383  - 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019):
384                         - ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList()
385                         - ImFont::GlyphRangesBuilder  -> use ImFontGlyphRangesBuilder
386  - 2021/05/19 (1.83) - backends: obsoleted direct access to ImDrawCmd::TextureId in favor of calling ImDrawCmd::GetTexID().
387                         - if you are using official backends from the source tree: you have nothing to do.
388                         - if you have copied old backend code or using your own: change access to draw_cmd->TextureId to draw_cmd->GetTexID().
389  - 2021/03/12 (1.82) - upgraded ImDrawList::AddRect(), AddRectFilled(), PathRect() to use ImDrawFlags instead of ImDrawCornersFlags.
390                         - ImDrawCornerFlags_TopLeft  -> use ImDrawFlags_RoundCornersTopLeft
391                         - ImDrawCornerFlags_BotRight -> use ImDrawFlags_RoundCornersBottomRight
392                         - ImDrawCornerFlags_None     -> use ImDrawFlags_RoundCornersNone etc.
393                        flags now sanely defaults to 0 instead of 0x0F, consistent with all other flags in the API.
394                        breaking: the default with rounding > 0.0f is now "round all corners" vs old implicit "round no corners":
395                         - rounding == 0.0f + flags == 0 --> meant no rounding  --> unchanged (common use)
396                         - rounding  > 0.0f + flags != 0 --> meant rounding     --> unchanged (common use)
397                         - rounding == 0.0f + flags != 0 --> meant no rounding  --> unchanged (unlikely use)
398                         - rounding  > 0.0f + flags == 0 --> meant no rounding  --> BREAKING (unlikely use): will now round all corners --> use ImDrawFlags_RoundCornersNone or rounding == 0.0f.
399                        this ONLY matters for hard coded use of 0 + rounding > 0.0f. Use of named ImDrawFlags_RoundCornersNone (new) or ImDrawCornerFlags_None (old) are ok.
400                        the old ImDrawCornersFlags used awkward default values of ~0 or 0xF (4 lower bits set) to signify "round all corners" and we sometimes encouraged using them as shortcuts.
401                        legacy path still support use of hard coded ~0 or any value from 0x1 or 0xF. They will behave the same with legacy paths enabled (will assert otherwise).
402  - 2021/03/11 (1.82) - removed redirecting functions/enums names that were marked obsolete in 1.66 (September 2018):
403                         - ImGui::SetScrollHere()              -> use ImGui::SetScrollHereY()
404  - 2021/03/11 (1.82) - clarified that ImDrawList::PathArcTo(), ImDrawList::PathArcToFast() won't render with radius < 0.0f. Previously it sorts of accidentally worked but would generally lead to counter-clockwise paths and have an effect on anti-aliasing.
405  - 2021/03/10 (1.82) - upgraded ImDrawList::AddPolyline() and PathStroke() "bool closed" parameter to "ImDrawFlags flags". The matching ImDrawFlags_Closed value is guaranteed to always stay == 1 in the future.
406  - 2021/02/22 (1.82) - (*undone in 1.84*) win32+mingw: Re-enabled IME functions by default even under MinGW. In July 2016, issue #738 had me incorrectly disable those default functions for MinGW. MinGW users should: either link with -limm32, either set their imconfig file  with '#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS'.
407  - 2021/02/17 (1.82) - renamed rarely used style.CircleSegmentMaxError (old default = 1.60f) to style.CircleTessellationMaxError (new default = 0.30f) as the meaning of the value changed.
408  - 2021/02/03 (1.81) - renamed ListBoxHeader(const char* label, ImVec2 size) to BeginListBox(). Kept inline redirection function (will obsolete).
409                      - removed ListBoxHeader(const char* label, int items_count, int height_in_items = -1) in favor of specifying size. Kept inline redirection function (will obsolete).
410                      - renamed ListBoxFooter() to EndListBox(). Kept inline redirection function (will obsolete).
411  - 2021/01/26 (1.81) - removed ImGuiFreeType::BuildFontAtlas(). Kept inline redirection function. Prefer using '#define IMGUI_ENABLE_FREETYPE', but there's a runtime selection path available too. The shared extra flags parameters (very rarely used) are now stored in ImFontAtlas::FontBuilderFlags.
412                      - renamed ImFontConfig::RasterizerFlags (used by FreeType) to ImFontConfig::FontBuilderFlags.
413                      - renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API.
414  - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.63 (August 2018):
415                         - ImGui::IsItemDeactivatedAfterChange() -> use ImGui::IsItemDeactivatedAfterEdit().
416                         - ImGuiCol_ModalWindowDarkening       -> use ImGuiCol_ModalWindowDimBg
417                         - ImGuiInputTextCallback              -> use ImGuiTextEditCallback
418                         - ImGuiInputTextCallbackData          -> use ImGuiTextEditCallbackData
419  - 2020/12/21 (1.80) - renamed ImDrawList::AddBezierCurve() to AddBezierCubic(), and PathBezierCurveTo() to PathBezierCubicCurveTo(). Kept inline redirection function (will obsolete).
420  - 2020/12/04 (1.80) - added imgui_tables.cpp file! Manually constructed project files will need the new file added!
421  - 2020/11/18 (1.80) - renamed undocumented/internals ImGuiColumnsFlags_* to ImGuiOldColumnFlags_* in prevision of incoming Tables API.
422  - 2020/11/03 (1.80) - renamed io.ConfigWindowsMemoryCompactTimer to io.ConfigMemoryCompactTimer as the feature will apply to other data structures
423  - 2020/10/14 (1.80) - backends: moved all backends files (imgui_impl_XXXX.cpp, imgui_impl_XXXX.h) from examples/ to backends/.
424  - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.60 (April 2018):
425                         - io.RenderDrawListsFn pointer        -> use ImGui::GetDrawData() value and call the render function of your backend
426                         - ImGui::IsAnyWindowFocused()         -> use ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)
427                         - ImGui::IsAnyWindowHovered()         -> use ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
428                         - ImGuiStyleVar_Count_                -> use ImGuiStyleVar_COUNT
429                         - ImGuiMouseCursor_Count_             -> use ImGuiMouseCursor_COUNT
430                       - removed redirecting functions names that were marked obsolete in 1.61 (May 2018):
431                         - InputFloat (... int decimal_precision ...) -> use InputFloat (... const char* format ...) with format = "%.Xf" where X is your value for decimal_precision.
432                         - same for InputFloat2()/InputFloat3()/InputFloat4() variants taking a `int decimal_precision` parameter.
433  - 2020/10/05 (1.79) - removed ImGuiListClipper: Renamed constructor parameters which created an ambiguous alternative to using the ImGuiListClipper::Begin() function, with misleading edge cases (note: imgui_memory_editor <0.40 from imgui_club/ used this old clipper API. Update your copy if needed).
434  - 2020/09/25 (1.79) - renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. Kept redirection enum (will obsolete sooner because previous name was added recently).
435  - 2020/09/25 (1.79) - renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton.
436  - 2020/09/21 (1.79) - renamed OpenPopupContextItem() back to OpenPopupOnItemClick(), reverting the change from 1.77. For varieties of reason this is more self-explanatory.
437  - 2020/09/21 (1.79) - removed return value from OpenPopupOnItemClick() - returned true on mouse release on an item - because it is inconsistent with other popup APIs and makes others misleading. It's also and unnecessary: you can use IsWindowAppearing() after BeginPopup() for a similar result.
438  - 2020/09/17 (1.79) - removed ImFont::DisplayOffset in favor of ImFontConfig::GlyphOffset. DisplayOffset was applied after scaling and not very meaningful/useful outside of being needed by the default ProggyClean font. If you scaled this value after calling AddFontDefault(), this is now done automatically. It was also getting in the way of better font scaling, so let's get rid of it now!
439  - 2020/08/17 (1.78) - obsoleted use of the trailing 'float power=1.0f' parameter for DragFloat(), DragFloat2(), DragFloat3(), DragFloat4(), DragFloatRange2(), DragScalar(), DragScalarN(), SliderFloat(), SliderFloat2(), SliderFloat3(), SliderFloat4(), SliderScalar(), SliderScalarN(), VSliderFloat() and VSliderScalar().
440                        replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags).
441                        worked out a backward-compatibility scheme so hopefully most C++ codebase should not be affected. in short, when calling those functions:
442                        - if you omitted the 'power' parameter (likely!), you are not affected.
443                        - if you set the 'power' parameter to 1.0f (same as previous default value): 1/ your compiler may warn on float>int conversion, 2/ everything else will work. 3/ you can replace the 1.0f value with 0 to fix the warning, and be technically correct.
444                        - if you set the 'power' parameter to >1.0f (to enable non-linear editing): 1/ your compiler may warn on float>int conversion, 2/ code will assert at runtime, 3/ in case asserts are disabled, the code will not crash and enable the _Logarithmic flag. 4/ you can replace the >1.0f value with ImGuiSliderFlags_Logarithmic to fix the warning/assert and get a _similar_ effect as previous uses of power >1.0f.
445                        see https://github.com/ocornut/imgui/issues/3361 for all details.
446                        kept inline redirection functions (will obsolete) apart for: DragFloatRange2(), VSliderFloat(), VSliderScalar(). For those three the 'float power=1.0f' version was removed directly as they were most unlikely ever used.
447                        for shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`.
448                      - obsoleted use of v_min > v_max in DragInt, DragFloat, DragScalar to lock edits (introduced in 1.73, was not demoed nor documented very), will be replaced by a more generic ReadOnly feature. You may use the ImGuiSliderFlags_ReadOnly internal flag in the meantime.
449  - 2020/06/23 (1.77) - removed BeginPopupContextWindow(const char*, int mouse_button, bool also_over_items) in favor of BeginPopupContextWindow(const char*, ImGuiPopupFlags flags) with ImGuiPopupFlags_NoOverItems.
450  - 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete). [NOTE: THIS WAS REVERTED IN 1.79]
451  - 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017.
452  - 2020/04/23 (1.77) - removed unnecessary ID (first arg) of ImFontAtlas::AddCustomRectRegular().
453  - 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more.
454  - 2019/12/17 (1.75) - [undid this change in 1.76] made Columns() limited to 64 columns by asserting above that limit. While the current code technically supports it, future code may not so we're putting the restriction ahead.
455  - 2019/12/13 (1.75) - [imgui_internal.h] changed ImRect() default constructor initializes all fields to 0.0f instead of (FLT_MAX,FLT_MAX,-FLT_MAX,-FLT_MAX). If you used ImRect::Add() to create bounding boxes by adding multiple points into it, you may need to fix your initial value.
456  - 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017):
457                        - ShowTestWindow()                    -> use ShowDemoWindow()
458                        - IsRootWindowFocused()               -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow)
459                        - IsRootWindowOrAnyChildFocused()     -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
460                        - SetNextWindowContentWidth(w)        -> use SetNextWindowContentSize(ImVec2(w, 0.0f)
461                        - GetItemsLineHeightWithSpacing()     -> use GetFrameHeightWithSpacing()
462                        - ImGuiCol_ChildWindowBg              -> use ImGuiCol_ChildBg
463                        - ImGuiStyleVar_ChildWindowRounding   -> use ImGuiStyleVar_ChildRounding
464                        - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap
465                        - IMGUI_DISABLE_TEST_WINDOWS          -> use IMGUI_DISABLE_DEMO_WINDOWS
466  - 2019/12/08 (1.75) - obsoleted calling ImDrawList::PrimReserve() with a negative count (which was vaguely documented and rarely if ever used). Instead, we added an explicit PrimUnreserve() API.
467  - 2019/12/06 (1.75) - removed implicit default parameter to IsMouseDragging(int button = 0) to be consistent with other mouse functions (none of the other functions have it).
468  - 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.
469  - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency.
470  - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency.
471  - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017):
472                        - Begin() [old 5 args version]        -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed
473                        - IsRootWindowOrAnyChildHovered()     -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows)
474                        - AlignFirstTextHeightToWidgets()     -> use AlignTextToFramePadding()
475                        - SetNextWindowPosCenter()            -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f)
476                        - ImFont::Glyph                       -> use ImFontGlyph
477  - 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.
478                        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.
479                        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).
480                        If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you.
481  - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete).
482  - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete).
483  - 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.
484  - 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
485                        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.
486                        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.
487                        Please reach out if you are affected.
488  - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete).
489  - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c).
490  - 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.
491  - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).
492  - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).
493  - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).
494  - 2019/02/14 (1.68) - made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). If for some reason your time step calculation gives you a zero value, replace it with an arbitrarily small value!
495  - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
496  - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
497  - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).
498  - 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.
499  - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
500  - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
501  - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
502  - 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.
503                        If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
504  - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
505  - 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.
506                        NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
507                        Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
508  - 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).
509  - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
510  - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
511  - 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.
512  - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
513  - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
514  - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
515  - 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.).
516                        old backends will still work as is, however prefer using the separated backends as they will be updated to support multi-viewports.
517                        when adopting new backends follow the main.cpp code of your preferred examples/ folder to know which functions to call.
518                        in particular, note that old backends called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function.
519  - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
520  - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
521  - 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.
522                        If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
523                        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.
524                        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.
525  - 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",
526                        consistent with other functions. Kept redirection functions (will obsolete).
527  - 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.
528  - 2018/03/20 (1.60) - renamed io.WantMoveMouse to io.WantSetMousePos for consistency and ease of understanding (was added in 1.52, _not_ used by core and only honored by some backend ahead of merging the Nav branch).
529  - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
530  - 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.
531  - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
532  - 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.
533  - 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.
534  - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
535                        - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
536                        - removed Shutdown() function, as DestroyContext() serve this purpose.
537                        - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
538                        - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
539                        - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
540  - 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.
541  - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
542  - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
543  - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
544  - 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.
545  - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
546  - 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
547  - 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.
548  - 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.
549  - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
550  - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
551                      - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
552  - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
553  - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
554  - 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.
555  - 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.
556                        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.
557  - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
558  - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
559  - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
560  - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
561  - 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.
562  - 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.
563  - 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.
564                        removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
565                          IsItemHoveredRect()        --> IsItemHovered(ImGuiHoveredFlags_RectOnly)
566                          IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
567                          IsMouseHoveringWindow()    --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior]
568  - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
569  - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
570  - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).
571  - 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).
572  - 2017/08/25 (1.52) - io.MousePos needs to be set to ImVec2(-FLT_MAX,-FLT_MAX) when mouse is unavailable/missing. Previously ImVec2(-1,-1) was enough but we now accept negative mouse coordinates. In your backend if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)".
573  - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
574                      - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
575                      - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
576  - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
577  - 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.
578  - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type.
579  - 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.
580  - 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).
581  - 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).
582  - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
583  - 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.
584                      - 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.
585                      - 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))'
586  - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
587  - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
588  - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
589  - 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().
590  - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
591  - 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.
592  - 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.
593  - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
594                        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 to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
595                        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:
596                        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); }
597                        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.
598  - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
599  - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
600  - 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).
601  - 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.
602  - 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).
603  - 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)
604  - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
605  - 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.
606  - 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.
607  - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
608  - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
609  - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
610                        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.
611                        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!
612  - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
613  - 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.
614  - 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
615  - 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.
616                        you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
617  - 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.
618                        this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
619                      - if you are using a vanilla copy of one of the imgui_impl_XXX.cpp provided in the example, you just need to update your copy and you can ignore the rest.
620                      - the signature of the io.RenderDrawListsFn handler has changed!
621                        old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
622                        new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
623                          parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
624                          ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
625                          ImDrawCmd:  'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
626                      - 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.
627                      - 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!
628                      - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
629  - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
630  - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
631  - 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.
632  - 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
633  - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely used. Sorry!
634  - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
635  - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
636  - 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.
637  - 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.
638  - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
639  - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
640  - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
641  - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
642  - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
643  - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
644  - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
645  - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
646  - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
647  - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
648  - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
649  - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
650  - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
651  - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
652  - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
653  - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
654  - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
655  - 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
656                        - old:  const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..];
657                        - new:  unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->SetTexID(YourTexIdentifier);
658                        you now have more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs. It is now recommended that you sample the font texture with bilinear interpolation.
659  - 2015/01/11 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to call io.Fonts->SetTexID()
660  - 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
661  - 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
662  - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
663  - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
664  - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
665  - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
666  - 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)
667  - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
668  - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
669  - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
670  - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
671  - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
672  - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
673 
674 
675  FREQUENTLY ASKED QUESTIONS (FAQ)
676  ================================
677 
678  Read all answers online:
679    https://www.dearimgui.org/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url)
680  Read all answers locally (with a text editor or ideally a Markdown viewer):
681    docs/FAQ.md
682  Some answers are copied down here to facilitate searching in code.
683 
684  Q&A: Basics
685  ===========
686 
687  Q: Where is the documentation?
688  A: This library is poorly documented at the moment and expects the user to be acquainted with C/C++.
689     - Run the examples/ and explore them.
690     - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function.
691     - The demo covers most features of Dear ImGui, so you can read the code and see its output.
692     - See documentation and comments at the top of imgui.cpp + effectively imgui.h.
693     - Dozens of standalone example applications using e.g. OpenGL/DirectX are provided in the
694       examples/ folder to explain how to integrate Dear ImGui with your own engine/application.
695     - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links.
696     - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful.
697     - Your programming IDE is your friend, find the type or function declaration to find comments
698       associated with it.
699 
700  Q: What is this library called?
701  Q: Which version should I get?
702  >> This library is called "Dear ImGui", please don't call it "ImGui" :)
703  >> See https://www.dearimgui.org/faq for details.
704 
705  Q&A: Integration
706  ================
707 
708  Q: How to get started?
709  A: Read 'PROGRAMMER GUIDE' above. Read examples/README.txt.
710 
711  Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?
712  A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
713  >> See https://www.dearimgui.org/faq for a fully detailed answer. You really want to read this.
714 
715  Q. How can I enable keyboard controls?
716  Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display)
717  Q: I integrated Dear ImGui in my engine and little squares are showing instead of text...
718  Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around...
719  Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries...
720  >> See https://www.dearimgui.org/faq
721 
722  Q&A: Usage
723  ----------
724 
725  Q: Why is my widget not reacting when I click on it?
726  Q: How can I have widgets with an empty label?
727  Q: How can I have multiple widgets with the same label?
728  Q: How can I display an image? What is ImTextureID, how does it works?
729  Q: How can I use my own math types instead of ImVec2/ImVec4?
730  Q: How can I interact with standard C++ types (such as std::string and std::vector)?
731  Q: How can I display custom shapes? (using low-level ImDrawList API)
732  >> See https://www.dearimgui.org/faq
733 
734  Q&A: Fonts, Text
735  ================
736 
737  Q: How should I handle DPI in my application?
738  Q: How can I load a different font than the default?
739  Q: How can I easily use icons in my application?
740  Q: How can I load multiple fonts?
741  Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
742  >> See https://www.dearimgui.org/faq and https://github.com/ocornut/imgui/edit/master/docs/FONTS.md
743 
744  Q&A: Concerns
745  =============
746 
747  Q: Who uses Dear ImGui?
748  Q: Can you create elaborate/serious tools with Dear ImGui?
749  Q: Can you reskin the look of Dear ImGui?
750  Q: Why using C++ (as opposed to C)?
751  >> See https://www.dearimgui.org/faq
752 
753  Q&A: Community
754  ==============
755 
756  Q: How can I help?
757  A: - Businesses: please reach out to "contact AT dearimgui.com" if you work in a place using Dear ImGui!
758       We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts.
759       This is among the most useful thing you can do for Dear ImGui. With increased funding, we can hire more people working on this project.
760     - Individuals: you can support continued development via PayPal donations. See README.
761     - If you are experienced with Dear ImGui and C++, look at the GitHub issues, look at the Wiki, read docs/TODO.txt
762       and see how you want to help and can help!
763     - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
764       You may post screenshot or links in the gallery threads. Visuals are ideal as they inspire other programmers.
765       But even without visuals, disclosing your use of dear imgui helps the library grow credibility, and help other teams and programmers with taking decisions.
766     - 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).
767 
768 */
769 
770 //-------------------------------------------------------------------------
771 // [SECTION] INCLUDES
772 //-------------------------------------------------------------------------
773 
774 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
775 #define _CRT_SECURE_NO_WARNINGS
776 #endif
777 
778 #include "imgui.h"
779 #ifndef IMGUI_DISABLE
780 
781 #ifndef IMGUI_DEFINE_MATH_OPERATORS
782 #define IMGUI_DEFINE_MATH_OPERATORS
783 #endif
784 #include "imgui_internal.h"
785 
786 // System includes
787 #include <ctype.h>      // toupper
788 #include <stdio.h>      // vsnprintf, sscanf, printf
789 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
790 #include <stddef.h>     // intptr_t
791 #else
792 #include <stdint.h>     // intptr_t
793 #endif
794 
795 // [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled
796 #if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
797 #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
798 #endif
799 
800 // [Windows] OS specific includes (optional)
801 #if defined(_WIN32) && defined(IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
802 #define IMGUI_DISABLE_WIN32_FUNCTIONS
803 #endif
804 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
805 #ifndef WIN32_LEAN_AND_MEAN
806 #define WIN32_LEAN_AND_MEAN
807 #endif
808 #ifndef NOMINMAX
809 #define NOMINMAX
810 #endif
811 #ifndef __MINGW32__
812 #include <Windows.h>        // _wfopen, OpenClipboard
813 #else
814 #include <windows.h>
815 #endif
816 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) // UWP doesn't have all Win32 functions
817 #define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
818 #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
819 #endif
820 #endif
821 
822 // [Apple] OS specific includes
823 #if defined(__APPLE__)
824 #include <TargetConditionals.h>
825 #endif
826 
827 // Visual Studio warnings
828 #ifdef _MSC_VER
829 #pragma warning (disable: 4127)             // condition expression is constant
830 #pragma warning (disable: 4996)             // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
831 #if defined(_MSC_VER) && _MSC_VER >= 1922   // MSVC 2019 16.2 or later
832 #pragma warning (disable: 5054)             // operator '|': deprecated between enumerations of different types
833 #endif
834 #pragma warning (disable: 26451)            // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
835 #pragma warning (disable: 26495)            // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).
836 #pragma warning (disable: 26812)            // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
837 #endif
838 
839 // Clang/GCC warnings with -Weverything
840 #if defined(__clang__)
841 #if __has_warning("-Wunknown-warning-option")
842 #pragma clang diagnostic ignored "-Wunknown-warning-option"         // warning: unknown warning group 'xxx'                      // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!
843 #endif
844 #pragma clang diagnostic ignored "-Wunknown-pragmas"                // warning: unknown warning group 'xxx'
845 #pragma clang diagnostic ignored "-Wold-style-cast"                 // warning: use of old-style cast                            // yes, they are more terse.
846 #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.
847 #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.
848 #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.
849 #pragma clang diagnostic ignored "-Wglobal-constructors"            // warning: declaration requires a global destructor         // similar to above, not sure what the exact difference is.
850 #pragma clang diagnostic ignored "-Wsign-conversion"                // warning: implicit conversion changes signedness
851 #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.
852 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast"       // warning: cast to 'void *' from smaller integer type 'int'
853 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"  // warning: zero as null pointer constant                    // some standard header variations use #define NULL 0
854 #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.
855 #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"  // warning: implicit conversion from 'xxx' to 'float' may lose precision
856 #elif defined(__GNUC__)
857 // We disable -Wpragmas because GCC doesn't provide an has_warning equivalent and some forks/patches may not following the warning/version association.
858 #pragma GCC diagnostic ignored "-Wpragmas"                  // warning: unknown option after '#pragma GCC diagnostic' kind
859 #pragma GCC diagnostic ignored "-Wunused-function"          // warning: 'xxxx' defined but not used
860 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"      // warning: cast to pointer from integer of different size
861 #pragma GCC diagnostic ignored "-Wformat"                   // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
862 #pragma GCC diagnostic ignored "-Wdouble-promotion"         // warning: implicit conversion from 'float' to 'double' when passing argument to function
863 #pragma GCC diagnostic ignored "-Wconversion"               // warning: conversion to 'xxxx' from 'xxxx' may alter its value
864 #pragma GCC diagnostic ignored "-Wformat-nonliteral"        // warning: format not a string literal, format string not checked
865 #pragma GCC diagnostic ignored "-Wstrict-overflow"          // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
866 #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
867 #endif
868 
869 // Debug options
870 #define IMGUI_DEBUG_NAV_SCORING     0   // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL
871 #define IMGUI_DEBUG_NAV_RECTS       0   // Display the reference navigation rectangle for each window
872 #define IMGUI_DEBUG_INI_SETTINGS    0   // Save additional comments in .ini file (particularly helps for Docking, but makes saving slower)
873 
874 // 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.
875 static const float NAV_WINDOWING_HIGHLIGHT_DELAY            = 0.20f;    // Time before the highlight and screen dimming starts fading in
876 static const float NAV_WINDOWING_LIST_APPEAR_DELAY          = 0.15f;    // Time before the window list starts to appear
877 
878 // Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by backend)
879 static const float WINDOWS_HOVER_PADDING                    = 4.0f;     // Extend outside window for hovering/resizing (maxxed with TouchPadding) and inside windows for borders. Affect FindHoveredWindow().
880 static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f;    // Reduce visual noise by only highlighting the border after a certain time.
881 static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER    = 2.00f;    // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved.
882 
883 //-------------------------------------------------------------------------
884 // [SECTION] FORWARD DECLARATIONS
885 //-------------------------------------------------------------------------
886 
887 static void             SetCurrentWindow(ImGuiWindow* window);
888 static void             FindHoveredWindow();
889 static ImGuiWindow*     CreateNewWindow(const char* name, ImGuiWindowFlags flags);
890 static ImVec2           CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);
891 
892 static void             AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
893 static void             AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
894 
895 // Settings
896 static void             WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
897 static void*            WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
898 static void             WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
899 static void             WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);
900 static void             WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf);
901 
902 // Platform Dependents default implementation for IO functions
903 static const char*      GetClipboardTextFn_DefaultImpl(void* user_data);
904 static void             SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
905 static void             ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
906 
907 namespace ImGui
908 {
909 // Navigation
910 static void             NavUpdate();
911 static void             NavUpdateWindowing();
912 static void             NavUpdateWindowingOverlay();
913 static void             NavUpdateInitResult();
914 static void             NavUpdateCancelRequest();
915 static void             NavUpdateCreateMoveRequest();
916 static float            NavUpdatePageUpPageDown();
917 static inline void      NavUpdateAnyRequestFlag();
918 static void             NavEndFrame();
919 static bool             NavScoreItem(ImGuiNavItemData* result, ImRect cand);
920 static void             NavApplyItemToResult(ImGuiNavItemData* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel);
921 static void             NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id);
922 static ImVec2           NavCalcPreferredRefPos();
923 static void             NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
924 static ImGuiWindow*     NavRestoreLastChildNavWindow(ImGuiWindow* window);
925 static void             NavRestoreLayer(ImGuiNavLayer layer);
926 static int              FindWindowFocusIndex(ImGuiWindow* window);
927 
928 // Error Checking
929 static void             ErrorCheckNewFrameSanityChecks();
930 static void             ErrorCheckEndFrameSanityChecks();
931 
932 // Misc
933 static void             UpdateSettings();
934 static void             UpdateMouseInputs();
935 static void             UpdateMouseWheel();
936 static void             UpdateTabFocus();
937 static void             UpdateDebugToolItemPicker();
938 static bool             UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect);
939 static void             RenderWindowOuterBorders(ImGuiWindow* window);
940 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);
941 static void             RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
942 
943 // Viewports
944 static void             UpdateViewportsNewFrame();
945 
946 }
947 
948 //-----------------------------------------------------------------------------
949 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
950 //-----------------------------------------------------------------------------
951 
952 // DLL users:
953 // - Heaps and globals are not shared across DLL boundaries!
954 // - You will need to call SetCurrentContext() + SetAllocatorFunctions() for each static/DLL boundary you are calling from.
955 // - Same applies for hot-reloading mechanisms that are reliant on reloading DLL (note that many hot-reloading mechanisms work without DLL).
956 // - Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
957 // - Confused? In a debugger: add GImGui to your watch window and notice how its value changes depending on your current location (which DLL boundary you are in).
958 
959 // Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
960 // - ImGui::CreateContext() will automatically set this pointer if it is NULL.
961 //   Change to a different context by calling ImGui::SetCurrentContext().
962 // - Important: Dear ImGui functions are not thread-safe because of this pointer.
963 //   If you want thread-safety to allow N threads to access N different contexts:
964 //   - Change this variable to use thread local storage so each thread can refer to a different context, in your imconfig.h:
965 //         struct ImGuiContext;
966 //         extern thread_local ImGuiContext* MyImGuiTLS;
967 //         #define GImGui MyImGuiTLS
968 //     And then define MyImGuiTLS in one of your cpp files. Note that thread_local is a C++11 keyword, earlier C++ uses compiler-specific keyword.
969 //   - Future development aims to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
970 //   - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from a different namespace.
971 // - DLL users: read comments above.
972 #ifndef GImGui
973 ImGuiContext*   GImGui = NULL;
974 #endif
975 
976 // Memory Allocator functions. Use SetAllocatorFunctions() to change them.
977 // - You probably don't want to modify that mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction.
978 // - DLL users: read comments above.
979 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
MallocWrapper(size_t size,void * user_data)980 static void*   MallocWrapper(size_t size, void* user_data)    { IM_UNUSED(user_data); return malloc(size); }
FreeWrapper(void * ptr,void * user_data)981 static void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); free(ptr); }
982 #else
MallocWrapper(size_t size,void * user_data)983 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)984 static void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }
985 #endif
986 static ImGuiMemAllocFunc    GImAllocatorAllocFunc = MallocWrapper;
987 static ImGuiMemFreeFunc     GImAllocatorFreeFunc = FreeWrapper;
988 static void*                GImAllocatorUserData = NULL;
989 
990 //-----------------------------------------------------------------------------
991 // [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
992 //-----------------------------------------------------------------------------
993 
ImGuiStyle()994 ImGuiStyle::ImGuiStyle()
995 {
996     Alpha                   = 1.0f;             // Global alpha applies to everything in Dear ImGui.
997     DisabledAlpha           = 0.60f;            // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha.
998     WindowPadding           = ImVec2(8,8);      // Padding within a window
999     WindowRounding          = 0.0f;             // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended.
1000     WindowBorderSize        = 1.0f;             // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1001     WindowMinSize           = ImVec2(32,32);    // Minimum window size
1002     WindowTitleAlign        = ImVec2(0.0f,0.5f);// Alignment for title bar text
1003     WindowMenuButtonPosition= ImGuiDir_Left;    // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.
1004     ChildRounding           = 0.0f;             // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
1005     ChildBorderSize         = 1.0f;             // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1006     PopupRounding           = 0.0f;             // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
1007     PopupBorderSize         = 1.0f;             // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1008     FramePadding            = ImVec2(4,3);      // Padding within a framed rectangle (used by most widgets)
1009     FrameRounding           = 0.0f;             // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
1010     FrameBorderSize         = 0.0f;             // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
1011     ItemSpacing             = ImVec2(8,4);      // Horizontal and vertical spacing between widgets/lines
1012     ItemInnerSpacing        = ImVec2(4,4);      // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
1013     CellPadding             = ImVec2(4,2);      // Padding within a table cell
1014     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!
1015     IndentSpacing           = 21.0f;            // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
1016     ColumnsMinSpacing       = 6.0f;             // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
1017     ScrollbarSize           = 14.0f;            // Width of the vertical scrollbar, Height of the horizontal scrollbar
1018     ScrollbarRounding       = 9.0f;             // Radius of grab corners rounding for scrollbar
1019     GrabMinSize             = 10.0f;            // Minimum width/height of a grab box for slider/scrollbar
1020     GrabRounding            = 0.0f;             // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
1021     LogSliderDeadzone       = 4.0f;             // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
1022     TabRounding             = 4.0f;             // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
1023     TabBorderSize           = 0.0f;             // Thickness of border around tabs.
1024     TabMinWidthForCloseButton = 0.0f;           // Minimum width for close button to appears on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected.
1025     ColorButtonPosition     = ImGuiDir_Right;   // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
1026     ButtonTextAlign         = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
1027     SelectableTextAlign     = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line.
1028     DisplayWindowPadding    = ImVec2(19,19);    // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows.
1029     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.
1030     MouseCursorScale        = 1.0f;             // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
1031     AntiAliasedLines        = true;             // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU.
1032     AntiAliasedLinesUseTex  = true;             // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering.
1033     AntiAliasedFill         = true;             // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).
1034     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.
1035     CircleTessellationMaxError = 0.30f;         // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
1036 
1037     // Default theme
1038     ImGui::StyleColorsDark(this);
1039 }
1040 
1041 // 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.
1042 // 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)1043 void ImGuiStyle::ScaleAllSizes(float scale_factor)
1044 {
1045     WindowPadding = ImFloor(WindowPadding * scale_factor);
1046     WindowRounding = ImFloor(WindowRounding * scale_factor);
1047     WindowMinSize = ImFloor(WindowMinSize * scale_factor);
1048     ChildRounding = ImFloor(ChildRounding * scale_factor);
1049     PopupRounding = ImFloor(PopupRounding * scale_factor);
1050     FramePadding = ImFloor(FramePadding * scale_factor);
1051     FrameRounding = ImFloor(FrameRounding * scale_factor);
1052     ItemSpacing = ImFloor(ItemSpacing * scale_factor);
1053     ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
1054     CellPadding = ImFloor(CellPadding * scale_factor);
1055     TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
1056     IndentSpacing = ImFloor(IndentSpacing * scale_factor);
1057     ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
1058     ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
1059     ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
1060     GrabMinSize = ImFloor(GrabMinSize * scale_factor);
1061     GrabRounding = ImFloor(GrabRounding * scale_factor);
1062     LogSliderDeadzone = ImFloor(LogSliderDeadzone * scale_factor);
1063     TabRounding = ImFloor(TabRounding * scale_factor);
1064     TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImFloor(TabMinWidthForCloseButton * scale_factor) : FLT_MAX;
1065     DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
1066     DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
1067     MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
1068 }
1069 
ImGuiIO()1070 ImGuiIO::ImGuiIO()
1071 {
1072     // Most fields are initialized with zero
1073     memset(this, 0, sizeof(*this));
1074     IM_ASSERT(IM_ARRAYSIZE(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_ARRAYSIZE(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT); // Our pre-C++11 IM_STATIC_ASSERT() macros triggers warning on modern compilers so we don't use it here.
1075 
1076     // Settings
1077     ConfigFlags = ImGuiConfigFlags_None;
1078     BackendFlags = ImGuiBackendFlags_None;
1079     DisplaySize = ImVec2(-1.0f, -1.0f);
1080     DeltaTime = 1.0f / 60.0f;
1081     IniSavingRate = 5.0f;
1082     IniFilename = "imgui.ini"; // Important: "imgui.ini" is relative to current working dir, most apps will want to lock this to an absolute path (e.g. same path as executables).
1083     LogFilename = "imgui_log.txt";
1084     MouseDoubleClickTime = 0.30f;
1085     MouseDoubleClickMaxDist = 6.0f;
1086     for (int i = 0; i < ImGuiKey_COUNT; i++)
1087         KeyMap[i] = -1;
1088     KeyRepeatDelay = 0.275f;
1089     KeyRepeatRate = 0.050f;
1090     UserData = NULL;
1091 
1092     Fonts = NULL;
1093     FontGlobalScale = 1.0f;
1094     FontDefault = NULL;
1095     FontAllowUserScaling = false;
1096     DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1097 
1098     // Miscellaneous options
1099     MouseDrawCursor = false;
1100 #ifdef __APPLE__
1101     ConfigMacOSXBehaviors = true;  // Set Mac OS X style defaults based on __APPLE__ compile time flag
1102 #else
1103     ConfigMacOSXBehaviors = false;
1104 #endif
1105     ConfigInputTextCursorBlink = true;
1106     ConfigWindowsResizeFromEdges = true;
1107     ConfigWindowsMoveFromTitleBarOnly = false;
1108     ConfigMemoryCompactTimer = 60.0f;
1109 
1110     // Platform Functions
1111     BackendPlatformName = BackendRendererName = NULL;
1112     BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1113     GetClipboardTextFn = GetClipboardTextFn_DefaultImpl;   // Platform dependent default implementations
1114     SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
1115     ClipboardUserData = NULL;
1116     ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
1117     ImeWindowHandle = NULL;
1118 
1119     // Input (NB: we already have memset zero the entire structure!)
1120     MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1121     MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1122     MouseDragThreshold = 6.0f;
1123     for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1124     for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i]  = KeysDownDurationPrev[i] = -1.0f;
1125     for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
1126 }
1127 
1128 // Pass in translated ASCII characters for text input.
1129 // - with glfw you can get those from the callback set in glfwSetCharCallback()
1130 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(unsigned int c)1131 void ImGuiIO::AddInputCharacter(unsigned int c)
1132 {
1133     if (c != 0)
1134         InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID);
1135 }
1136 
1137 // UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so
1138 // we should save the high surrogate.
AddInputCharacterUTF16(ImWchar16 c)1139 void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c)
1140 {
1141     if (c == 0 && InputQueueSurrogate == 0)
1142         return;
1143 
1144     if ((c & 0xFC00) == 0xD800) // High surrogate, must save
1145     {
1146         if (InputQueueSurrogate != 0)
1147             InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID);
1148         InputQueueSurrogate = c;
1149         return;
1150     }
1151 
1152     ImWchar cp = c;
1153     if (InputQueueSurrogate != 0)
1154     {
1155         if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate
1156         {
1157             InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID);
1158         }
1159         else
1160         {
1161 #if IM_UNICODE_CODEPOINT_MAX == 0xFFFF
1162             cp = IM_UNICODE_CODEPOINT_INVALID; // Codepoint will not fit in ImWchar
1163 #else
1164             cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000);
1165 #endif
1166         }
1167 
1168         InputQueueSurrogate = 0;
1169     }
1170     InputQueueCharacters.push_back(cp);
1171 }
1172 
AddInputCharactersUTF8(const char * utf8_chars)1173 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1174 {
1175     while (*utf8_chars != 0)
1176     {
1177         unsigned int c = 0;
1178         utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
1179         if (c != 0)
1180             InputQueueCharacters.push_back((ImWchar)c);
1181     }
1182 }
1183 
ClearInputCharacters()1184 void ImGuiIO::ClearInputCharacters()
1185 {
1186     InputQueueCharacters.resize(0);
1187 }
1188 
AddFocusEvent(bool focused)1189 void ImGuiIO::AddFocusEvent(bool focused)
1190 {
1191     if (focused)
1192         return;
1193 
1194     // Clear buttons state when focus is lost
1195     // (this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle)
1196     memset(KeysDown, 0, sizeof(KeysDown));
1197     for (int n = 0; n < IM_ARRAYSIZE(KeysDownDuration); n++)
1198         KeysDownDuration[n] = KeysDownDurationPrev[n] = -1.0f;
1199     KeyCtrl = KeyShift = KeyAlt = KeySuper = false;
1200     KeyMods = KeyModsPrev = ImGuiKeyModFlags_None;
1201     for (int n = 0; n < IM_ARRAYSIZE(NavInputsDownDuration); n++)
1202         NavInputsDownDuration[n] = NavInputsDownDurationPrev[n] = -1.0f;
1203 }
1204 
1205 //-----------------------------------------------------------------------------
1206 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
1207 //-----------------------------------------------------------------------------
1208 
ImBezierCubicClosestPoint(const ImVec2 & p1,const ImVec2 & p2,const ImVec2 & p3,const ImVec2 & p4,const ImVec2 & p,int num_segments)1209 ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments)
1210 {
1211     IM_ASSERT(num_segments > 0); // Use ImBezierCubicClosestPointCasteljau()
1212     ImVec2 p_last = p1;
1213     ImVec2 p_closest;
1214     float p_closest_dist2 = FLT_MAX;
1215     float t_step = 1.0f / (float)num_segments;
1216     for (int i_step = 1; i_step <= num_segments; i_step++)
1217     {
1218         ImVec2 p_current = ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step);
1219         ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1220         float dist2 = ImLengthSqr(p - p_line);
1221         if (dist2 < p_closest_dist2)
1222         {
1223             p_closest = p_line;
1224             p_closest_dist2 = dist2;
1225         }
1226         p_last = p_current;
1227     }
1228     return p_closest;
1229 }
1230 
1231 // Closely mimics PathBezierToCasteljau() in imgui_draw.cpp
ImBezierCubicClosestPointCasteljauStep(const ImVec2 & p,ImVec2 & p_closest,ImVec2 & p_last,float & p_closest_dist2,float x1,float y1,float x2,float y2,float x3,float y3,float x4,float y4,float tess_tol,int level)1232 static void ImBezierCubicClosestPointCasteljauStep(const ImVec2& p, ImVec2& p_closest, ImVec2& p_last, float& p_closest_dist2, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level)
1233 {
1234     float dx = x4 - x1;
1235     float dy = y4 - y1;
1236     float d2 = ((x2 - x4) * dy - (y2 - y4) * dx);
1237     float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
1238     d2 = (d2 >= 0) ? d2 : -d2;
1239     d3 = (d3 >= 0) ? d3 : -d3;
1240     if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))
1241     {
1242         ImVec2 p_current(x4, y4);
1243         ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1244         float dist2 = ImLengthSqr(p - p_line);
1245         if (dist2 < p_closest_dist2)
1246         {
1247             p_closest = p_line;
1248             p_closest_dist2 = dist2;
1249         }
1250         p_last = p_current;
1251     }
1252     else if (level < 10)
1253     {
1254         float x12 = (x1 + x2)*0.5f,       y12 = (y1 + y2)*0.5f;
1255         float x23 = (x2 + x3)*0.5f,       y23 = (y2 + y3)*0.5f;
1256         float x34 = (x3 + x4)*0.5f,       y34 = (y3 + y4)*0.5f;
1257         float x123 = (x12 + x23)*0.5f,    y123 = (y12 + y23)*0.5f;
1258         float x234 = (x23 + x34)*0.5f,    y234 = (y23 + y34)*0.5f;
1259         float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f;
1260         ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);
1261         ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);
1262     }
1263 }
1264 
1265 // tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol
1266 // Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically.
ImBezierCubicClosestPointCasteljau(const ImVec2 & p1,const ImVec2 & p2,const ImVec2 & p3,const ImVec2 & p4,const ImVec2 & p,float tess_tol)1267 ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol)
1268 {
1269     IM_ASSERT(tess_tol > 0.0f);
1270     ImVec2 p_last = p1;
1271     ImVec2 p_closest;
1272     float p_closest_dist2 = FLT_MAX;
1273     ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, tess_tol, 0);
1274     return p_closest;
1275 }
1276 
ImLineClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & p)1277 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1278 {
1279     ImVec2 ap = p - a;
1280     ImVec2 ab_dir = b - a;
1281     float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1282     if (dot < 0.0f)
1283         return a;
1284     float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1285     if (dot > ab_len_sqr)
1286         return b;
1287     return a + ab_dir * dot / ab_len_sqr;
1288 }
1289 
ImTriangleContainsPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1290 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1291 {
1292     bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1293     bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1294     bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1295     return ((b1 == b2) && (b2 == b3));
1296 }
1297 
ImTriangleBarycentricCoords(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p,float & out_u,float & out_v,float & out_w)1298 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1299 {
1300     ImVec2 v0 = b - a;
1301     ImVec2 v1 = c - a;
1302     ImVec2 v2 = p - a;
1303     const float denom = v0.x * v1.y - v1.x * v0.y;
1304     out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1305     out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1306     out_u = 1.0f - out_v - out_w;
1307 }
1308 
ImTriangleClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1309 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1310 {
1311     ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1312     ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1313     ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1314     float dist2_ab = ImLengthSqr(p - proj_ab);
1315     float dist2_bc = ImLengthSqr(p - proj_bc);
1316     float dist2_ca = ImLengthSqr(p - proj_ca);
1317     float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1318     if (m == dist2_ab)
1319         return proj_ab;
1320     if (m == dist2_bc)
1321         return proj_bc;
1322     return proj_ca;
1323 }
1324 
1325 //-----------------------------------------------------------------------------
1326 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
1327 //-----------------------------------------------------------------------------
1328 
1329 // 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)1330 int ImStricmp(const char* str1, const char* str2)
1331 {
1332     int d;
1333     while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
1334     return d;
1335 }
1336 
ImStrnicmp(const char * str1,const char * str2,size_t count)1337 int ImStrnicmp(const char* str1, const char* str2, size_t count)
1338 {
1339     int d = 0;
1340     while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
1341     return d;
1342 }
1343 
ImStrncpy(char * dst,const char * src,size_t count)1344 void ImStrncpy(char* dst, const char* src, size_t count)
1345 {
1346     if (count < 1)
1347         return;
1348     if (count > 1)
1349         strncpy(dst, src, count - 1);
1350     dst[count - 1] = 0;
1351 }
1352 
ImStrdup(const char * str)1353 char* ImStrdup(const char* str)
1354 {
1355     size_t len = strlen(str);
1356     void* buf = IM_ALLOC(len + 1);
1357     return (char*)memcpy(buf, (const void*)str, len + 1);
1358 }
1359 
ImStrdupcpy(char * dst,size_t * p_dst_size,const char * src)1360 char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
1361 {
1362     size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1;
1363     size_t src_size = strlen(src) + 1;
1364     if (dst_buf_size < src_size)
1365     {
1366         IM_FREE(dst);
1367         dst = (char*)IM_ALLOC(src_size);
1368         if (p_dst_size)
1369             *p_dst_size = src_size;
1370     }
1371     return (char*)memcpy(dst, (const void*)src, src_size);
1372 }
1373 
ImStrchrRange(const char * str,const char * str_end,char c)1374 const char* ImStrchrRange(const char* str, const char* str_end, char c)
1375 {
1376     const char* p = (const char*)memchr(str, (int)c, str_end - str);
1377     return p;
1378 }
1379 
ImStrlenW(const ImWchar * str)1380 int ImStrlenW(const ImWchar* str)
1381 {
1382     //return (int)wcslen((const wchar_t*)str);  // FIXME-OPT: Could use this when wchar_t are 16-bit
1383     int n = 0;
1384     while (*str++) n++;
1385     return n;
1386 }
1387 
1388 // Find end-of-line. Return pointer will point to either first \n, either str_end.
ImStreolRange(const char * str,const char * str_end)1389 const char* ImStreolRange(const char* str, const char* str_end)
1390 {
1391     const char* p = (const char*)memchr(str, '\n', str_end - str);
1392     return p ? p : str_end;
1393 }
1394 
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)1395 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
1396 {
1397     while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1398         buf_mid_line--;
1399     return buf_mid_line;
1400 }
1401 
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)1402 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1403 {
1404     if (!needle_end)
1405         needle_end = needle + strlen(needle);
1406 
1407     const char un0 = (char)toupper(*needle);
1408     while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1409     {
1410         if (toupper(*haystack) == un0)
1411         {
1412             const char* b = needle + 1;
1413             for (const char* a = haystack + 1; b < needle_end; a++, b++)
1414                 if (toupper(*a) != toupper(*b))
1415                     break;
1416             if (b == needle_end)
1417                 return haystack;
1418         }
1419         haystack++;
1420     }
1421     return NULL;
1422 }
1423 
1424 // 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)1425 void ImStrTrimBlanks(char* buf)
1426 {
1427     char* p = buf;
1428     while (p[0] == ' ' || p[0] == '\t')     // Leading blanks
1429         p++;
1430     char* p_start = p;
1431     while (*p != 0)                         // Find end of string
1432         p++;
1433     while (p > p_start && (p[-1] == ' ' || p[-1] == '\t'))  // Trailing blanks
1434         p--;
1435     if (p_start != buf)                     // Copy memory if we had leading blanks
1436         memmove(buf, p_start, p - p_start);
1437     buf[p - p_start] = 0;                   // Zero terminate
1438 }
1439 
ImStrSkipBlank(const char * str)1440 const char* ImStrSkipBlank(const char* str)
1441 {
1442     while (str[0] == ' ' || str[0] == '\t')
1443         str++;
1444     return str;
1445 }
1446 
1447 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1448 // 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.
1449 // B) When buf==NULL vsnprintf() will return the output size.
1450 #ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1451 
1452 // We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h)
1453 // You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1454 // and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are
1455 // designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.)
1456 #ifdef IMGUI_USE_STB_SPRINTF
1457 #define STB_SPRINTF_IMPLEMENTATION
1458 #include "stb_sprintf.h"
1459 #endif
1460 
1461 #if defined(_MSC_VER) && !defined(vsnprintf)
1462 #define vsnprintf _vsnprintf
1463 #endif
1464 
ImFormatString(char * buf,size_t buf_size,const char * fmt,...)1465 int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1466 {
1467     va_list args;
1468     va_start(args, fmt);
1469 #ifdef IMGUI_USE_STB_SPRINTF
1470     int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1471 #else
1472     int w = vsnprintf(buf, buf_size, fmt, args);
1473 #endif
1474     va_end(args);
1475     if (buf == NULL)
1476         return w;
1477     if (w == -1 || w >= (int)buf_size)
1478         w = (int)buf_size - 1;
1479     buf[w] = 0;
1480     return w;
1481 }
1482 
ImFormatStringV(char * buf,size_t buf_size,const char * fmt,va_list args)1483 int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1484 {
1485 #ifdef IMGUI_USE_STB_SPRINTF
1486     int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1487 #else
1488     int w = vsnprintf(buf, buf_size, fmt, args);
1489 #endif
1490     if (buf == NULL)
1491         return w;
1492     if (w == -1 || w >= (int)buf_size)
1493         w = (int)buf_size - 1;
1494     buf[w] = 0;
1495     return w;
1496 }
1497 #endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1498 
1499 // CRC32 needs a 1KB lookup table (not cache friendly)
1500 // Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
1501 // - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
1502 static const ImU32 GCrc32LookupTable[256] =
1503 {
1504     0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
1505     0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
1506     0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
1507     0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
1508     0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
1509     0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
1510     0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
1511     0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
1512     0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
1513     0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
1514     0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
1515     0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
1516     0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
1517     0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
1518     0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
1519     0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
1520 };
1521 
1522 // Known size hash
1523 // It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
1524 // 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)1525 ImGuiID ImHashData(const void* data_p, size_t data_size, ImU32 seed)
1526 {
1527     ImU32 crc = ~seed;
1528     const unsigned char* data = (const unsigned char*)data_p;
1529     const ImU32* crc32_lut = GCrc32LookupTable;
1530     while (data_size-- != 0)
1531         crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
1532     return ~crc;
1533 }
1534 
1535 // Zero-terminated string hash, with support for ### to reset back to seed value
1536 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1537 // Because this syntax is rarely used we are optimizing for the common case.
1538 // - If we reach ### in the string we discard the hash so far and reset to the seed.
1539 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
1540 // 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)1541 ImGuiID ImHashStr(const char* data_p, size_t data_size, ImU32 seed)
1542 {
1543     seed = ~seed;
1544     ImU32 crc = seed;
1545     const unsigned char* data = (const unsigned char*)data_p;
1546     const ImU32* crc32_lut = GCrc32LookupTable;
1547     if (data_size != 0)
1548     {
1549         while (data_size-- != 0)
1550         {
1551             unsigned char c = *data++;
1552             if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')
1553                 crc = seed;
1554             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1555         }
1556     }
1557     else
1558     {
1559         while (unsigned char c = *data++)
1560         {
1561             if (c == '#' && data[0] == '#' && data[1] == '#')
1562                 crc = seed;
1563             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1564         }
1565     }
1566     return ~crc;
1567 }
1568 
1569 //-----------------------------------------------------------------------------
1570 // [SECTION] MISC HELPERS/UTILITIES (File functions)
1571 //-----------------------------------------------------------------------------
1572 
1573 // Default file functions
1574 #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
1575 
ImFileOpen(const char * filename,const char * mode)1576 ImFileHandle ImFileOpen(const char* filename, const char* mode)
1577 {
1578 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__)
1579     // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.
1580     // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32!
1581     const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
1582     const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
1583     ImVector<ImWchar> buf;
1584     buf.resize(filename_wsize + mode_wsize);
1585     ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, (wchar_t*)&buf[0], filename_wsize);
1586     ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, (wchar_t*)&buf[filename_wsize], mode_wsize);
1587     return ::_wfopen((const wchar_t*)&buf[0], (const wchar_t*)&buf[filename_wsize]);
1588 #else
1589     return fopen(filename, mode);
1590 #endif
1591 }
1592 
1593 // 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)1594 bool    ImFileClose(ImFileHandle f)     { return fclose(f) == 0; }
ImFileGetSize(ImFileHandle f)1595 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)1596 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)1597 ImU64   ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f)    { return fwrite(data, (size_t)sz, (size_t)count, f); }
1598 #endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
1599 
1600 // Helper: Load file content into memory
1601 // Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree()
1602 // This can't really be used with "rt" because fseek size won't match read size.
ImFileLoadToMemory(const char * filename,const char * mode,size_t * out_file_size,int padding_bytes)1603 void*   ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes)
1604 {
1605     IM_ASSERT(filename && mode);
1606     if (out_file_size)
1607         *out_file_size = 0;
1608 
1609     ImFileHandle f;
1610     if ((f = ImFileOpen(filename, mode)) == NULL)
1611         return NULL;
1612 
1613     size_t file_size = (size_t)ImFileGetSize(f);
1614     if (file_size == (size_t)-1)
1615     {
1616         ImFileClose(f);
1617         return NULL;
1618     }
1619 
1620     void* file_data = IM_ALLOC(file_size + padding_bytes);
1621     if (file_data == NULL)
1622     {
1623         ImFileClose(f);
1624         return NULL;
1625     }
1626     if (ImFileRead(file_data, 1, file_size, f) != file_size)
1627     {
1628         ImFileClose(f);
1629         IM_FREE(file_data);
1630         return NULL;
1631     }
1632     if (padding_bytes > 0)
1633         memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
1634 
1635     ImFileClose(f);
1636     if (out_file_size)
1637         *out_file_size = file_size;
1638 
1639     return file_data;
1640 }
1641 
1642 //-----------------------------------------------------------------------------
1643 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
1644 //-----------------------------------------------------------------------------
1645 
1646 // Convert UTF-8 to 32-bit character, process single character input.
1647 // A nearly-branchless UTF-8 decoder, based on work of Christopher Wellons (https://github.com/skeeto/branchless-utf8).
1648 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)1649 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1650 {
1651     static const char lengths[32] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0 };
1652     static const int masks[]  = { 0x00, 0x7f, 0x1f, 0x0f, 0x07 };
1653     static const uint32_t mins[] = { 0x400000, 0, 0x80, 0x800, 0x10000 };
1654     static const int shiftc[] = { 0, 18, 12, 6, 0 };
1655     static const int shifte[] = { 0, 6, 4, 2, 0 };
1656     int len = lengths[*(const unsigned char*)in_text >> 3];
1657     int wanted = len + !len;
1658 
1659     if (in_text_end == NULL)
1660         in_text_end = in_text + wanted; // Max length, nulls will be taken into account.
1661 
1662     // Copy at most 'len' bytes, stop copying at 0 or past in_text_end. Branch predictor does a good job here,
1663     // so it is fast even with excessive branching.
1664     unsigned char s[4];
1665     s[0] = in_text + 0 < in_text_end ? in_text[0] : 0;
1666     s[1] = in_text + 1 < in_text_end ? in_text[1] : 0;
1667     s[2] = in_text + 2 < in_text_end ? in_text[2] : 0;
1668     s[3] = in_text + 3 < in_text_end ? in_text[3] : 0;
1669 
1670     // Assume a four-byte character and load four bytes. Unused bits are shifted out.
1671     *out_char  = (uint32_t)(s[0] & masks[len]) << 18;
1672     *out_char |= (uint32_t)(s[1] & 0x3f) << 12;
1673     *out_char |= (uint32_t)(s[2] & 0x3f) <<  6;
1674     *out_char |= (uint32_t)(s[3] & 0x3f) <<  0;
1675     *out_char >>= shiftc[len];
1676 
1677     // Accumulate the various error conditions.
1678     int e = 0;
1679     e  = (*out_char < mins[len]) << 6; // non-canonical encoding
1680     e |= ((*out_char >> 11) == 0x1b) << 7;  // surrogate half?
1681     e |= (*out_char > IM_UNICODE_CODEPOINT_MAX) << 8;  // out of range?
1682     e |= (s[1] & 0xc0) >> 2;
1683     e |= (s[2] & 0xc0) >> 4;
1684     e |= (s[3]       ) >> 6;
1685     e ^= 0x2a; // top two bits of each tail byte correct?
1686     e >>= shifte[len];
1687 
1688     if (e)
1689     {
1690         // No bytes are consumed when *in_text == 0 || in_text == in_text_end.
1691         // One byte is consumed in case of invalid first byte of in_text.
1692         // All available bytes (at most `len` bytes) are consumed on incomplete/invalid second to last bytes.
1693         // Invalid or incomplete input may consume less bytes than wanted, therefore every byte has to be inspected in s.
1694         wanted = ImMin(wanted, !!s[0] + !!s[1] + !!s[2] + !!s[3]);
1695         *out_char = IM_UNICODE_CODEPOINT_INVALID;
1696     }
1697 
1698     return wanted;
1699 }
1700 
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1701 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1702 {
1703     ImWchar* buf_out = buf;
1704     ImWchar* buf_end = buf + buf_size;
1705     while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
1706     {
1707         unsigned int c;
1708         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1709         if (c == 0)
1710             break;
1711         *buf_out++ = (ImWchar)c;
1712     }
1713     *buf_out = 0;
1714     if (in_text_remaining)
1715         *in_text_remaining = in_text;
1716     return (int)(buf_out - buf);
1717 }
1718 
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1719 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1720 {
1721     int char_count = 0;
1722     while ((!in_text_end || in_text < in_text_end) && *in_text)
1723     {
1724         unsigned int c;
1725         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1726         if (c == 0)
1727             break;
1728         char_count++;
1729     }
1730     return char_count;
1731 }
1732 
1733 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8_inline(char * buf,int buf_size,unsigned int c)1734 static inline int ImTextCharToUtf8_inline(char* buf, int buf_size, unsigned int c)
1735 {
1736     if (c < 0x80)
1737     {
1738         buf[0] = (char)c;
1739         return 1;
1740     }
1741     if (c < 0x800)
1742     {
1743         if (buf_size < 2) return 0;
1744         buf[0] = (char)(0xc0 + (c >> 6));
1745         buf[1] = (char)(0x80 + (c & 0x3f));
1746         return 2;
1747     }
1748     if (c < 0x10000)
1749     {
1750         if (buf_size < 3) return 0;
1751         buf[0] = (char)(0xe0 + (c >> 12));
1752         buf[1] = (char)(0x80 + ((c >> 6) & 0x3f));
1753         buf[2] = (char)(0x80 + ((c ) & 0x3f));
1754         return 3;
1755     }
1756     if (c <= 0x10FFFF)
1757     {
1758         if (buf_size < 4) return 0;
1759         buf[0] = (char)(0xf0 + (c >> 18));
1760         buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1761         buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1762         buf[3] = (char)(0x80 + ((c ) & 0x3f));
1763         return 4;
1764     }
1765     // Invalid code point, the max unicode is 0x10FFFF
1766     return 0;
1767 }
1768 
ImTextCharToUtf8(char out_buf[5],unsigned int c)1769 const char* ImTextCharToUtf8(char out_buf[5], unsigned int c)
1770 {
1771     int count = ImTextCharToUtf8_inline(out_buf, 5, c);
1772     out_buf[count] = 0;
1773     return out_buf;
1774 }
1775 
1776 // Not optimal but we very rarely use this function.
ImTextCountUtf8BytesFromChar(const char * in_text,const char * in_text_end)1777 int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
1778 {
1779     unsigned int unused = 0;
1780     return ImTextCharFromUtf8(&unused, in_text, in_text_end);
1781 }
1782 
ImTextCountUtf8BytesFromChar(unsigned int c)1783 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1784 {
1785     if (c < 0x80) return 1;
1786     if (c < 0x800) return 2;
1787     if (c < 0x10000) return 3;
1788     if (c <= 0x10FFFF) return 4;
1789     return 3;
1790 }
1791 
ImTextStrToUtf8(char * out_buf,int out_buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1792 int ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1793 {
1794     char* buf_p = out_buf;
1795     const char* buf_end = out_buf + out_buf_size;
1796     while (buf_p < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
1797     {
1798         unsigned int c = (unsigned int)(*in_text++);
1799         if (c < 0x80)
1800             *buf_p++ = (char)c;
1801         else
1802             buf_p += ImTextCharToUtf8_inline(buf_p, (int)(buf_end - buf_p - 1), c);
1803     }
1804     *buf_p = 0;
1805     return (int)(buf_p - out_buf);
1806 }
1807 
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1808 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1809 {
1810     int bytes_count = 0;
1811     while ((!in_text_end || in_text < in_text_end) && *in_text)
1812     {
1813         unsigned int c = (unsigned int)(*in_text++);
1814         if (c < 0x80)
1815             bytes_count++;
1816         else
1817             bytes_count += ImTextCountUtf8BytesFromChar(c);
1818     }
1819     return bytes_count;
1820 }
1821 
1822 //-----------------------------------------------------------------------------
1823 // [SECTION] MISC HELPERS/UTILITIES (Color functions)
1824 // Note: The Convert functions are early design which are not consistent with other API.
1825 //-----------------------------------------------------------------------------
1826 
ImAlphaBlendColors(ImU32 col_a,ImU32 col_b)1827 IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b)
1828 {
1829     float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;
1830     int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);
1831     int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);
1832     int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);
1833     return IM_COL32(r, g, b, 0xFF);
1834 }
1835 
ColorConvertU32ToFloat4(ImU32 in)1836 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1837 {
1838     float s = 1.0f / 255.0f;
1839     return ImVec4(
1840         ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1841         ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1842         ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1843         ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1844 }
1845 
ColorConvertFloat4ToU32(const ImVec4 & in)1846 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1847 {
1848     ImU32 out;
1849     out  = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1850     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1851     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1852     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1853     return out;
1854 }
1855 
1856 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1857 // 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)1858 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1859 {
1860     float K = 0.f;
1861     if (g < b)
1862     {
1863         ImSwap(g, b);
1864         K = -1.f;
1865     }
1866     if (r < g)
1867     {
1868         ImSwap(r, g);
1869         K = -2.f / 6.f - K;
1870     }
1871 
1872     const float chroma = r - (g < b ? g : b);
1873     out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
1874     out_s = chroma / (r + 1e-20f);
1875     out_v = r;
1876 }
1877 
1878 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1879 // 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)1880 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1881 {
1882     if (s == 0.0f)
1883     {
1884         // gray
1885         out_r = out_g = out_b = v;
1886         return;
1887     }
1888 
1889     h = ImFmod(h, 1.0f) / (60.0f / 360.0f);
1890     int   i = (int)h;
1891     float f = h - (float)i;
1892     float p = v * (1.0f - s);
1893     float q = v * (1.0f - s * f);
1894     float t = v * (1.0f - s * (1.0f - f));
1895 
1896     switch (i)
1897     {
1898     case 0: out_r = v; out_g = t; out_b = p; break;
1899     case 1: out_r = q; out_g = v; out_b = p; break;
1900     case 2: out_r = p; out_g = v; out_b = t; break;
1901     case 3: out_r = p; out_g = q; out_b = v; break;
1902     case 4: out_r = t; out_g = p; out_b = v; break;
1903     case 5: default: out_r = v; out_g = p; out_b = q; break;
1904     }
1905 }
1906 
1907 //-----------------------------------------------------------------------------
1908 // [SECTION] ImGuiStorage
1909 // Helper: Key->value storage
1910 //-----------------------------------------------------------------------------
1911 
1912 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair> & data,ImGuiID key)1913 static ImGuiStorage::ImGuiStoragePair* LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair>& data, ImGuiID key)
1914 {
1915     ImGuiStorage::ImGuiStoragePair* first = data.Data;
1916     ImGuiStorage::ImGuiStoragePair* last = data.Data + data.Size;
1917     size_t count = (size_t)(last - first);
1918     while (count > 0)
1919     {
1920         size_t count2 = count >> 1;
1921         ImGuiStorage::ImGuiStoragePair* mid = first + count2;
1922         if (mid->key < key)
1923         {
1924             first = ++mid;
1925             count -= count2 + 1;
1926         }
1927         else
1928         {
1929             count = count2;
1930         }
1931     }
1932     return first;
1933 }
1934 
1935 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
BuildSortByKey()1936 void ImGuiStorage::BuildSortByKey()
1937 {
1938     struct StaticFunc
1939     {
1940         static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)
1941         {
1942             // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1943             if (((const ImGuiStoragePair*)lhs)->key > ((const ImGuiStoragePair*)rhs)->key) return +1;
1944             if (((const ImGuiStoragePair*)lhs)->key < ((const ImGuiStoragePair*)rhs)->key) return -1;
1945             return 0;
1946         }
1947     };
1948     if (Data.Size > 1)
1949         ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairCompareByID);
1950 }
1951 
GetInt(ImGuiID key,int default_val) const1952 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1953 {
1954     ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1955     if (it == Data.end() || it->key != key)
1956         return default_val;
1957     return it->val_i;
1958 }
1959 
GetBool(ImGuiID key,bool default_val) const1960 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1961 {
1962     return GetInt(key, default_val ? 1 : 0) != 0;
1963 }
1964 
GetFloat(ImGuiID key,float default_val) const1965 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1966 {
1967     ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1968     if (it == Data.end() || it->key != key)
1969         return default_val;
1970     return it->val_f;
1971 }
1972 
GetVoidPtr(ImGuiID key) const1973 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1974 {
1975     ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1976     if (it == Data.end() || it->key != key)
1977         return NULL;
1978     return it->val_p;
1979 }
1980 
1981 // 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)1982 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1983 {
1984     ImGuiStoragePair* it = LowerBound(Data, key);
1985     if (it == Data.end() || it->key != key)
1986         it = Data.insert(it, ImGuiStoragePair(key, default_val));
1987     return &it->val_i;
1988 }
1989 
GetBoolRef(ImGuiID key,bool default_val)1990 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1991 {
1992     return (bool*)GetIntRef(key, default_val ? 1 : 0);
1993 }
1994 
GetFloatRef(ImGuiID key,float default_val)1995 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1996 {
1997     ImGuiStoragePair* it = LowerBound(Data, key);
1998     if (it == Data.end() || it->key != key)
1999         it = Data.insert(it, ImGuiStoragePair(key, default_val));
2000     return &it->val_f;
2001 }
2002 
GetVoidPtrRef(ImGuiID key,void * default_val)2003 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
2004 {
2005     ImGuiStoragePair* it = LowerBound(Data, key);
2006     if (it == Data.end() || it->key != key)
2007         it = Data.insert(it, ImGuiStoragePair(key, default_val));
2008     return &it->val_p;
2009 }
2010 
2011 // 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)2012 void ImGuiStorage::SetInt(ImGuiID key, int val)
2013 {
2014     ImGuiStoragePair* it = LowerBound(Data, key);
2015     if (it == Data.end() || it->key != key)
2016     {
2017         Data.insert(it, ImGuiStoragePair(key, val));
2018         return;
2019     }
2020     it->val_i = val;
2021 }
2022 
SetBool(ImGuiID key,bool val)2023 void ImGuiStorage::SetBool(ImGuiID key, bool val)
2024 {
2025     SetInt(key, val ? 1 : 0);
2026 }
2027 
SetFloat(ImGuiID key,float val)2028 void ImGuiStorage::SetFloat(ImGuiID key, float val)
2029 {
2030     ImGuiStoragePair* it = LowerBound(Data, key);
2031     if (it == Data.end() || it->key != key)
2032     {
2033         Data.insert(it, ImGuiStoragePair(key, val));
2034         return;
2035     }
2036     it->val_f = val;
2037 }
2038 
SetVoidPtr(ImGuiID key,void * val)2039 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
2040 {
2041     ImGuiStoragePair* it = LowerBound(Data, key);
2042     if (it == Data.end() || it->key != key)
2043     {
2044         Data.insert(it, ImGuiStoragePair(key, val));
2045         return;
2046     }
2047     it->val_p = val;
2048 }
2049 
SetAllInt(int v)2050 void ImGuiStorage::SetAllInt(int v)
2051 {
2052     for (int i = 0; i < Data.Size; i++)
2053         Data[i].val_i = v;
2054 }
2055 
2056 //-----------------------------------------------------------------------------
2057 // [SECTION] ImGuiTextFilter
2058 //-----------------------------------------------------------------------------
2059 
2060 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)2061 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
2062 {
2063     if (default_filter)
2064     {
2065         ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
2066         Build();
2067     }
2068     else
2069     {
2070         InputBuf[0] = 0;
2071         CountGrep = 0;
2072     }
2073 }
2074 
Draw(const char * label,float width)2075 bool ImGuiTextFilter::Draw(const char* label, float width)
2076 {
2077     if (width != 0.0f)
2078         ImGui::SetNextItemWidth(width);
2079     bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
2080     if (value_changed)
2081         Build();
2082     return value_changed;
2083 }
2084 
split(char separator,ImVector<ImGuiTextRange> * out) const2085 void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const
2086 {
2087     out->resize(0);
2088     const char* wb = b;
2089     const char* we = wb;
2090     while (we < e)
2091     {
2092         if (*we == separator)
2093         {
2094             out->push_back(ImGuiTextRange(wb, we));
2095             wb = we + 1;
2096         }
2097         we++;
2098     }
2099     if (wb != we)
2100         out->push_back(ImGuiTextRange(wb, we));
2101 }
2102 
Build()2103 void ImGuiTextFilter::Build()
2104 {
2105     Filters.resize(0);
2106     ImGuiTextRange input_range(InputBuf, InputBuf + strlen(InputBuf));
2107     input_range.split(',', &Filters);
2108 
2109     CountGrep = 0;
2110     for (int i = 0; i != Filters.Size; i++)
2111     {
2112         ImGuiTextRange& f = Filters[i];
2113         while (f.b < f.e && ImCharIsBlankA(f.b[0]))
2114             f.b++;
2115         while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
2116             f.e--;
2117         if (f.empty())
2118             continue;
2119         if (Filters[i].b[0] != '-')
2120             CountGrep += 1;
2121     }
2122 }
2123 
PassFilter(const char * text,const char * text_end) const2124 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
2125 {
2126     if (Filters.empty())
2127         return true;
2128 
2129     if (text == NULL)
2130         text = "";
2131 
2132     for (int i = 0; i != Filters.Size; i++)
2133     {
2134         const ImGuiTextRange& f = Filters[i];
2135         if (f.empty())
2136             continue;
2137         if (f.b[0] == '-')
2138         {
2139             // Subtract
2140             if (ImStristr(text, text_end, f.b + 1, f.e) != NULL)
2141                 return false;
2142         }
2143         else
2144         {
2145             // Grep
2146             if (ImStristr(text, text_end, f.b, f.e) != NULL)
2147                 return true;
2148         }
2149     }
2150 
2151     // Implicit * grep
2152     if (CountGrep == 0)
2153         return true;
2154 
2155     return false;
2156 }
2157 
2158 //-----------------------------------------------------------------------------
2159 // [SECTION] ImGuiTextBuffer
2160 //-----------------------------------------------------------------------------
2161 
2162 // On some platform vsnprintf() takes va_list by reference and modifies it.
2163 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
2164 #ifndef va_copy
2165 #if defined(__GNUC__) || defined(__clang__)
2166 #define va_copy(dest, src) __builtin_va_copy(dest, src)
2167 #else
2168 #define va_copy(dest, src) (dest = src)
2169 #endif
2170 #endif
2171 
2172 char ImGuiTextBuffer::EmptyString[1] = { 0 };
2173 
append(const char * str,const char * str_end)2174 void ImGuiTextBuffer::append(const char* str, const char* str_end)
2175 {
2176     int len = str_end ? (int)(str_end - str) : (int)strlen(str);
2177 
2178     // Add zero-terminator the first time
2179     const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2180     const int needed_sz = write_off + len;
2181     if (write_off + len >= Buf.Capacity)
2182     {
2183         int new_capacity = Buf.Capacity * 2;
2184         Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2185     }
2186 
2187     Buf.resize(needed_sz);
2188     memcpy(&Buf[write_off - 1], str, (size_t)len);
2189     Buf[write_off - 1 + len] = 0;
2190 }
2191 
appendf(const char * fmt,...)2192 void ImGuiTextBuffer::appendf(const char* fmt, ...)
2193 {
2194     va_list args;
2195     va_start(args, fmt);
2196     appendfv(fmt, args);
2197     va_end(args);
2198 }
2199 
2200 // Helper: Text buffer for logging/accumulating text
appendfv(const char * fmt,va_list args)2201 void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
2202 {
2203     va_list args_copy;
2204     va_copy(args_copy, args);
2205 
2206     int len = ImFormatStringV(NULL, 0, fmt, args);         // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
2207     if (len <= 0)
2208     {
2209         va_end(args_copy);
2210         return;
2211     }
2212 
2213     // Add zero-terminator the first time
2214     const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2215     const int needed_sz = write_off + len;
2216     if (write_off + len >= Buf.Capacity)
2217     {
2218         int new_capacity = Buf.Capacity * 2;
2219         Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2220     }
2221 
2222     Buf.resize(needed_sz);
2223     ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
2224     va_end(args_copy);
2225 }
2226 
2227 //-----------------------------------------------------------------------------
2228 // [SECTION] ImGuiListClipper
2229 // This is currently not as flexible/powerful as it should be and really confusing/spaghetti, mostly because we changed
2230 // the API mid-way through development and support two ways to using the clipper, needs some rework (see TODO)
2231 //-----------------------------------------------------------------------------
2232 
2233 // FIXME-TABLE: This prevents us from using ImGuiListClipper _inside_ a table cell.
2234 // The problem we have is that without a Begin/End scheme for rows using the clipper is ambiguous.
GetSkipItemForListClipping()2235 static bool GetSkipItemForListClipping()
2236 {
2237     ImGuiContext& g = *GImGui;
2238     return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems);
2239 }
2240 
2241 // Helper to calculate coarse clipping of large list of evenly sized items.
2242 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
2243 // 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)2244 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
2245 {
2246     ImGuiContext& g = *GImGui;
2247     ImGuiWindow* window = g.CurrentWindow;
2248     if (g.LogEnabled)
2249     {
2250         // If logging is active, do not perform any clipping
2251         *out_items_display_start = 0;
2252         *out_items_display_end = items_count;
2253         return;
2254     }
2255     if (GetSkipItemForListClipping())
2256     {
2257         *out_items_display_start = *out_items_display_end = 0;
2258         return;
2259     }
2260 
2261     // We create the union of the ClipRect and the scoring rect which at worst should be 1 page away from ClipRect
2262     ImRect unclipped_rect = window->ClipRect;
2263     if (g.NavMoveScoringItems)
2264         unclipped_rect.Add(g.NavScoringRect);
2265     if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId)
2266         unclipped_rect.Add(ImRect(window->Pos + window->NavRectRel[0].Min, window->Pos + window->NavRectRel[0].Max)); // Could store and use NavJustMovedToRectRel
2267 
2268     const ImVec2 pos = window->DC.CursorPos;
2269     int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
2270     int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
2271 
2272     // When performing a navigation request, ensure we have one item extra in the direction we are moving to
2273     if (g.NavMoveScoringItems && g.NavMoveClipDir == ImGuiDir_Up)
2274         start--;
2275     if (g.NavMoveScoringItems && g.NavMoveClipDir == ImGuiDir_Down)
2276         end++;
2277 
2278     start = ImClamp(start, 0, items_count);
2279     end = ImClamp(end + 1, start, items_count);
2280     *out_items_display_start = start;
2281     *out_items_display_end = end;
2282 }
2283 
SetCursorPosYAndSetupForPrevLine(float pos_y,float line_height)2284 static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height)
2285 {
2286     // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
2287     // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
2288     // The clipper should probably have a 4th step to display the last item in a regular manner.
2289     ImGuiContext& g = *GImGui;
2290     ImGuiWindow* window = g.CurrentWindow;
2291     float off_y = pos_y - window->DC.CursorPos.y;
2292     window->DC.CursorPos.y = pos_y;
2293     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y);
2294     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.
2295     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.
2296     if (ImGuiOldColumns* columns = window->DC.CurrentColumns)
2297         columns->LineMinY = window->DC.CursorPos.y;                         // Setting this so that cell Y position are set properly
2298     if (ImGuiTable* table = g.CurrentTable)
2299     {
2300         if (table->IsInsideRow)
2301             ImGui::TableEndRow(table);
2302         table->RowPosY2 = window->DC.CursorPos.y;
2303         const int row_increase = (int)((off_y / line_height) + 0.5f);
2304         //table->CurrentRow += row_increase; // Can't do without fixing TableEndRow()
2305         table->RowBgColorCounter += row_increase;
2306     }
2307 }
2308 
ImGuiListClipper()2309 ImGuiListClipper::ImGuiListClipper()
2310 {
2311     memset(this, 0, sizeof(*this));
2312     ItemsCount = -1;
2313 }
2314 
~ImGuiListClipper()2315 ImGuiListClipper::~ImGuiListClipper()
2316 {
2317     IM_ASSERT(ItemsCount == -1 && "Forgot to call End(), or to Step() until false?");
2318 }
2319 
2320 // Use case A: Begin() called from constructor with items_height<0, then called again from Step() in StepNo 1
2321 // Use case B: Begin() called from constructor with items_height>0
2322 // FIXME-LEGACY: Ideally we should remove the Begin/End functions but they are part of the legacy API we still support. This is why some of the code in Step() calling Begin() and reassign some fields, spaghetti style.
Begin(int items_count,float items_height)2323 void ImGuiListClipper::Begin(int items_count, float items_height)
2324 {
2325     ImGuiContext& g = *GImGui;
2326     ImGuiWindow* window = g.CurrentWindow;
2327 
2328     if (ImGuiTable* table = g.CurrentTable)
2329         if (table->IsInsideRow)
2330             ImGui::TableEndRow(table);
2331 
2332     StartPosY = window->DC.CursorPos.y;
2333     ItemsHeight = items_height;
2334     ItemsCount = items_count;
2335     ItemsFrozen = 0;
2336     StepNo = 0;
2337     DisplayStart = -1;
2338     DisplayEnd = 0;
2339 }
2340 
End()2341 void ImGuiListClipper::End()
2342 {
2343     if (ItemsCount < 0) // Already ended
2344         return;
2345 
2346     // 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.
2347     if (ItemsCount < INT_MAX && DisplayStart >= 0)
2348         SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight);
2349     ItemsCount = -1;
2350     StepNo = 3;
2351 }
2352 
Step()2353 bool ImGuiListClipper::Step()
2354 {
2355     ImGuiContext& g = *GImGui;
2356     ImGuiWindow* window = g.CurrentWindow;
2357 
2358     ImGuiTable* table = g.CurrentTable;
2359     if (table && table->IsInsideRow)
2360         ImGui::TableEndRow(table);
2361 
2362     // No items
2363     if (ItemsCount == 0 || GetSkipItemForListClipping())
2364     {
2365         End();
2366         return false;
2367     }
2368 
2369     // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height)
2370     if (StepNo == 0)
2371     {
2372         // While we are in frozen row state, keep displaying items one by one, unclipped
2373         // FIXME: Could be stored as a table-agnostic state.
2374         if (table != NULL && !table->IsUnfrozenRows)
2375         {
2376             DisplayStart = ItemsFrozen;
2377             DisplayEnd = ItemsFrozen + 1;
2378             ItemsFrozen++;
2379             return true;
2380         }
2381 
2382         StartPosY = window->DC.CursorPos.y;
2383         if (ItemsHeight <= 0.0f)
2384         {
2385             // Submit the first item so we can measure its height (generally it is 0..1)
2386             DisplayStart = ItemsFrozen;
2387             DisplayEnd = ItemsFrozen + 1;
2388             StepNo = 1;
2389             return true;
2390         }
2391 
2392         // Already has item height (given by user in Begin): skip to calculating step
2393         DisplayStart = DisplayEnd;
2394         StepNo = 2;
2395     }
2396 
2397     // Step 1: the clipper infer height from first element
2398     if (StepNo == 1)
2399     {
2400         IM_ASSERT(ItemsHeight <= 0.0f);
2401         if (table)
2402         {
2403             const float pos_y1 = table->RowPosY1;   // Using this instead of StartPosY to handle clipper straddling the frozen row
2404             const float pos_y2 = table->RowPosY2;   // Using this instead of CursorPos.y to take account of tallest cell.
2405             ItemsHeight = pos_y2 - pos_y1;
2406             window->DC.CursorPos.y = pos_y2;
2407         }
2408         else
2409         {
2410             ItemsHeight = window->DC.CursorPos.y - StartPosY;
2411         }
2412         IM_ASSERT(ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!");
2413         StepNo = 2;
2414     }
2415 
2416     // Reached end of list
2417     if (DisplayEnd >= ItemsCount)
2418     {
2419         End();
2420         return false;
2421     }
2422 
2423     // Step 2: calculate the actual range of elements to display, and position the cursor before the first element
2424     if (StepNo == 2)
2425     {
2426         IM_ASSERT(ItemsHeight > 0.0f);
2427 
2428         int already_submitted = DisplayEnd;
2429         ImGui::CalcListClipping(ItemsCount - already_submitted, ItemsHeight, &DisplayStart, &DisplayEnd);
2430         DisplayStart += already_submitted;
2431         DisplayEnd += already_submitted;
2432 
2433         // Seek cursor
2434         if (DisplayStart > already_submitted)
2435             SetCursorPosYAndSetupForPrevLine(StartPosY + (DisplayStart - ItemsFrozen) * ItemsHeight, ItemsHeight);
2436 
2437         StepNo = 3;
2438         return true;
2439     }
2440 
2441     // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd),
2442     // Advance the cursor to the end of the list and then returns 'false' to end the loop.
2443     if (StepNo == 3)
2444     {
2445         // Seek cursor
2446         if (ItemsCount < INT_MAX)
2447             SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight); // advance cursor
2448         ItemsCount = -1;
2449         return false;
2450     }
2451 
2452     IM_ASSERT(0);
2453     return false;
2454 }
2455 
2456 //-----------------------------------------------------------------------------
2457 // [SECTION] STYLING
2458 //-----------------------------------------------------------------------------
2459 
GetStyle()2460 ImGuiStyle& ImGui::GetStyle()
2461 {
2462     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
2463     return GImGui->Style;
2464 }
2465 
GetColorU32(ImGuiCol idx,float alpha_mul)2466 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
2467 {
2468     ImGuiStyle& style = GImGui->Style;
2469     ImVec4 c = style.Colors[idx];
2470     c.w *= style.Alpha * alpha_mul;
2471     return ColorConvertFloat4ToU32(c);
2472 }
2473 
GetColorU32(const ImVec4 & col)2474 ImU32 ImGui::GetColorU32(const ImVec4& col)
2475 {
2476     ImGuiStyle& style = GImGui->Style;
2477     ImVec4 c = col;
2478     c.w *= style.Alpha;
2479     return ColorConvertFloat4ToU32(c);
2480 }
2481 
GetStyleColorVec4(ImGuiCol idx)2482 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
2483 {
2484     ImGuiStyle& style = GImGui->Style;
2485     return style.Colors[idx];
2486 }
2487 
GetColorU32(ImU32 col)2488 ImU32 ImGui::GetColorU32(ImU32 col)
2489 {
2490     ImGuiStyle& style = GImGui->Style;
2491     if (style.Alpha >= 1.0f)
2492         return col;
2493     ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
2494     a = (ImU32)(a * style.Alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
2495     return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
2496 }
2497 
2498 // 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)2499 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
2500 {
2501     ImGuiContext& g = *GImGui;
2502     ImGuiColorMod backup;
2503     backup.Col = idx;
2504     backup.BackupValue = g.Style.Colors[idx];
2505     g.ColorStack.push_back(backup);
2506     g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
2507 }
2508 
PushStyleColor(ImGuiCol idx,const ImVec4 & col)2509 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
2510 {
2511     ImGuiContext& g = *GImGui;
2512     ImGuiColorMod backup;
2513     backup.Col = idx;
2514     backup.BackupValue = g.Style.Colors[idx];
2515     g.ColorStack.push_back(backup);
2516     g.Style.Colors[idx] = col;
2517 }
2518 
PopStyleColor(int count)2519 void ImGui::PopStyleColor(int count)
2520 {
2521     ImGuiContext& g = *GImGui;
2522     while (count > 0)
2523     {
2524         ImGuiColorMod& backup = g.ColorStack.back();
2525         g.Style.Colors[backup.Col] = backup.BackupValue;
2526         g.ColorStack.pop_back();
2527         count--;
2528     }
2529 }
2530 
2531 struct ImGuiStyleVarInfo
2532 {
2533     ImGuiDataType   Type;
2534     ImU32           Count;
2535     ImU32           Offset;
GetVarPtrImGuiStyleVarInfo2536     void*           GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
2537 };
2538 
2539 static const ImGuiStyleVarInfo GStyleVarInfo[] =
2540 {
2541     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) },               // ImGuiStyleVar_Alpha
2542     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, DisabledAlpha) },       // ImGuiStyleVar_DisabledAlpha
2543     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) },       // ImGuiStyleVar_WindowPadding
2544     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) },      // ImGuiStyleVar_WindowRounding
2545     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) },    // ImGuiStyleVar_WindowBorderSize
2546     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) },       // ImGuiStyleVar_WindowMinSize
2547     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) },    // ImGuiStyleVar_WindowTitleAlign
2548     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) },       // ImGuiStyleVar_ChildRounding
2549     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) },     // ImGuiStyleVar_ChildBorderSize
2550     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) },       // ImGuiStyleVar_PopupRounding
2551     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) },     // ImGuiStyleVar_PopupBorderSize
2552     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) },        // ImGuiStyleVar_FramePadding
2553     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) },       // ImGuiStyleVar_FrameRounding
2554     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) },     // ImGuiStyleVar_FrameBorderSize
2555     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) },         // ImGuiStyleVar_ItemSpacing
2556     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) },    // ImGuiStyleVar_ItemInnerSpacing
2557     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) },       // ImGuiStyleVar_IndentSpacing
2558     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, CellPadding) },         // ImGuiStyleVar_CellPadding
2559     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) },       // ImGuiStyleVar_ScrollbarSize
2560     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) },   // ImGuiStyleVar_ScrollbarRounding
2561     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) },         // ImGuiStyleVar_GrabMinSize
2562     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) },        // ImGuiStyleVar_GrabRounding
2563     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) },         // ImGuiStyleVar_TabRounding
2564     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) },     // ImGuiStyleVar_ButtonTextAlign
2565     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
2566 };
2567 
GetStyleVarInfo(ImGuiStyleVar idx)2568 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
2569 {
2570     IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
2571     IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
2572     return &GStyleVarInfo[idx];
2573 }
2574 
PushStyleVar(ImGuiStyleVar idx,float val)2575 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
2576 {
2577     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
2578     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
2579     {
2580         ImGuiContext& g = *GImGui;
2581         float* pvar = (float*)var_info->GetVarPtr(&g.Style);
2582         g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
2583         *pvar = val;
2584         return;
2585     }
2586     IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!");
2587 }
2588 
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)2589 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
2590 {
2591     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
2592     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
2593     {
2594         ImGuiContext& g = *GImGui;
2595         ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
2596         g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
2597         *pvar = val;
2598         return;
2599     }
2600     IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!");
2601 }
2602 
PopStyleVar(int count)2603 void ImGui::PopStyleVar(int count)
2604 {
2605     ImGuiContext& g = *GImGui;
2606     while (count > 0)
2607     {
2608         // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
2609         ImGuiStyleMod& backup = g.StyleVarStack.back();
2610         const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
2611         void* data = info->GetVarPtr(&g.Style);
2612         if (info->Type == ImGuiDataType_Float && info->Count == 1)      { ((float*)data)[0] = backup.BackupFloat[0]; }
2613         else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
2614         g.StyleVarStack.pop_back();
2615         count--;
2616     }
2617 }
2618 
GetStyleColorName(ImGuiCol idx)2619 const char* ImGui::GetStyleColorName(ImGuiCol idx)
2620 {
2621     // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
2622     switch (idx)
2623     {
2624     case ImGuiCol_Text: return "Text";
2625     case ImGuiCol_TextDisabled: return "TextDisabled";
2626     case ImGuiCol_WindowBg: return "WindowBg";
2627     case ImGuiCol_ChildBg: return "ChildBg";
2628     case ImGuiCol_PopupBg: return "PopupBg";
2629     case ImGuiCol_Border: return "Border";
2630     case ImGuiCol_BorderShadow: return "BorderShadow";
2631     case ImGuiCol_FrameBg: return "FrameBg";
2632     case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
2633     case ImGuiCol_FrameBgActive: return "FrameBgActive";
2634     case ImGuiCol_TitleBg: return "TitleBg";
2635     case ImGuiCol_TitleBgActive: return "TitleBgActive";
2636     case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
2637     case ImGuiCol_MenuBarBg: return "MenuBarBg";
2638     case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
2639     case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
2640     case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
2641     case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
2642     case ImGuiCol_CheckMark: return "CheckMark";
2643     case ImGuiCol_SliderGrab: return "SliderGrab";
2644     case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
2645     case ImGuiCol_Button: return "Button";
2646     case ImGuiCol_ButtonHovered: return "ButtonHovered";
2647     case ImGuiCol_ButtonActive: return "ButtonActive";
2648     case ImGuiCol_Header: return "Header";
2649     case ImGuiCol_HeaderHovered: return "HeaderHovered";
2650     case ImGuiCol_HeaderActive: return "HeaderActive";
2651     case ImGuiCol_Separator: return "Separator";
2652     case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
2653     case ImGuiCol_SeparatorActive: return "SeparatorActive";
2654     case ImGuiCol_ResizeGrip: return "ResizeGrip";
2655     case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
2656     case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
2657     case ImGuiCol_Tab: return "Tab";
2658     case ImGuiCol_TabHovered: return "TabHovered";
2659     case ImGuiCol_TabActive: return "TabActive";
2660     case ImGuiCol_TabUnfocused: return "TabUnfocused";
2661     case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive";
2662     case ImGuiCol_PlotLines: return "PlotLines";
2663     case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
2664     case ImGuiCol_PlotHistogram: return "PlotHistogram";
2665     case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
2666     case ImGuiCol_TableHeaderBg: return "TableHeaderBg";
2667     case ImGuiCol_TableBorderStrong: return "TableBorderStrong";
2668     case ImGuiCol_TableBorderLight: return "TableBorderLight";
2669     case ImGuiCol_TableRowBg: return "TableRowBg";
2670     case ImGuiCol_TableRowBgAlt: return "TableRowBgAlt";
2671     case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
2672     case ImGuiCol_DragDropTarget: return "DragDropTarget";
2673     case ImGuiCol_NavHighlight: return "NavHighlight";
2674     case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
2675     case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
2676     case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
2677     }
2678     IM_ASSERT(0);
2679     return "Unknown";
2680 }
2681 
2682 
2683 //-----------------------------------------------------------------------------
2684 // [SECTION] RENDER HELPERS
2685 // Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change,
2686 // we need a nicer separation between low-level functions and high-level functions relying on the ImGui context.
2687 // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context.
2688 //-----------------------------------------------------------------------------
2689 
FindRenderedTextEnd(const char * text,const char * text_end)2690 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2691 {
2692     const char* text_display_end = text;
2693     if (!text_end)
2694         text_end = (const char*)-1;
2695 
2696     while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2697         text_display_end++;
2698     return text_display_end;
2699 }
2700 
2701 // Internal ImGui functions to render text
2702 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)2703 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2704 {
2705     ImGuiContext& g = *GImGui;
2706     ImGuiWindow* window = g.CurrentWindow;
2707 
2708     // Hide anything after a '##' string
2709     const char* text_display_end;
2710     if (hide_text_after_hash)
2711     {
2712         text_display_end = FindRenderedTextEnd(text, text_end);
2713     }
2714     else
2715     {
2716         if (!text_end)
2717             text_end = text + strlen(text); // FIXME-OPT
2718         text_display_end = text_end;
2719     }
2720 
2721     if (text != text_display_end)
2722     {
2723         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2724         if (g.LogEnabled)
2725             LogRenderedText(&pos, text, text_display_end);
2726     }
2727 }
2728 
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)2729 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2730 {
2731     ImGuiContext& g = *GImGui;
2732     ImGuiWindow* window = g.CurrentWindow;
2733 
2734     if (!text_end)
2735         text_end = text + strlen(text); // FIXME-OPT
2736 
2737     if (text != text_end)
2738     {
2739         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2740         if (g.LogEnabled)
2741             LogRenderedText(&pos, text, text_end);
2742     }
2743 }
2744 
2745 // Default clip_rect uses (pos_min,pos_max)
2746 // 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)2747 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)
2748 {
2749     // Perform CPU side clipping for single clipped element to avoid using scissor state
2750     ImVec2 pos = pos_min;
2751     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
2752 
2753     const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
2754     const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
2755     bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2756     if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
2757         need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2758 
2759     // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
2760     if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
2761     if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
2762 
2763     // Render
2764     if (need_clipping)
2765     {
2766         ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2767         draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2768     }
2769     else
2770     {
2771         draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2772     }
2773 }
2774 
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)2775 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)
2776 {
2777     // Hide anything after a '##' string
2778     const char* text_display_end = FindRenderedTextEnd(text, text_end);
2779     const int text_len = (int)(text_display_end - text);
2780     if (text_len == 0)
2781         return;
2782 
2783     ImGuiContext& g = *GImGui;
2784     ImGuiWindow* window = g.CurrentWindow;
2785     RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
2786     if (g.LogEnabled)
2787         LogRenderedText(&pos_min, text, text_display_end);
2788 }
2789 
2790 
2791 // Another overly complex function until we reorganize everything into a nice all-in-one helper.
2792 // 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.
2793 // 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)2794 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)
2795 {
2796     ImGuiContext& g = *GImGui;
2797     if (text_end_full == NULL)
2798         text_end_full = FindRenderedTextEnd(text);
2799     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);
2800 
2801     //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));
2802     //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));
2803     //draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255));
2804     // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
2805     if (text_size.x > pos_max.x - pos_min.x)
2806     {
2807         // Hello wo...
2808         // |       |   |
2809         // min   max   ellipsis_max
2810         //          <-> this is generally some padding value
2811 
2812         const ImFont* font = draw_list->_Data->Font;
2813         const float font_size = draw_list->_Data->FontSize;
2814         const char* text_end_ellipsis = NULL;
2815 
2816         ImWchar ellipsis_char = font->EllipsisChar;
2817         int ellipsis_char_count = 1;
2818         if (ellipsis_char == (ImWchar)-1)
2819         {
2820             ellipsis_char = font->DotChar;
2821             ellipsis_char_count = 3;
2822         }
2823         const ImFontGlyph* glyph = font->FindGlyph(ellipsis_char);
2824 
2825         float ellipsis_glyph_width = glyph->X1;                 // Width of the glyph with no padding on either side
2826         float ellipsis_total_width = ellipsis_glyph_width;      // Full width of entire ellipsis
2827 
2828         if (ellipsis_char_count > 1)
2829         {
2830             // Full ellipsis size without free spacing after it.
2831             const float spacing_between_dots = 1.0f * (draw_list->_Data->FontSize / font->FontSize);
2832             ellipsis_glyph_width = glyph->X1 - glyph->X0 + spacing_between_dots;
2833             ellipsis_total_width = ellipsis_glyph_width * (float)ellipsis_char_count - spacing_between_dots;
2834         }
2835 
2836         // We can now claim the space between pos_max.x and ellipsis_max.x
2837         const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_total_width) - pos_min.x, 1.0f);
2838         float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
2839         if (text == text_end_ellipsis && text_end_ellipsis < text_end_full)
2840         {
2841             // Always display at least 1 character if there's no room for character + ellipsis
2842             text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full);
2843             text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x;
2844         }
2845         while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1]))
2846         {
2847             // 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)
2848             text_end_ellipsis--;
2849             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
2850         }
2851 
2852         // Render text, render ellipsis
2853         RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
2854         float ellipsis_x = pos_min.x + text_size_clipped_x;
2855         if (ellipsis_x + ellipsis_total_width <= ellipsis_max_x)
2856             for (int i = 0; i < ellipsis_char_count; i++)
2857             {
2858                 font->RenderChar(draw_list, font_size, ImVec2(ellipsis_x, pos_min.y), GetColorU32(ImGuiCol_Text), ellipsis_char);
2859                 ellipsis_x += ellipsis_glyph_width;
2860             }
2861     }
2862     else
2863     {
2864         RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));
2865     }
2866 
2867     if (g.LogEnabled)
2868         LogRenderedText(&pos_min, text, text_end_full);
2869 }
2870 
2871 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)2872 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2873 {
2874     ImGuiContext& g = *GImGui;
2875     ImGuiWindow* window = g.CurrentWindow;
2876     window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2877     const float border_size = g.Style.FrameBorderSize;
2878     if (border && border_size > 0.0f)
2879     {
2880         window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);
2881         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
2882     }
2883 }
2884 
RenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding)2885 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
2886 {
2887     ImGuiContext& g = *GImGui;
2888     ImGuiWindow* window = g.CurrentWindow;
2889     const float border_size = g.Style.FrameBorderSize;
2890     if (border_size > 0.0f)
2891     {
2892         window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);
2893         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
2894     }
2895 }
2896 
RenderNavHighlight(const ImRect & bb,ImGuiID id,ImGuiNavHighlightFlags flags)2897 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
2898 {
2899     ImGuiContext& g = *GImGui;
2900     if (id != g.NavId)
2901         return;
2902     if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
2903         return;
2904     ImGuiWindow* window = g.CurrentWindow;
2905     if (window->DC.NavHideHighlightOneFrame)
2906         return;
2907 
2908     float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
2909     ImRect display_rect = bb;
2910     display_rect.ClipWith(window->ClipRect);
2911     if (flags & ImGuiNavHighlightFlags_TypeDefault)
2912     {
2913         const float THICKNESS = 2.0f;
2914         const float DISTANCE = 3.0f + THICKNESS * 0.5f;
2915         display_rect.Expand(ImVec2(DISTANCE, DISTANCE));
2916         bool fully_visible = window->ClipRect.Contains(display_rect);
2917         if (!fully_visible)
2918             window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
2919         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, 0, THICKNESS);
2920         if (!fully_visible)
2921             window->DrawList->PopClipRect();
2922     }
2923     if (flags & ImGuiNavHighlightFlags_TypeThin)
2924     {
2925         window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, 0, 1.0f);
2926     }
2927 }
2928 
2929 //-----------------------------------------------------------------------------
2930 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
2931 //-----------------------------------------------------------------------------
2932 
2933 // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
ImGuiWindow(ImGuiContext * context,const char * name)2934 ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) : DrawListInst(NULL)
2935 {
2936     memset(this, 0, sizeof(*this));
2937     Name = ImStrdup(name);
2938     NameBufLen = (int)strlen(name) + 1;
2939     ID = ImHashStr(name);
2940     IDStack.push_back(ID);
2941     MoveId = GetID("#MOVE");
2942     ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
2943     ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
2944     AutoFitFramesX = AutoFitFramesY = -1;
2945     AutoPosLastDirection = ImGuiDir_None;
2946     SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
2947     SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
2948     LastFrameActive = -1;
2949     LastTimeActive = -1.0f;
2950     FontWindowScale = 1.0f;
2951     SettingsOffset = -1;
2952     DrawList = &DrawListInst;
2953     DrawList->_Data = &context->DrawListSharedData;
2954     DrawList->_OwnerName = Name;
2955 }
2956 
~ImGuiWindow()2957 ImGuiWindow::~ImGuiWindow()
2958 {
2959     IM_ASSERT(DrawList == &DrawListInst);
2960     IM_DELETE(Name);
2961     ColumnsStorage.clear_destruct();
2962 }
2963 
GetID(const char * str,const char * str_end)2964 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
2965 {
2966     ImGuiID seed = IDStack.back();
2967     ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2968     ImGui::KeepAliveID(id);
2969 #ifdef IMGUI_ENABLE_TEST_ENGINE
2970     ImGuiContext& g = *GImGui;
2971     IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
2972 #endif
2973     return id;
2974 }
2975 
GetID(const void * ptr)2976 ImGuiID ImGuiWindow::GetID(const void* ptr)
2977 {
2978     ImGuiID seed = IDStack.back();
2979     ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
2980     ImGui::KeepAliveID(id);
2981 #ifdef IMGUI_ENABLE_TEST_ENGINE
2982     ImGuiContext& g = *GImGui;
2983     IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr);
2984 #endif
2985     return id;
2986 }
2987 
GetID(int n)2988 ImGuiID ImGuiWindow::GetID(int n)
2989 {
2990     ImGuiID seed = IDStack.back();
2991     ImGuiID id = ImHashData(&n, sizeof(n), seed);
2992     ImGui::KeepAliveID(id);
2993 #ifdef IMGUI_ENABLE_TEST_ENGINE
2994     ImGuiContext& g = *GImGui;
2995     IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n);
2996 #endif
2997     return id;
2998 }
2999 
GetIDNoKeepAlive(const char * str,const char * str_end)3000 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
3001 {
3002     ImGuiID seed = IDStack.back();
3003     ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
3004 #ifdef IMGUI_ENABLE_TEST_ENGINE
3005     ImGuiContext& g = *GImGui;
3006     IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
3007 #endif
3008     return id;
3009 }
3010 
GetIDNoKeepAlive(const void * ptr)3011 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
3012 {
3013     ImGuiID seed = IDStack.back();
3014     ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
3015 #ifdef IMGUI_ENABLE_TEST_ENGINE
3016     ImGuiContext& g = *GImGui;
3017     IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr);
3018 #endif
3019     return id;
3020 }
3021 
GetIDNoKeepAlive(int n)3022 ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n)
3023 {
3024     ImGuiID seed = IDStack.back();
3025     ImGuiID id = ImHashData(&n, sizeof(n), seed);
3026 #ifdef IMGUI_ENABLE_TEST_ENGINE
3027     ImGuiContext& g = *GImGui;
3028     IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n);
3029 #endif
3030     return id;
3031 }
3032 
3033 // This is only used in rare/specific situations to manufacture an ID out of nowhere.
GetIDFromRectangle(const ImRect & r_abs)3034 ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
3035 {
3036     ImGuiID seed = IDStack.back();
3037     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) };
3038     ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
3039     ImGui::KeepAliveID(id);
3040     return id;
3041 }
3042 
SetCurrentWindow(ImGuiWindow * window)3043 static void SetCurrentWindow(ImGuiWindow* window)
3044 {
3045     ImGuiContext& g = *GImGui;
3046     g.CurrentWindow = window;
3047     g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL;
3048     if (window)
3049         g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
3050 }
3051 
GcCompactTransientMiscBuffers()3052 void ImGui::GcCompactTransientMiscBuffers()
3053 {
3054     ImGuiContext& g = *GImGui;
3055     g.ItemFlagsStack.clear();
3056     g.GroupStack.clear();
3057     TableGcCompactSettings();
3058 }
3059 
3060 // Free up/compact internal window buffers, we can use this when a window becomes unused.
3061 // Not freed:
3062 // - ImGuiWindow, ImGuiWindowSettings, Name, StateStorage, ColumnsStorage (may hold useful data)
3063 // This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.
GcCompactTransientWindowBuffers(ImGuiWindow * window)3064 void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window)
3065 {
3066     window->MemoryCompacted = true;
3067     window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;
3068     window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;
3069     window->IDStack.clear();
3070     window->DrawList->_ClearFreeMemory();
3071     window->DC.ChildWindows.clear();
3072     window->DC.ItemWidthStack.clear();
3073     window->DC.TextWrapPosStack.clear();
3074 }
3075 
GcAwakeTransientWindowBuffers(ImGuiWindow * window)3076 void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window)
3077 {
3078     // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.
3079     // The other buffers tends to amortize much faster.
3080     window->MemoryCompacted = false;
3081     window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity);
3082     window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity);
3083     window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0;
3084 }
3085 
SetActiveID(ImGuiID id,ImGuiWindow * window)3086 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
3087 {
3088     ImGuiContext& g = *GImGui;
3089     g.ActiveIdIsJustActivated = (g.ActiveId != id);
3090     if (g.ActiveIdIsJustActivated)
3091     {
3092         g.ActiveIdTimer = 0.0f;
3093         g.ActiveIdHasBeenPressedBefore = false;
3094         g.ActiveIdHasBeenEditedBefore = false;
3095         g.ActiveIdMouseButton = -1;
3096         if (id != 0)
3097         {
3098             g.LastActiveId = id;
3099             g.LastActiveIdTimer = 0.0f;
3100         }
3101     }
3102     g.ActiveId = id;
3103     g.ActiveIdAllowOverlap = false;
3104     g.ActiveIdNoClearOnFocusLoss = false;
3105     g.ActiveIdWindow = window;
3106     g.ActiveIdHasBeenEditedThisFrame = false;
3107     if (id)
3108     {
3109         g.ActiveIdIsAlive = id;
3110         g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
3111     }
3112 
3113     // Clear declaration of inputs claimed by the widget
3114     // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
3115     g.ActiveIdUsingMouseWheel = false;
3116     g.ActiveIdUsingNavDirMask = 0x00;
3117     g.ActiveIdUsingNavInputMask = 0x00;
3118     g.ActiveIdUsingKeyInputMask = 0x00;
3119 }
3120 
ClearActiveID()3121 void ImGui::ClearActiveID()
3122 {
3123     SetActiveID(0, NULL); // g.ActiveId = 0;
3124 }
3125 
SetHoveredID(ImGuiID id)3126 void ImGui::SetHoveredID(ImGuiID id)
3127 {
3128     ImGuiContext& g = *GImGui;
3129     g.HoveredId = id;
3130     g.HoveredIdAllowOverlap = false;
3131     g.HoveredIdUsingMouseWheel = false;
3132     if (id != 0 && g.HoveredIdPreviousFrame != id)
3133         g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
3134 }
3135 
GetHoveredID()3136 ImGuiID ImGui::GetHoveredID()
3137 {
3138     ImGuiContext& g = *GImGui;
3139     return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
3140 }
3141 
KeepAliveID(ImGuiID id)3142 void ImGui::KeepAliveID(ImGuiID id)
3143 {
3144     ImGuiContext& g = *GImGui;
3145     if (g.ActiveId == id)
3146         g.ActiveIdIsAlive = id;
3147     if (g.ActiveIdPreviousFrame == id)
3148         g.ActiveIdPreviousFrameIsAlive = true;
3149 }
3150 
MarkItemEdited(ImGuiID id)3151 void ImGui::MarkItemEdited(ImGuiID id)
3152 {
3153     // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
3154     // 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.
3155     ImGuiContext& g = *GImGui;
3156     IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive);
3157     IM_UNUSED(id); // Avoid unused variable warnings when asserts are compiled out.
3158     //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
3159     g.ActiveIdHasBeenEditedThisFrame = true;
3160     g.ActiveIdHasBeenEditedBefore = true;
3161     g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;
3162 }
3163 
IsWindowContentHoverable(ImGuiWindow * window,ImGuiHoveredFlags flags)3164 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
3165 {
3166     // An active popup disable hovering on other windows (apart from its own children)
3167     // FIXME-OPT: This could be cached/stored within the window.
3168     ImGuiContext& g = *GImGui;
3169     if (g.NavWindow)
3170         if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
3171             if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
3172             {
3173                 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
3174                 // NB: The order of those two tests is important because Modal windows are also Popups.
3175                 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
3176                     return false;
3177                 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
3178                     return false;
3179             }
3180     return true;
3181 }
3182 
3183 // This is roughly matching the behavior of internal-facing ItemHoverable()
3184 // - 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()
3185 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
IsItemHovered(ImGuiHoveredFlags flags)3186 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
3187 {
3188     ImGuiContext& g = *GImGui;
3189     ImGuiWindow* window = g.CurrentWindow;
3190     if (g.NavDisableMouseHover && !g.NavDisableHighlight)
3191     {
3192         if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
3193             return false;
3194         return IsItemFocused();
3195     }
3196 
3197     // Test for bounding box overlap, as updated as ItemAdd()
3198     ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags;
3199     if (!(status_flags & ImGuiItemStatusFlags_HoveredRect))
3200         return false;
3201     IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0);   // Flags not supported by this function
3202 
3203     // Test if we are hovering the right window (our window could be behind another window)
3204     // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851)
3205     // [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
3206     // to use IsItemHovered() after EndChild() itself. Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was
3207     // the test that has been running for a long while.
3208     if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0)
3209         if ((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0)
3210             return false;
3211 
3212     // Test if another item is active (e.g. being dragged)
3213     if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0)
3214         if (g.ActiveId != 0 && g.ActiveId != g.LastItemData.ID && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
3215             return false;
3216 
3217     // Test if interactions on this window are blocked by an active popup or modal.
3218     // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
3219     if (!IsWindowContentHoverable(window, flags))
3220         return false;
3221 
3222     // Test if the item is disabled
3223     if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
3224         return false;
3225 
3226     // Special handling for calling after Begin() which represent the title bar or tab.
3227     // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
3228     if (g.LastItemData.ID == window->MoveId && window->WriteAccessed)
3229         return false;
3230     return true;
3231 }
3232 
3233 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
ItemHoverable(const ImRect & bb,ImGuiID id)3234 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
3235 {
3236     ImGuiContext& g = *GImGui;
3237     if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
3238         return false;
3239 
3240     ImGuiWindow* window = g.CurrentWindow;
3241     if (g.HoveredWindow != window)
3242         return false;
3243     if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
3244         return false;
3245     if (!IsMouseHoveringRect(bb.Min, bb.Max))
3246         return false;
3247     if (g.NavDisableMouseHover)
3248         return false;
3249     if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
3250     {
3251         g.HoveredIdDisabled = true;
3252         return false;
3253     }
3254 
3255     // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level
3256     // hover test in widgets code. We could also decide to split this function is two.
3257     if (id != 0)
3258         SetHoveredID(id);
3259 
3260     // When disabled we'll return false but still set HoveredId
3261     ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.InFlags : g.CurrentItemFlags);
3262     if (item_flags & ImGuiItemFlags_Disabled)
3263     {
3264         // Release active id if turning disabled
3265         if (g.ActiveId == id)
3266             ClearActiveID();
3267         g.HoveredIdDisabled = true;
3268         return false;
3269     }
3270 
3271     if (id != 0)
3272     {
3273         // [DEBUG] Item Picker tool!
3274         // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making
3275         // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered
3276         // items if we perform the test in ItemAdd(), but that would incur a small runtime cost.
3277         // #define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX in imconfig.h if you want this check to also be performed in ItemAdd().
3278         if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
3279             GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
3280         if (g.DebugItemPickerBreakId == id)
3281             IM_DEBUG_BREAK();
3282     }
3283 
3284     return true;
3285 }
3286 
IsClippedEx(const ImRect & bb,ImGuiID id,bool clip_even_when_logged)3287 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
3288 {
3289     ImGuiContext& g = *GImGui;
3290     ImGuiWindow* window = g.CurrentWindow;
3291     if (!bb.Overlaps(window->ClipRect))
3292         if (id == 0 || (id != g.ActiveId && id != g.NavId))
3293             if (clip_even_when_logged || !g.LogEnabled)
3294                 return true;
3295     return false;
3296 }
3297 
3298 // Called by ItemAdd()
3299 // Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out.
ItemFocusable(ImGuiWindow * window,ImGuiID id)3300 void ImGui::ItemFocusable(ImGuiWindow* window, ImGuiID id)
3301 {
3302     ImGuiContext& g = *GImGui;
3303     IM_ASSERT(id != 0 && id == g.LastItemData.ID);
3304 
3305     // Increment counters
3306     // FIXME: ImGuiItemFlags_Disabled should disable more.
3307     const bool is_tab_stop = (g.LastItemData.InFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0;
3308     window->DC.FocusCounterRegular++;
3309     if (is_tab_stop)
3310     {
3311         window->DC.FocusCounterTabStop++;
3312         if (g.NavId == id)
3313             g.NavIdTabCounter = window->DC.FocusCounterTabStop;
3314     }
3315 
3316     // Process TAB/Shift-TAB to tab *OUT* of the currently focused item.
3317     // (Note that we can always TAB out of a widget that doesn't allow tabbing in)
3318     if (g.ActiveId == id && g.TabFocusPressed && !IsActiveIdUsingKey(ImGuiKey_Tab) && g.TabFocusRequestNextWindow == NULL)
3319     {
3320         g.TabFocusRequestNextWindow = window;
3321         g.TabFocusRequestNextCounterTabStop = window->DC.FocusCounterTabStop + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items.
3322     }
3323 
3324     // Handle focus requests
3325     if (g.TabFocusRequestCurrWindow == window)
3326     {
3327         if (window->DC.FocusCounterRegular == g.TabFocusRequestCurrCounterRegular)
3328         {
3329             g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_FocusedByCode;
3330             return;
3331         }
3332         if (is_tab_stop && window->DC.FocusCounterTabStop == g.TabFocusRequestCurrCounterTabStop)
3333         {
3334             g.NavJustTabbedId = id;
3335             g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_FocusedByTabbing;
3336             return;
3337         }
3338 
3339         // If another item is about to be focused, we clear our own active id
3340         if (g.ActiveId == id)
3341             ClearActiveID();
3342     }
3343 }
3344 
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)3345 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
3346 {
3347     if (wrap_pos_x < 0.0f)
3348         return 0.0f;
3349 
3350     ImGuiContext& g = *GImGui;
3351     ImGuiWindow* window = g.CurrentWindow;
3352     if (wrap_pos_x == 0.0f)
3353     {
3354         // We could decide to setup a default wrapping max point for auto-resizing windows,
3355         // or have auto-wrap (with unspecified wrapping pos) behave as a ContentSize extending function?
3356         //if (window->Hidden && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize))
3357         //    wrap_pos_x = ImMax(window->WorkRect.Min.x + g.FontSize * 10.0f, window->WorkRect.Max.x);
3358         //else
3359         wrap_pos_x = window->WorkRect.Max.x;
3360     }
3361     else if (wrap_pos_x > 0.0f)
3362     {
3363         wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
3364     }
3365 
3366     return ImMax(wrap_pos_x - pos.x, 1.0f);
3367 }
3368 
3369 // IM_ALLOC() == ImGui::MemAlloc()
MemAlloc(size_t size)3370 void* ImGui::MemAlloc(size_t size)
3371 {
3372     if (ImGuiContext* ctx = GImGui)
3373         ctx->IO.MetricsActiveAllocations++;
3374     return (*GImAllocatorAllocFunc)(size, GImAllocatorUserData);
3375 }
3376 
3377 // IM_FREE() == ImGui::MemFree()
MemFree(void * ptr)3378 void ImGui::MemFree(void* ptr)
3379 {
3380     if (ptr)
3381         if (ImGuiContext* ctx = GImGui)
3382             ctx->IO.MetricsActiveAllocations--;
3383     return (*GImAllocatorFreeFunc)(ptr, GImAllocatorUserData);
3384 }
3385 
GetClipboardText()3386 const char* ImGui::GetClipboardText()
3387 {
3388     ImGuiContext& g = *GImGui;
3389     return g.IO.GetClipboardTextFn ? g.IO.GetClipboardTextFn(g.IO.ClipboardUserData) : "";
3390 }
3391 
SetClipboardText(const char * text)3392 void ImGui::SetClipboardText(const char* text)
3393 {
3394     ImGuiContext& g = *GImGui;
3395     if (g.IO.SetClipboardTextFn)
3396         g.IO.SetClipboardTextFn(g.IO.ClipboardUserData, text);
3397 }
3398 
GetVersion()3399 const char* ImGui::GetVersion()
3400 {
3401     return IMGUI_VERSION;
3402 }
3403 
3404 // Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself
3405 // 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()3406 ImGuiContext* ImGui::GetCurrentContext()
3407 {
3408     return GImGui;
3409 }
3410 
SetCurrentContext(ImGuiContext * ctx)3411 void ImGui::SetCurrentContext(ImGuiContext* ctx)
3412 {
3413 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
3414     IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
3415 #else
3416     GImGui = ctx;
3417 #endif
3418 }
3419 
SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func,ImGuiMemFreeFunc free_func,void * user_data)3420 void ImGui::SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data)
3421 {
3422     GImAllocatorAllocFunc = alloc_func;
3423     GImAllocatorFreeFunc = free_func;
3424     GImAllocatorUserData = user_data;
3425 }
3426 
3427 // This is provided to facilitate copying allocators from one static/DLL boundary to another (e.g. retrieve default allocator of your executable address space)
GetAllocatorFunctions(ImGuiMemAllocFunc * p_alloc_func,ImGuiMemFreeFunc * p_free_func,void ** p_user_data)3428 void ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data)
3429 {
3430     *p_alloc_func = GImAllocatorAllocFunc;
3431     *p_free_func = GImAllocatorFreeFunc;
3432     *p_user_data = GImAllocatorUserData;
3433 }
3434 
CreateContext(ImFontAtlas * shared_font_atlas)3435 ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
3436 {
3437     ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
3438     if (GImGui == NULL)
3439         SetCurrentContext(ctx);
3440     Initialize(ctx);
3441     return ctx;
3442 }
3443 
DestroyContext(ImGuiContext * ctx)3444 void ImGui::DestroyContext(ImGuiContext* ctx)
3445 {
3446     if (ctx == NULL)
3447         ctx = GImGui;
3448     Shutdown(ctx);
3449     if (GImGui == ctx)
3450         SetCurrentContext(NULL);
3451     IM_DELETE(ctx);
3452 }
3453 
3454 // No specific ordering/dependency support, will see as needed
AddContextHook(ImGuiContext * ctx,const ImGuiContextHook * hook)3455 ImGuiID ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook)
3456 {
3457     ImGuiContext& g = *ctx;
3458     IM_ASSERT(hook->Callback != NULL && hook->HookId == 0 && hook->Type != ImGuiContextHookType_PendingRemoval_);
3459     g.Hooks.push_back(*hook);
3460     g.Hooks.back().HookId = ++g.HookIdNext;
3461     return g.HookIdNext;
3462 }
3463 
3464 // Deferred removal, avoiding issue with changing vector while iterating it
RemoveContextHook(ImGuiContext * ctx,ImGuiID hook_id)3465 void ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id)
3466 {
3467     ImGuiContext& g = *ctx;
3468     IM_ASSERT(hook_id != 0);
3469     for (int n = 0; n < g.Hooks.Size; n++)
3470         if (g.Hooks[n].HookId == hook_id)
3471             g.Hooks[n].Type = ImGuiContextHookType_PendingRemoval_;
3472 }
3473 
3474 // Call context hooks (used by e.g. test engine)
3475 // We assume a small number of hooks so all stored in same array
CallContextHooks(ImGuiContext * ctx,ImGuiContextHookType hook_type)3476 void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type)
3477 {
3478     ImGuiContext& g = *ctx;
3479     for (int n = 0; n < g.Hooks.Size; n++)
3480         if (g.Hooks[n].Type == hook_type)
3481             g.Hooks[n].Callback(&g, &g.Hooks[n]);
3482 }
3483 
GetIO()3484 ImGuiIO& ImGui::GetIO()
3485 {
3486     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3487     return GImGui->IO;
3488 }
3489 
3490 // Pass this to your backend rendering function! Valid after Render() and until the next call to NewFrame()
GetDrawData()3491 ImDrawData* ImGui::GetDrawData()
3492 {
3493     ImGuiContext& g = *GImGui;
3494     ImGuiViewportP* viewport = g.Viewports[0];
3495     return viewport->DrawDataP.Valid ? &viewport->DrawDataP : NULL;
3496 }
3497 
GetTime()3498 double ImGui::GetTime()
3499 {
3500     return GImGui->Time;
3501 }
3502 
GetFrameCount()3503 int ImGui::GetFrameCount()
3504 {
3505     return GImGui->FrameCount;
3506 }
3507 
GetViewportDrawList(ImGuiViewportP * viewport,size_t drawlist_no,const char * drawlist_name)3508 static ImDrawList* GetViewportDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name)
3509 {
3510     // Create the draw list on demand, because they are not frequently used for all viewports
3511     ImGuiContext& g = *GImGui;
3512     IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->DrawLists));
3513     ImDrawList* draw_list = viewport->DrawLists[drawlist_no];
3514     if (draw_list == NULL)
3515     {
3516         draw_list = IM_NEW(ImDrawList)(&g.DrawListSharedData);
3517         draw_list->_OwnerName = drawlist_name;
3518         viewport->DrawLists[drawlist_no] = draw_list;
3519     }
3520 
3521     // Our ImDrawList system requires that there is always a command
3522     if (viewport->DrawListsLastFrame[drawlist_no] != g.FrameCount)
3523     {
3524         draw_list->_ResetForNewFrame();
3525         draw_list->PushTextureID(g.IO.Fonts->TexID);
3526         draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false);
3527         viewport->DrawListsLastFrame[drawlist_no] = g.FrameCount;
3528     }
3529     return draw_list;
3530 }
3531 
GetBackgroundDrawList(ImGuiViewport * viewport)3532 ImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport)
3533 {
3534     return GetViewportDrawList((ImGuiViewportP*)viewport, 0, "##Background");
3535 }
3536 
GetBackgroundDrawList()3537 ImDrawList* ImGui::GetBackgroundDrawList()
3538 {
3539     ImGuiContext& g = *GImGui;
3540     return GetBackgroundDrawList(g.Viewports[0]);
3541 }
3542 
GetForegroundDrawList(ImGuiViewport * viewport)3543 ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport)
3544 {
3545     return GetViewportDrawList((ImGuiViewportP*)viewport, 1, "##Foreground");
3546 }
3547 
GetForegroundDrawList()3548 ImDrawList* ImGui::GetForegroundDrawList()
3549 {
3550     ImGuiContext& g = *GImGui;
3551     return GetForegroundDrawList(g.Viewports[0]);
3552 }
3553 
GetDrawListSharedData()3554 ImDrawListSharedData* ImGui::GetDrawListSharedData()
3555 {
3556     return &GImGui->DrawListSharedData;
3557 }
3558 
StartMouseMovingWindow(ImGuiWindow * window)3559 void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
3560 {
3561     // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
3562     // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
3563     // This is because we want ActiveId to be set even when the window is not permitted to move.
3564     ImGuiContext& g = *GImGui;
3565     FocusWindow(window);
3566     SetActiveID(window->MoveId, window);
3567     g.NavDisableHighlight = true;
3568     g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos;
3569     g.ActiveIdNoClearOnFocusLoss = true;
3570     SetActiveIdUsingNavAndKeys();
3571 
3572     bool can_move_window = true;
3573     if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
3574         can_move_window = false;
3575     if (can_move_window)
3576         g.MovingWindow = window;
3577 }
3578 
3579 // Handle mouse moving window
3580 // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
3581 // FIXME: We don't have strong guarantee that g.MovingWindow stay synched with g.ActiveId == g.MovingWindow->MoveId.
3582 // This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs,
3583 // but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other.
UpdateMouseMovingWindowNewFrame()3584 void ImGui::UpdateMouseMovingWindowNewFrame()
3585 {
3586     ImGuiContext& g = *GImGui;
3587     if (g.MovingWindow != NULL)
3588     {
3589         // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
3590         // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
3591         KeepAliveID(g.ActiveId);
3592         IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
3593         ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
3594         if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
3595         {
3596             ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
3597             if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
3598             {
3599                 MarkIniSettingsDirty(moving_window);
3600                 SetWindowPos(moving_window, pos, ImGuiCond_Always);
3601             }
3602             FocusWindow(g.MovingWindow);
3603         }
3604         else
3605         {
3606             g.MovingWindow = NULL;
3607             ClearActiveID();
3608         }
3609     }
3610     else
3611     {
3612         // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
3613         if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
3614         {
3615             KeepAliveID(g.ActiveId);
3616             if (!g.IO.MouseDown[0])
3617                 ClearActiveID();
3618         }
3619     }
3620 }
3621 
3622 // Initiate moving window when clicking on empty space or title bar.
3623 // Handle left-click and right-click focus.
UpdateMouseMovingWindowEndFrame()3624 void ImGui::UpdateMouseMovingWindowEndFrame()
3625 {
3626     ImGuiContext& g = *GImGui;
3627     if (g.ActiveId != 0 || g.HoveredId != 0)
3628         return;
3629 
3630     // Unless we just made a window/popup appear
3631     if (g.NavWindow && g.NavWindow->Appearing)
3632         return;
3633 
3634     // Click on empty space to focus window and start moving
3635     // (after we're done with all our widgets)
3636     if (g.IO.MouseClicked[0])
3637     {
3638         // Handle the edge case of a popup being closed while clicking in its empty space.
3639         // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more.
3640         ImGuiWindow* root_window = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
3641         const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(root_window->PopupId, ImGuiPopupFlags_AnyPopupLevel);
3642 
3643         if (root_window != NULL && !is_closed_popup)
3644         {
3645             StartMouseMovingWindow(g.HoveredWindow); //-V595
3646 
3647             // Cancel moving if clicked outside of title bar
3648             if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(root_window->Flags & ImGuiWindowFlags_NoTitleBar))
3649                 if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
3650                     g.MovingWindow = NULL;
3651 
3652             // Cancel moving if clicked over an item which was disabled or inhibited by popups (note that we know HoveredId == 0 already)
3653             if (g.HoveredIdDisabled)
3654                 g.MovingWindow = NULL;
3655         }
3656         else if (root_window == NULL && g.NavWindow != NULL && GetTopMostPopupModal() == NULL)
3657         {
3658             // Clicking on void disable focus
3659             FocusWindow(NULL);
3660         }
3661     }
3662 
3663     // With right mouse button we close popups without changing focus based on where the mouse is aimed
3664     // Instead, focus will be restored to the window under the bottom-most closed popup.
3665     // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
3666     if (g.IO.MouseClicked[1])
3667     {
3668         // Find the top-most window between HoveredWindow and the top-most Modal Window.
3669         // This is where we can trim the popup stack.
3670         ImGuiWindow* modal = GetTopMostPopupModal();
3671         bool hovered_window_above_modal = g.HoveredWindow && IsWindowAbove(g.HoveredWindow, modal);
3672         ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true);
3673     }
3674 }
3675 
IsWindowActiveAndVisible(ImGuiWindow * window)3676 static bool IsWindowActiveAndVisible(ImGuiWindow* window)
3677 {
3678     return (window->Active) && (!window->Hidden);
3679 }
3680 
UpdateMouseInputs()3681 static void ImGui::UpdateMouseInputs()
3682 {
3683     ImGuiContext& g = *GImGui;
3684 
3685     // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
3686     if (IsMousePosValid(&g.IO.MousePos))
3687         g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos);
3688 
3689     // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
3690     if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
3691         g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
3692     else
3693         g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
3694     if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
3695         g.NavDisableMouseHover = false;
3696 
3697     g.IO.MousePosPrev = g.IO.MousePos;
3698     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3699     {
3700         g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
3701         g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
3702         g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
3703         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;
3704         g.IO.MouseDoubleClicked[i] = false;
3705         if (g.IO.MouseClicked[i])
3706         {
3707             if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime)
3708             {
3709                 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3710                 if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
3711                     g.IO.MouseDoubleClicked[i] = true;
3712                 g.IO.MouseClickedTime[i] = -g.IO.MouseDoubleClickTime * 2.0f; // Mark as "old enough" so the third click isn't turned into a double-click
3713             }
3714             else
3715             {
3716                 g.IO.MouseClickedTime[i] = g.Time;
3717             }
3718             g.IO.MouseClickedPos[i] = g.IO.MousePos;
3719             g.IO.MouseDownWasDoubleClick[i] = g.IO.MouseDoubleClicked[i];
3720             g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
3721             g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
3722         }
3723         else if (g.IO.MouseDown[i])
3724         {
3725             // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
3726             ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3727             g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
3728             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);
3729             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);
3730         }
3731         if (!g.IO.MouseDown[i] && !g.IO.MouseReleased[i])
3732             g.IO.MouseDownWasDoubleClick[i] = false;
3733         if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
3734             g.NavDisableMouseHover = false;
3735     }
3736 }
3737 
StartLockWheelingWindow(ImGuiWindow * window)3738 static void StartLockWheelingWindow(ImGuiWindow* window)
3739 {
3740     ImGuiContext& g = *GImGui;
3741     if (g.WheelingWindow == window)
3742         return;
3743     g.WheelingWindow = window;
3744     g.WheelingWindowRefMousePos = g.IO.MousePos;
3745     g.WheelingWindowTimer = WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER;
3746 }
3747 
UpdateMouseWheel()3748 void ImGui::UpdateMouseWheel()
3749 {
3750     ImGuiContext& g = *GImGui;
3751 
3752     // Reset the locked window if we move the mouse or after the timer elapses
3753     if (g.WheelingWindow != NULL)
3754     {
3755         g.WheelingWindowTimer -= g.IO.DeltaTime;
3756         if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
3757             g.WheelingWindowTimer = 0.0f;
3758         if (g.WheelingWindowTimer <= 0.0f)
3759         {
3760             g.WheelingWindow = NULL;
3761             g.WheelingWindowTimer = 0.0f;
3762         }
3763     }
3764 
3765     if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f)
3766         return;
3767 
3768     if ((g.ActiveId != 0 && g.ActiveIdUsingMouseWheel) || (g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrameUsingMouseWheel))
3769         return;
3770 
3771     ImGuiWindow* window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
3772     if (!window || window->Collapsed)
3773         return;
3774 
3775     // Zoom / Scale window
3776     // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
3777     if (g.IO.MouseWheel != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
3778     {
3779         StartLockWheelingWindow(window);
3780         const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
3781         const float scale = new_font_scale / window->FontWindowScale;
3782         window->FontWindowScale = new_font_scale;
3783         if (window == window->RootWindow)
3784         {
3785             const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
3786             SetWindowPos(window, window->Pos + offset, 0);
3787             window->Size = ImFloor(window->Size * scale);
3788             window->SizeFull = ImFloor(window->SizeFull * scale);
3789         }
3790         return;
3791     }
3792 
3793     // Mouse wheel scrolling
3794     // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent
3795     if (g.IO.KeyCtrl)
3796         return;
3797 
3798     // As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead
3799     // (we avoid doing it on OSX as it the OS input layer handles this already)
3800     const bool swap_axis = g.IO.KeyShift && !g.IO.ConfigMacOSXBehaviors;
3801     const float wheel_y = swap_axis ? 0.0f : g.IO.MouseWheel;
3802     const float wheel_x = swap_axis ? g.IO.MouseWheel : g.IO.MouseWheelH;
3803 
3804     // Vertical Mouse Wheel scrolling
3805     if (wheel_y != 0.0f)
3806     {
3807         StartLockWheelingWindow(window);
3808         while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3809             window = window->ParentWindow;
3810         if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3811         {
3812             float max_step = window->InnerRect.GetHeight() * 0.67f;
3813             float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step));
3814             SetScrollY(window, window->Scroll.y - wheel_y * scroll_step);
3815         }
3816     }
3817 
3818     // Horizontal Mouse Wheel scrolling, or Vertical Mouse Wheel w/ Shift held
3819     if (wheel_x != 0.0f)
3820     {
3821         StartLockWheelingWindow(window);
3822         while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.x == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3823             window = window->ParentWindow;
3824         if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3825         {
3826             float max_step = window->InnerRect.GetWidth() * 0.67f;
3827             float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step));
3828             SetScrollX(window, window->Scroll.x - wheel_x * scroll_step);
3829         }
3830     }
3831 }
3832 
UpdateTabFocus()3833 void ImGui::UpdateTabFocus()
3834 {
3835     ImGuiContext& g = *GImGui;
3836 
3837     // Pressing TAB activate widget focus
3838     g.TabFocusPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab));
3839     if (g.ActiveId == 0 && g.TabFocusPressed)
3840     {
3841         // - This path is only taken when no widget are active/tabbed-into yet.
3842         //   Subsequent tabbing will be processed by FocusableItemRegister()
3843         // - Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also
3844         //   manipulate the Next fields here even though they will be turned into Curr fields below.
3845         g.TabFocusRequestNextWindow = g.NavWindow;
3846         g.TabFocusRequestNextCounterRegular = INT_MAX;
3847         if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
3848             g.TabFocusRequestNextCounterTabStop = g.NavIdTabCounter + (g.IO.KeyShift ? -1 : 0);
3849         else
3850             g.TabFocusRequestNextCounterTabStop = g.IO.KeyShift ? -1 : 0;
3851     }
3852 
3853     // Turn queued focus request into current one
3854     g.TabFocusRequestCurrWindow = NULL;
3855     g.TabFocusRequestCurrCounterRegular = g.TabFocusRequestCurrCounterTabStop = INT_MAX;
3856     if (g.TabFocusRequestNextWindow != NULL)
3857     {
3858         ImGuiWindow* window = g.TabFocusRequestNextWindow;
3859         g.TabFocusRequestCurrWindow = window;
3860         if (g.TabFocusRequestNextCounterRegular != INT_MAX && window->DC.FocusCounterRegular != -1)
3861             g.TabFocusRequestCurrCounterRegular = ImModPositive(g.TabFocusRequestNextCounterRegular, window->DC.FocusCounterRegular + 1);
3862         if (g.TabFocusRequestNextCounterTabStop != INT_MAX && window->DC.FocusCounterTabStop != -1)
3863             g.TabFocusRequestCurrCounterTabStop = ImModPositive(g.TabFocusRequestNextCounterTabStop, window->DC.FocusCounterTabStop + 1);
3864         g.TabFocusRequestNextWindow = NULL;
3865         g.TabFocusRequestNextCounterRegular = g.TabFocusRequestNextCounterTabStop = INT_MAX;
3866     }
3867 
3868     g.NavIdTabCounter = INT_MAX;
3869 }
3870 
3871 // 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()3872 void ImGui::UpdateHoveredWindowAndCaptureFlags()
3873 {
3874     ImGuiContext& g = *GImGui;
3875     ImGuiIO& io = g.IO;
3876     g.WindowsHoverPadding = ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_HOVER_PADDING, WINDOWS_HOVER_PADDING));
3877 
3878     // Find the window hovered by mouse:
3879     // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3880     // - 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.
3881     // - 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.
3882     bool clear_hovered_windows = false;
3883     FindHoveredWindow();
3884 
3885     // Modal windows prevents mouse from hovering behind them.
3886     ImGuiWindow* modal_window = GetTopMostPopupModal();
3887     if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindow, modal_window))
3888         clear_hovered_windows = true;
3889 
3890     // Disabled mouse?
3891     if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)
3892         clear_hovered_windows = true;
3893 
3894     // We track click ownership. When clicked outside of a window the click is owned by the application and
3895     // won't report hovering nor request capture even while dragging over our windows afterward.
3896     const bool has_open_popup = (g.OpenPopupStack.Size > 0);
3897     const bool has_open_modal = (modal_window != NULL);
3898     int mouse_earliest_down = -1;
3899     bool mouse_any_down = false;
3900     for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
3901     {
3902         if (io.MouseClicked[i])
3903         {
3904             io.MouseDownOwned[i] = (g.HoveredWindow != NULL) || has_open_popup;
3905             io.MouseDownOwnedUnlessPopupClose[i] = (g.HoveredWindow != NULL) || has_open_modal;
3906         }
3907         mouse_any_down |= io.MouseDown[i];
3908         if (io.MouseDown[i])
3909             if (mouse_earliest_down == -1 || io.MouseClickedTime[i] < io.MouseClickedTime[mouse_earliest_down])
3910                 mouse_earliest_down = i;
3911     }
3912     const bool mouse_avail = (mouse_earliest_down == -1) || io.MouseDownOwned[mouse_earliest_down];
3913     const bool mouse_avail_unless_popup_close = (mouse_earliest_down == -1) || io.MouseDownOwnedUnlessPopupClose[mouse_earliest_down];
3914 
3915     // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
3916     // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
3917     const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
3918     if (!mouse_avail && !mouse_dragging_extern_payload)
3919         clear_hovered_windows = true;
3920 
3921     if (clear_hovered_windows)
3922         g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
3923 
3924     // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to Dear ImGui only, false = dispatch mouse to Dear ImGui + underlying app)
3925     // Update io.WantCaptureMouseAllowPopupClose (experimental) to give a chance for app to react to popup closure with a drag
3926     if (g.WantCaptureMouseNextFrame != -1)
3927     {
3928         io.WantCaptureMouse = io.WantCaptureMouseUnlessPopupClose = (g.WantCaptureMouseNextFrame != 0);
3929     }
3930     else
3931     {
3932         io.WantCaptureMouse = (mouse_avail && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_popup;
3933         io.WantCaptureMouseUnlessPopupClose = (mouse_avail_unless_popup_close && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_modal;
3934     }
3935 
3936     // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to Dear ImGui only, false = dispatch keyboard info to Dear ImGui + underlying app)
3937     if (g.WantCaptureKeyboardNextFrame != -1)
3938         io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
3939     else
3940         io.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
3941     if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
3942         io.WantCaptureKeyboard = true;
3943 
3944     // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
3945     io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
3946 }
3947 
GetMergedKeyModFlags()3948 ImGuiKeyModFlags ImGui::GetMergedKeyModFlags()
3949 {
3950     ImGuiContext& g = *GImGui;
3951     ImGuiKeyModFlags key_mod_flags = ImGuiKeyModFlags_None;
3952     if (g.IO.KeyCtrl)   { key_mod_flags |= ImGuiKeyModFlags_Ctrl; }
3953     if (g.IO.KeyShift)  { key_mod_flags |= ImGuiKeyModFlags_Shift; }
3954     if (g.IO.KeyAlt)    { key_mod_flags |= ImGuiKeyModFlags_Alt; }
3955     if (g.IO.KeySuper)  { key_mod_flags |= ImGuiKeyModFlags_Super; }
3956     return key_mod_flags;
3957 }
3958 
NewFrame()3959 void ImGui::NewFrame()
3960 {
3961     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3962     ImGuiContext& g = *GImGui;
3963 
3964     // Remove pending delete hooks before frame start.
3965     // This deferred removal avoid issues of removal while iterating the hook vector
3966     for (int n = g.Hooks.Size - 1; n >= 0; n--)
3967         if (g.Hooks[n].Type == ImGuiContextHookType_PendingRemoval_)
3968             g.Hooks.erase(&g.Hooks[n]);
3969 
3970     CallContextHooks(&g, ImGuiContextHookType_NewFramePre);
3971 
3972     // Check and assert for various common IO and Configuration mistakes
3973     ErrorCheckNewFrameSanityChecks();
3974 
3975     // Load settings on first frame, save settings when modified (after a delay)
3976     UpdateSettings();
3977 
3978     g.Time += g.IO.DeltaTime;
3979     g.WithinFrameScope = true;
3980     g.FrameCount += 1;
3981     g.TooltipOverrideCount = 0;
3982     g.WindowsActiveCount = 0;
3983     g.MenusIdSubmittedThisFrame.resize(0);
3984 
3985     // Calculate frame-rate for the user, as a purely luxurious feature
3986     g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
3987     g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
3988     g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
3989     g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, IM_ARRAYSIZE(g.FramerateSecPerFrame));
3990     g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)g.FramerateSecPerFrameCount)) : FLT_MAX;
3991 
3992     UpdateViewportsNewFrame();
3993 
3994     // Setup current font and draw list shared data
3995     g.IO.Fonts->Locked = true;
3996     SetCurrentFont(GetDefaultFont());
3997     IM_ASSERT(g.Font->IsLoaded());
3998     ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
3999     for (int n = 0; n < g.Viewports.Size; n++)
4000         virtual_space.Add(g.Viewports[n]->GetMainRect());
4001     g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4();
4002     g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
4003     g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError);
4004     g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
4005     if (g.Style.AntiAliasedLines)
4006         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
4007     if (g.Style.AntiAliasedLinesUseTex && !(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines))
4008         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex;
4009     if (g.Style.AntiAliasedFill)
4010         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
4011     if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
4012         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
4013 
4014     // Mark rendering data as invalid to prevent user who may have a handle on it to use it.
4015     for (int n = 0; n < g.Viewports.Size; n++)
4016     {
4017         ImGuiViewportP* viewport = g.Viewports[n];
4018         viewport->DrawDataP.Clear();
4019     }
4020 
4021     // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
4022     if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
4023         KeepAliveID(g.DragDropPayload.SourceId);
4024 
4025     // Update HoveredId data
4026     if (!g.HoveredIdPreviousFrame)
4027         g.HoveredIdTimer = 0.0f;
4028     if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
4029         g.HoveredIdNotActiveTimer = 0.0f;
4030     if (g.HoveredId)
4031         g.HoveredIdTimer += g.IO.DeltaTime;
4032     if (g.HoveredId && g.ActiveId != g.HoveredId)
4033         g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
4034     g.HoveredIdPreviousFrame = g.HoveredId;
4035     g.HoveredIdPreviousFrameUsingMouseWheel = g.HoveredIdUsingMouseWheel;
4036     g.HoveredId = 0;
4037     g.HoveredIdAllowOverlap = false;
4038     g.HoveredIdUsingMouseWheel = false;
4039     g.HoveredIdDisabled = false;
4040 
4041     // Update ActiveId data (clear reference to active widget if the widget isn't alive anymore)
4042     if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
4043         ClearActiveID();
4044     if (g.ActiveId)
4045         g.ActiveIdTimer += g.IO.DeltaTime;
4046     g.LastActiveIdTimer += g.IO.DeltaTime;
4047     g.ActiveIdPreviousFrame = g.ActiveId;
4048     g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
4049     g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore;
4050     g.ActiveIdIsAlive = 0;
4051     g.ActiveIdHasBeenEditedThisFrame = false;
4052     g.ActiveIdPreviousFrameIsAlive = false;
4053     g.ActiveIdIsJustActivated = false;
4054     if (g.TempInputId != 0 && g.ActiveId != g.TempInputId)
4055         g.TempInputId = 0;
4056     if (g.ActiveId == 0)
4057     {
4058         g.ActiveIdUsingNavDirMask = 0x00;
4059         g.ActiveIdUsingNavInputMask = 0x00;
4060         g.ActiveIdUsingKeyInputMask = 0x00;
4061     }
4062 
4063     // Drag and drop
4064     g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
4065     g.DragDropAcceptIdCurr = 0;
4066     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
4067     g.DragDropWithinSource = false;
4068     g.DragDropWithinTarget = false;
4069     g.DragDropHoldJustPressedId = 0;
4070 
4071     // Update keyboard input state
4072     // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools
4073     g.IO.KeyMods = GetMergedKeyModFlags();
4074     memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
4075     for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
4076         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;
4077 
4078     // Update gamepad/keyboard navigation
4079     NavUpdate();
4080 
4081     // Update mouse input state
4082     UpdateMouseInputs();
4083 
4084     // Find hovered window
4085     // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
4086     UpdateHoveredWindowAndCaptureFlags();
4087 
4088     // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
4089     UpdateMouseMovingWindowNewFrame();
4090 
4091     // Background darkening/whitening
4092     if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
4093         g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
4094     else
4095         g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
4096 
4097     g.MouseCursor = ImGuiMouseCursor_Arrow;
4098     g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
4099     g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
4100 
4101     // Mouse wheel scrolling, scale
4102     UpdateMouseWheel();
4103 
4104     // Update legacy TAB focus
4105     UpdateTabFocus();
4106 
4107     // Mark all windows as not visible and compact unused memory.
4108     IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size);
4109     const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer;
4110     for (int i = 0; i != g.Windows.Size; i++)
4111     {
4112         ImGuiWindow* window = g.Windows[i];
4113         window->WasActive = window->Active;
4114         window->BeginCount = 0;
4115         window->Active = false;
4116         window->WriteAccessed = false;
4117 
4118         // Garbage collect transient buffers of recently unused windows
4119         if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
4120             GcCompactTransientWindowBuffers(window);
4121     }
4122 
4123     // Garbage collect transient buffers of recently unused tables
4124     for (int i = 0; i < g.TablesLastTimeActive.Size; i++)
4125         if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time)
4126             TableGcCompactTransientBuffers(g.Tables.GetByIndex(i));
4127     for (int i = 0; i < g.TablesTempDataStack.Size; i++)
4128         if (g.TablesTempDataStack[i].LastTimeActive >= 0.0f && g.TablesTempDataStack[i].LastTimeActive < memory_compact_start_time)
4129             TableGcCompactTransientBuffers(&g.TablesTempDataStack[i]);
4130     if (g.GcCompactAll)
4131         GcCompactTransientMiscBuffers();
4132     g.GcCompactAll = false;
4133 
4134     // Closing the focused window restore focus to the first active root window in descending z-order
4135     if (g.NavWindow && !g.NavWindow->WasActive)
4136         FocusTopMostWindowUnderOne(NULL, NULL);
4137 
4138     // No window should be open at the beginning of the frame.
4139     // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
4140     g.CurrentWindowStack.resize(0);
4141     g.BeginPopupStack.resize(0);
4142     g.ItemFlagsStack.resize(0);
4143     g.ItemFlagsStack.push_back(ImGuiItemFlags_None);
4144     g.GroupStack.resize(0);
4145 
4146     // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
4147     UpdateDebugToolItemPicker();
4148 
4149     // Create implicit/fallback window - which we will only render it if the user has added something to it.
4150     // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
4151     // This fallback is particularly important as it avoid ImGui:: calls from crashing.
4152     g.WithinFrameScopeWithImplicitWindow = true;
4153     SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
4154     Begin("Debug##Default");
4155     IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true);
4156 
4157     CallContextHooks(&g, ImGuiContextHookType_NewFramePost);
4158 }
4159 
4160 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
UpdateDebugToolItemPicker()4161 void ImGui::UpdateDebugToolItemPicker()
4162 {
4163     ImGuiContext& g = *GImGui;
4164     g.DebugItemPickerBreakId = 0;
4165     if (g.DebugItemPickerActive)
4166     {
4167         const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
4168         SetMouseCursor(ImGuiMouseCursor_Hand);
4169         if (IsKeyPressedMap(ImGuiKey_Escape))
4170             g.DebugItemPickerActive = false;
4171         if (IsMouseClicked(0) && hovered_id)
4172         {
4173             g.DebugItemPickerBreakId = hovered_id;
4174             g.DebugItemPickerActive = false;
4175         }
4176         SetNextWindowBgAlpha(0.60f);
4177         BeginTooltip();
4178         Text("HoveredId: 0x%08X", hovered_id);
4179         Text("Press ESC to abort picking.");
4180         TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!");
4181         EndTooltip();
4182     }
4183 }
4184 
Initialize(ImGuiContext * context)4185 void ImGui::Initialize(ImGuiContext* context)
4186 {
4187     ImGuiContext& g = *context;
4188     IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
4189 
4190     // Add .ini handle for ImGuiWindow type
4191     {
4192         ImGuiSettingsHandler ini_handler;
4193         ini_handler.TypeName = "Window";
4194         ini_handler.TypeHash = ImHashStr("Window");
4195         ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll;
4196         ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen;
4197         ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;
4198         ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll;
4199         ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll;
4200         g.SettingsHandlers.push_back(ini_handler);
4201     }
4202 
4203     // Add .ini handle for ImGuiTable type
4204     TableSettingsInstallHandler(context);
4205 
4206     // Create default viewport
4207     ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)();
4208     g.Viewports.push_back(viewport);
4209 
4210 #ifdef IMGUI_HAS_DOCK
4211 #endif
4212 
4213     g.Initialized = true;
4214 }
4215 
4216 // This function is merely here to free heap allocations.
Shutdown(ImGuiContext * context)4217 void ImGui::Shutdown(ImGuiContext* context)
4218 {
4219     // 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)
4220     ImGuiContext& g = *context;
4221     if (g.IO.Fonts && g.FontAtlasOwnedByContext)
4222     {
4223         g.IO.Fonts->Locked = false;
4224         IM_DELETE(g.IO.Fonts);
4225     }
4226     g.IO.Fonts = NULL;
4227 
4228     // Cleanup of other data are conditional on actually having initialized Dear ImGui.
4229     if (!g.Initialized)
4230         return;
4231 
4232     // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
4233     if (g.SettingsLoaded && g.IO.IniFilename != NULL)
4234     {
4235         ImGuiContext* backup_context = GImGui;
4236         SetCurrentContext(&g);
4237         SaveIniSettingsToDisk(g.IO.IniFilename);
4238         SetCurrentContext(backup_context);
4239     }
4240 
4241     CallContextHooks(&g, ImGuiContextHookType_Shutdown);
4242 
4243     // Clear everything else
4244     g.Windows.clear_delete();
4245     g.WindowsFocusOrder.clear();
4246     g.WindowsTempSortBuffer.clear();
4247     g.CurrentWindow = NULL;
4248     g.CurrentWindowStack.clear();
4249     g.WindowsById.Clear();
4250     g.NavWindow = NULL;
4251     g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
4252     g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
4253     g.MovingWindow = NULL;
4254     g.ColorStack.clear();
4255     g.StyleVarStack.clear();
4256     g.FontStack.clear();
4257     g.OpenPopupStack.clear();
4258     g.BeginPopupStack.clear();
4259 
4260     g.Viewports.clear_delete();
4261 
4262     g.TabBars.Clear();
4263     g.CurrentTabBarStack.clear();
4264     g.ShrinkWidthBuffer.clear();
4265 
4266     g.Tables.Clear();
4267     g.TablesTempDataStack.clear_destruct();
4268     g.DrawChannelsTempMergeBuffer.clear();
4269 
4270     g.ClipboardHandlerData.clear();
4271     g.MenusIdSubmittedThisFrame.clear();
4272     g.InputTextState.ClearFreeMemory();
4273 
4274     g.SettingsWindows.clear();
4275     g.SettingsHandlers.clear();
4276 
4277     if (g.LogFile)
4278     {
4279 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
4280         if (g.LogFile != stdout)
4281 #endif
4282             ImFileClose(g.LogFile);
4283         g.LogFile = NULL;
4284     }
4285     g.LogBuffer.clear();
4286 
4287     g.Initialized = false;
4288 }
4289 
4290 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)4291 static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
4292 {
4293     const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
4294     const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
4295     if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
4296         return d;
4297     if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
4298         return d;
4299     return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
4300 }
4301 
AddWindowToSortBuffer(ImVector<ImGuiWindow * > * out_sorted_windows,ImGuiWindow * window)4302 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
4303 {
4304     out_sorted_windows->push_back(window);
4305     if (window->Active)
4306     {
4307         int count = window->DC.ChildWindows.Size;
4308         if (count > 1)
4309             ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
4310         for (int i = 0; i < count; i++)
4311         {
4312             ImGuiWindow* child = window->DC.ChildWindows[i];
4313             if (child->Active)
4314                 AddWindowToSortBuffer(out_sorted_windows, child);
4315         }
4316     }
4317 }
4318 
AddDrawListToDrawData(ImVector<ImDrawList * > * out_list,ImDrawList * draw_list)4319 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
4320 {
4321     // Remove trailing command if unused.
4322     // Technically we could return directly instead of popping, but this make things looks neat in Metrics/Debugger window as well.
4323     draw_list->_PopUnusedDrawCmd();
4324     if (draw_list->CmdBuffer.Size == 0)
4325         return;
4326 
4327     // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
4328     // May trigger for you if you are using PrimXXX functions incorrectly.
4329     IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
4330     IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
4331     if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset))
4332         IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
4333 
4334     // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
4335     // If this assert triggers because you are drawing lots of stuff manually:
4336     // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds.
4337     //   Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents.
4338     // - If you want large meshes with more than 64K vertices, you can either:
4339     //   (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'.
4340     //       Most example backends already support this from 1.71. Pre-1.71 backends won't.
4341     //       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.
4342     //   (B) Or handle 32-bit indices in your renderer backend, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h.
4343     //       Most example backends already support this. For example, the OpenGL example code detect index size at compile-time:
4344     //         glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
4345     //       Your own engine or render API may use different parameters or function calls to specify index sizes.
4346     //       2 and 4 bytes indices are generally supported by most graphics API.
4347     // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching
4348     //   the 64K limit to split your draw commands in multiple draw lists.
4349     if (sizeof(ImDrawIdx) == 2)
4350         IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
4351 
4352     out_list->push_back(draw_list);
4353 }
4354 
AddWindowToDrawData(ImGuiWindow * window,int layer)4355 static void AddWindowToDrawData(ImGuiWindow* window, int layer)
4356 {
4357     ImGuiContext& g = *GImGui;
4358     ImGuiViewportP* viewport = g.Viewports[0];
4359     g.IO.MetricsRenderWindows++;
4360     AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[layer], window->DrawList);
4361     for (int i = 0; i < window->DC.ChildWindows.Size; i++)
4362     {
4363         ImGuiWindow* child = window->DC.ChildWindows[i];
4364         if (IsWindowActiveAndVisible(child)) // Clipped children may have been marked not active
4365             AddWindowToDrawData(child, layer);
4366     }
4367 }
4368 
4369 // Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)
AddRootWindowToDrawData(ImGuiWindow * window)4370 static void AddRootWindowToDrawData(ImGuiWindow* window)
4371 {
4372     int layer = (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0;
4373     AddWindowToDrawData(window, layer);
4374 }
4375 
FlattenIntoSingleLayer()4376 void ImDrawDataBuilder::FlattenIntoSingleLayer()
4377 {
4378     int n = Layers[0].Size;
4379     int size = n;
4380     for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
4381         size += Layers[i].Size;
4382     Layers[0].resize(size);
4383     for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
4384     {
4385         ImVector<ImDrawList*>& layer = Layers[layer_n];
4386         if (layer.empty())
4387             continue;
4388         memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
4389         n += layer.Size;
4390         layer.resize(0);
4391     }
4392 }
4393 
SetupViewportDrawData(ImGuiViewportP * viewport,ImVector<ImDrawList * > * draw_lists)4394 static void SetupViewportDrawData(ImGuiViewportP* viewport, ImVector<ImDrawList*>* draw_lists)
4395 {
4396     ImGuiIO& io = ImGui::GetIO();
4397     ImDrawData* draw_data = &viewport->DrawDataP;
4398     draw_data->Valid = true;
4399     draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
4400     draw_data->CmdListsCount = draw_lists->Size;
4401     draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
4402     draw_data->DisplayPos = viewport->Pos;
4403     draw_data->DisplaySize = viewport->Size;
4404     draw_data->FramebufferScale = io.DisplayFramebufferScale;
4405     for (int n = 0; n < draw_lists->Size; n++)
4406     {
4407         draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size;
4408         draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size;
4409     }
4410 }
4411 
4412 // Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering.
4413 // - When using this function it is sane to ensure that float are perfectly rounded to integer values,
4414 //   so that e.g. (int)(max.x-min.x) in user's render produce correct result.
4415 // - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect():
4416 //   some frequently called functions which to modify both channels and clipping simultaneously tend to use the
4417 //   more specialized SetWindowClipRectBeforeSetChannel() to avoid extraneous updates of underlying ImDrawCmds.
PushClipRect(const ImVec2 & clip_rect_min,const ImVec2 & clip_rect_max,bool intersect_with_current_clip_rect)4418 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
4419 {
4420     ImGuiWindow* window = GetCurrentWindow();
4421     window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
4422     window->ClipRect = window->DrawList->_ClipRectStack.back();
4423 }
4424 
PopClipRect()4425 void ImGui::PopClipRect()
4426 {
4427     ImGuiWindow* window = GetCurrentWindow();
4428     window->DrawList->PopClipRect();
4429     window->ClipRect = window->DrawList->_ClipRectStack.back();
4430 }
4431 
4432 // 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()4433 void ImGui::EndFrame()
4434 {
4435     ImGuiContext& g = *GImGui;
4436     IM_ASSERT(g.Initialized);
4437 
4438     // Don't process EndFrame() multiple times.
4439     if (g.FrameCountEnded == g.FrameCount)
4440         return;
4441     IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?");
4442 
4443     CallContextHooks(&g, ImGuiContextHookType_EndFramePre);
4444 
4445     ErrorCheckEndFrameSanityChecks();
4446 
4447     // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
4448     if (g.IO.ImeSetInputScreenPosFn && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f))
4449     {
4450         g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y);
4451         g.PlatformImeLastPos = g.PlatformImePos;
4452     }
4453 
4454     // Hide implicit/fallback "Debug" window if it hasn't been used
4455     g.WithinFrameScopeWithImplicitWindow = false;
4456     if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
4457         g.CurrentWindow->Active = false;
4458     End();
4459 
4460     // Update navigation: CTRL+Tab, wrap-around requests
4461     NavEndFrame();
4462 
4463     // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
4464     if (g.DragDropActive)
4465     {
4466         bool is_delivered = g.DragDropPayload.Delivery;
4467         bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton));
4468         if (is_delivered || is_elapsed)
4469             ClearDragDrop();
4470     }
4471 
4472     // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.
4473     if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
4474     {
4475         g.DragDropWithinSource = true;
4476         SetTooltip("...");
4477         g.DragDropWithinSource = false;
4478     }
4479 
4480     // End frame
4481     g.WithinFrameScope = false;
4482     g.FrameCountEnded = g.FrameCount;
4483 
4484     // Initiate moving window + handle left-click and right-click focus
4485     UpdateMouseMovingWindowEndFrame();
4486 
4487     // Sort the window list so that all child windows are after their parent
4488     // We cannot do that on FocusWindow() because children may not exist yet
4489     g.WindowsTempSortBuffer.resize(0);
4490     g.WindowsTempSortBuffer.reserve(g.Windows.Size);
4491     for (int i = 0; i != g.Windows.Size; i++)
4492     {
4493         ImGuiWindow* window = g.Windows[i];
4494         if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow))       // if a child is active its parent will add it
4495             continue;
4496         AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window);
4497     }
4498 
4499     // 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.
4500     IM_ASSERT(g.Windows.Size == g.WindowsTempSortBuffer.Size);
4501     g.Windows.swap(g.WindowsTempSortBuffer);
4502     g.IO.MetricsActiveWindows = g.WindowsActiveCount;
4503 
4504     // Unlock font atlas
4505     g.IO.Fonts->Locked = false;
4506 
4507     // Clear Input data for next frame
4508     g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
4509     g.IO.InputQueueCharacters.resize(0);
4510     g.IO.KeyModsPrev = g.IO.KeyMods; // doing it here is better than in NewFrame() as we'll tolerate backend writing to KeyMods. If we want to firmly disallow it we should detect it.
4511     memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
4512 
4513     CallContextHooks(&g, ImGuiContextHookType_EndFramePost);
4514 }
4515 
4516 // Prepare the data for rendering so you can call GetDrawData()
4517 // (As with anything within the ImGui:: namspace this doesn't touch your GPU or graphics API at all:
4518 // it is the role of the ImGui_ImplXXXX_RenderDrawData() function provided by the renderer backend)
Render()4519 void ImGui::Render()
4520 {
4521     ImGuiContext& g = *GImGui;
4522     IM_ASSERT(g.Initialized);
4523 
4524     if (g.FrameCountEnded != g.FrameCount)
4525         EndFrame();
4526     g.FrameCountRendered = g.FrameCount;
4527     g.IO.MetricsRenderWindows = 0;
4528 
4529     CallContextHooks(&g, ImGuiContextHookType_RenderPre);
4530 
4531     // Add background ImDrawList (for each active viewport)
4532     for (int n = 0; n != g.Viewports.Size; n++)
4533     {
4534         ImGuiViewportP* viewport = g.Viewports[n];
4535         viewport->DrawDataBuilder.Clear();
4536         if (viewport->DrawLists[0] != NULL)
4537             AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport));
4538     }
4539 
4540     // Add ImDrawList to render
4541     ImGuiWindow* windows_to_render_top_most[2];
4542     windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
4543     windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL);
4544     for (int n = 0; n != g.Windows.Size; n++)
4545     {
4546         ImGuiWindow* window = g.Windows[n];
4547         IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'"
4548         if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])
4549             AddRootWindowToDrawData(window);
4550     }
4551     for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++)
4552         if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window
4553             AddRootWindowToDrawData(windows_to_render_top_most[n]);
4554 
4555     // Setup ImDrawData structures for end-user
4556     g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0;
4557     for (int n = 0; n < g.Viewports.Size; n++)
4558     {
4559         ImGuiViewportP* viewport = g.Viewports[n];
4560         viewport->DrawDataBuilder.FlattenIntoSingleLayer();
4561 
4562         // Draw software mouse cursor if requested by io.MouseDrawCursor flag
4563         if (g.IO.MouseDrawCursor)
4564             RenderMouseCursor(GetForegroundDrawList(viewport), g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
4565 
4566         // Add foreground ImDrawList (for each active viewport)
4567         if (viewport->DrawLists[1] != NULL)
4568             AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport));
4569 
4570         SetupViewportDrawData(viewport, &viewport->DrawDataBuilder.Layers[0]);
4571         ImDrawData* draw_data = &viewport->DrawDataP;
4572         g.IO.MetricsRenderVertices += draw_data->TotalVtxCount;
4573         g.IO.MetricsRenderIndices += draw_data->TotalIdxCount;
4574     }
4575 
4576     CallContextHooks(&g, ImGuiContextHookType_RenderPost);
4577 }
4578 
4579 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
4580 // 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)4581 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
4582 {
4583     ImGuiContext& g = *GImGui;
4584 
4585     const char* text_display_end;
4586     if (hide_text_after_double_hash)
4587         text_display_end = FindRenderedTextEnd(text, text_end);      // Hide anything after a '##' string
4588     else
4589         text_display_end = text_end;
4590 
4591     ImFont* font = g.Font;
4592     const float font_size = g.FontSize;
4593     if (text == text_display_end)
4594         return ImVec2(0.0f, font_size);
4595     ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
4596 
4597     // Round
4598     // FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out.
4599     // FIXME: Investigate using ceilf or e.g.
4600     // - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c
4601     // - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html
4602     text_size.x = IM_FLOOR(text_size.x + 0.99999f);
4603 
4604     return text_size;
4605 }
4606 
4607 // Find window given position, search front-to-back
4608 // FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically
4609 // with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
4610 // called, aka before the next Begin(). Moving window isn't affected.
FindHoveredWindow()4611 static void FindHoveredWindow()
4612 {
4613     ImGuiContext& g = *GImGui;
4614 
4615     ImGuiWindow* hovered_window = NULL;
4616     ImGuiWindow* hovered_window_ignoring_moving_window = NULL;
4617     if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
4618         hovered_window = g.MovingWindow;
4619 
4620     ImVec2 padding_regular = g.Style.TouchExtraPadding;
4621     ImVec2 padding_for_resize = g.IO.ConfigWindowsResizeFromEdges ? g.WindowsHoverPadding : padding_regular;
4622     for (int i = g.Windows.Size - 1; i >= 0; i--)
4623     {
4624         ImGuiWindow* window = g.Windows[i];
4625         IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
4626         if (!window->Active || window->Hidden)
4627             continue;
4628         if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
4629             continue;
4630 
4631         // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
4632         ImRect bb(window->OuterRectClipped);
4633         if (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize))
4634             bb.Expand(padding_regular);
4635         else
4636             bb.Expand(padding_for_resize);
4637         if (!bb.Contains(g.IO.MousePos))
4638             continue;
4639 
4640         // Support for one rectangular hole in any given window
4641         // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512)
4642         if (window->HitTestHoleSize.x != 0)
4643         {
4644             ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y);
4645             ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y);
4646             if (ImRect(hole_pos, hole_pos + hole_size).Contains(g.IO.MousePos))
4647                 continue;
4648         }
4649 
4650         if (hovered_window == NULL)
4651             hovered_window = window;
4652         IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
4653         if (hovered_window_ignoring_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow))
4654             hovered_window_ignoring_moving_window = window;
4655         if (hovered_window && hovered_window_ignoring_moving_window)
4656             break;
4657     }
4658 
4659     g.HoveredWindow = hovered_window;
4660     g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window;
4661 }
4662 
4663 // Test if mouse cursor is hovering given rectangle
4664 // NB- Rectangle is clipped by our current clip setting
4665 // 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)4666 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
4667 {
4668     ImGuiContext& g = *GImGui;
4669 
4670     // Clip
4671     ImRect rect_clipped(r_min, r_max);
4672     if (clip)
4673         rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
4674 
4675     // Expand for touch input
4676     const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
4677     if (!rect_for_touch.Contains(g.IO.MousePos))
4678         return false;
4679     return true;
4680 }
4681 
GetKeyIndex(ImGuiKey imgui_key)4682 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
4683 {
4684     IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
4685     ImGuiContext& g = *GImGui;
4686     return g.IO.KeyMap[imgui_key];
4687 }
4688 
4689 // Note that dear imgui doesn't know the semantic of each entry of io.KeysDown[]!
4690 // Use your own indices/enums according to how your backend/engine stored them into io.KeysDown[]!
IsKeyDown(int user_key_index)4691 bool ImGui::IsKeyDown(int user_key_index)
4692 {
4693     if (user_key_index < 0)
4694         return false;
4695     ImGuiContext& g = *GImGui;
4696     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4697     return g.IO.KeysDown[user_key_index];
4698 }
4699 
4700 // t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
4701 // t1 = current time (e.g.: g.Time)
4702 // An event is triggered at:
4703 //  t = 0.0f     t = repeat_delay,    t = repeat_delay + repeat_rate*N
CalcTypematicRepeatAmount(float t0,float t1,float repeat_delay,float repeat_rate)4704 int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)
4705 {
4706     if (t1 == 0.0f)
4707         return 1;
4708     if (t0 >= t1)
4709         return 0;
4710     if (repeat_rate <= 0.0f)
4711         return (t0 < repeat_delay) && (t1 >= repeat_delay);
4712     const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);
4713     const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);
4714     const int count = count_t1 - count_t0;
4715     return count;
4716 }
4717 
GetKeyPressedAmount(int key_index,float repeat_delay,float repeat_rate)4718 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
4719 {
4720     ImGuiContext& g = *GImGui;
4721     if (key_index < 0)
4722         return 0;
4723     IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4724     const float t = g.IO.KeysDownDuration[key_index];
4725     return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
4726 }
4727 
IsKeyPressed(int user_key_index,bool repeat)4728 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
4729 {
4730     ImGuiContext& g = *GImGui;
4731     if (user_key_index < 0)
4732         return false;
4733     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4734     const float t = g.IO.KeysDownDuration[user_key_index];
4735     if (t == 0.0f)
4736         return true;
4737     if (repeat && t > g.IO.KeyRepeatDelay)
4738         return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
4739     return false;
4740 }
4741 
IsKeyReleased(int user_key_index)4742 bool ImGui::IsKeyReleased(int user_key_index)
4743 {
4744     ImGuiContext& g = *GImGui;
4745     if (user_key_index < 0) return false;
4746     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4747     return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
4748 }
4749 
IsMouseDown(ImGuiMouseButton button)4750 bool ImGui::IsMouseDown(ImGuiMouseButton button)
4751 {
4752     ImGuiContext& g = *GImGui;
4753     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4754     return g.IO.MouseDown[button];
4755 }
4756 
IsMouseClicked(ImGuiMouseButton button,bool repeat)4757 bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)
4758 {
4759     ImGuiContext& g = *GImGui;
4760     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4761     const float t = g.IO.MouseDownDuration[button];
4762     if (t == 0.0f)
4763         return true;
4764 
4765     if (repeat && t > g.IO.KeyRepeatDelay)
4766     {
4767         // 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.
4768         int amount = CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.50f);
4769         if (amount > 0)
4770             return true;
4771     }
4772     return false;
4773 }
4774 
IsMouseReleased(ImGuiMouseButton button)4775 bool ImGui::IsMouseReleased(ImGuiMouseButton button)
4776 {
4777     ImGuiContext& g = *GImGui;
4778     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4779     return g.IO.MouseReleased[button];
4780 }
4781 
IsMouseDoubleClicked(ImGuiMouseButton button)4782 bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
4783 {
4784     ImGuiContext& g = *GImGui;
4785     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4786     return g.IO.MouseDoubleClicked[button];
4787 }
4788 
4789 // Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame.
4790 // [Internal] This doesn't test if the button is pressed
IsMouseDragPastThreshold(ImGuiMouseButton button,float lock_threshold)4791 bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold)
4792 {
4793     ImGuiContext& g = *GImGui;
4794     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4795     if (lock_threshold < 0.0f)
4796         lock_threshold = g.IO.MouseDragThreshold;
4797     return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
4798 }
4799 
IsMouseDragging(ImGuiMouseButton button,float lock_threshold)4800 bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold)
4801 {
4802     ImGuiContext& g = *GImGui;
4803     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4804     if (!g.IO.MouseDown[button])
4805         return false;
4806     return IsMouseDragPastThreshold(button, lock_threshold);
4807 }
4808 
GetMousePos()4809 ImVec2 ImGui::GetMousePos()
4810 {
4811     ImGuiContext& g = *GImGui;
4812     return g.IO.MousePos;
4813 }
4814 
4815 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()4816 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
4817 {
4818     ImGuiContext& g = *GImGui;
4819     if (g.BeginPopupStack.Size > 0)
4820         return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos;
4821     return g.IO.MousePos;
4822 }
4823 
4824 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
IsMousePosValid(const ImVec2 * mouse_pos)4825 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
4826 {
4827     // The assert is only to silence a false-positive in XCode Static Analysis.
4828     // 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).
4829     IM_ASSERT(GImGui != NULL);
4830     const float MOUSE_INVALID = -256000.0f;
4831     ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
4832     return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
4833 }
4834 
IsAnyMouseDown()4835 bool ImGui::IsAnyMouseDown()
4836 {
4837     ImGuiContext& g = *GImGui;
4838     for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
4839         if (g.IO.MouseDown[n])
4840             return true;
4841     return false;
4842 }
4843 
4844 // Return the delta from the initial clicking position while the mouse button is clicked or was just released.
4845 // This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
4846 // NB: This is only valid if IsMousePosValid(). backends in theory should always keep mouse position valid when dragging even outside the client window.
GetMouseDragDelta(ImGuiMouseButton button,float lock_threshold)4847 ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold)
4848 {
4849     ImGuiContext& g = *GImGui;
4850     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4851     if (lock_threshold < 0.0f)
4852         lock_threshold = g.IO.MouseDragThreshold;
4853     if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])
4854         if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
4855             if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))
4856                 return g.IO.MousePos - g.IO.MouseClickedPos[button];
4857     return ImVec2(0.0f, 0.0f);
4858 }
4859 
ResetMouseDragDelta(ImGuiMouseButton button)4860 void ImGui::ResetMouseDragDelta(ImGuiMouseButton button)
4861 {
4862     ImGuiContext& g = *GImGui;
4863     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4864     // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
4865     g.IO.MouseClickedPos[button] = g.IO.MousePos;
4866 }
4867 
GetMouseCursor()4868 ImGuiMouseCursor ImGui::GetMouseCursor()
4869 {
4870     return GImGui->MouseCursor;
4871 }
4872 
SetMouseCursor(ImGuiMouseCursor cursor_type)4873 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
4874 {
4875     GImGui->MouseCursor = cursor_type;
4876 }
4877 
CaptureKeyboardFromApp(bool capture)4878 void ImGui::CaptureKeyboardFromApp(bool capture)
4879 {
4880     GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
4881 }
4882 
CaptureMouseFromApp(bool capture)4883 void ImGui::CaptureMouseFromApp(bool capture)
4884 {
4885     GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
4886 }
4887 
IsItemActive()4888 bool ImGui::IsItemActive()
4889 {
4890     ImGuiContext& g = *GImGui;
4891     if (g.ActiveId)
4892         return g.ActiveId == g.LastItemData.ID;
4893     return false;
4894 }
4895 
IsItemActivated()4896 bool ImGui::IsItemActivated()
4897 {
4898     ImGuiContext& g = *GImGui;
4899     if (g.ActiveId)
4900         if (g.ActiveId == g.LastItemData.ID && g.ActiveIdPreviousFrame != g.LastItemData.ID)
4901             return true;
4902     return false;
4903 }
4904 
IsItemDeactivated()4905 bool ImGui::IsItemDeactivated()
4906 {
4907     ImGuiContext& g = *GImGui;
4908     if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDeactivated)
4909         return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Deactivated) != 0;
4910     return (g.ActiveIdPreviousFrame == g.LastItemData.ID && g.ActiveIdPreviousFrame != 0 && g.ActiveId != g.LastItemData.ID);
4911 }
4912 
IsItemDeactivatedAfterEdit()4913 bool ImGui::IsItemDeactivatedAfterEdit()
4914 {
4915     ImGuiContext& g = *GImGui;
4916     return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore));
4917 }
4918 
4919 // == GetItemID() == GetFocusID()
IsItemFocused()4920 bool ImGui::IsItemFocused()
4921 {
4922     ImGuiContext& g = *GImGui;
4923     if (g.NavId != g.LastItemData.ID || g.NavId == 0)
4924         return false;
4925     return true;
4926 }
4927 
4928 // Important: this can be useful but it is NOT equivalent to the behavior of e.g.Button()!
4929 // Most widgets have specific reactions based on mouse-up/down state, mouse position etc.
IsItemClicked(ImGuiMouseButton mouse_button)4930 bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button)
4931 {
4932     return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
4933 }
4934 
IsItemToggledOpen()4935 bool ImGui::IsItemToggledOpen()
4936 {
4937     ImGuiContext& g = *GImGui;
4938     return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false;
4939 }
4940 
IsItemToggledSelection()4941 bool ImGui::IsItemToggledSelection()
4942 {
4943     ImGuiContext& g = *GImGui;
4944     return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;
4945 }
4946 
IsAnyItemHovered()4947 bool ImGui::IsAnyItemHovered()
4948 {
4949     ImGuiContext& g = *GImGui;
4950     return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
4951 }
4952 
IsAnyItemActive()4953 bool ImGui::IsAnyItemActive()
4954 {
4955     ImGuiContext& g = *GImGui;
4956     return g.ActiveId != 0;
4957 }
4958 
IsAnyItemFocused()4959 bool ImGui::IsAnyItemFocused()
4960 {
4961     ImGuiContext& g = *GImGui;
4962     return g.NavId != 0 && !g.NavDisableHighlight;
4963 }
4964 
IsItemVisible()4965 bool ImGui::IsItemVisible()
4966 {
4967     ImGuiContext& g = *GImGui;
4968     return g.CurrentWindow->ClipRect.Overlaps(g.LastItemData.Rect);
4969 }
4970 
IsItemEdited()4971 bool ImGui::IsItemEdited()
4972 {
4973     ImGuiContext& g = *GImGui;
4974     return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Edited) != 0;
4975 }
4976 
4977 // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
4978 // FIXME: Although this is exposed, its interaction and ideal idiom with using ImGuiButtonFlags_AllowItemOverlap flag are extremely confusing, need rework.
SetItemAllowOverlap()4979 void ImGui::SetItemAllowOverlap()
4980 {
4981     ImGuiContext& g = *GImGui;
4982     ImGuiID id = g.LastItemData.ID;
4983     if (g.HoveredId == id)
4984         g.HoveredIdAllowOverlap = true;
4985     if (g.ActiveId == id)
4986         g.ActiveIdAllowOverlap = true;
4987 }
4988 
SetItemUsingMouseWheel()4989 void ImGui::SetItemUsingMouseWheel()
4990 {
4991     ImGuiContext& g = *GImGui;
4992     ImGuiID id = g.LastItemData.ID;
4993     if (g.HoveredId == id)
4994         g.HoveredIdUsingMouseWheel = true;
4995     if (g.ActiveId == id)
4996         g.ActiveIdUsingMouseWheel = true;
4997 }
4998 
SetActiveIdUsingNavAndKeys()4999 void ImGui::SetActiveIdUsingNavAndKeys()
5000 {
5001     ImGuiContext& g = *GImGui;
5002     IM_ASSERT(g.ActiveId != 0);
5003     g.ActiveIdUsingNavDirMask = ~(ImU32)0;
5004     g.ActiveIdUsingNavInputMask = ~(ImU32)0;
5005     g.ActiveIdUsingKeyInputMask = ~(ImU64)0;
5006     NavMoveRequestCancel();
5007 }
5008 
GetItemRectMin()5009 ImVec2 ImGui::GetItemRectMin()
5010 {
5011     ImGuiContext& g = *GImGui;
5012     return g.LastItemData.Rect.Min;
5013 }
5014 
GetItemRectMax()5015 ImVec2 ImGui::GetItemRectMax()
5016 {
5017     ImGuiContext& g = *GImGui;
5018     return g.LastItemData.Rect.Max;
5019 }
5020 
GetItemRectSize()5021 ImVec2 ImGui::GetItemRectSize()
5022 {
5023     ImGuiContext& g = *GImGui;
5024     return g.LastItemData.Rect.GetSize();
5025 }
5026 
BeginChildEx(const char * name,ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags flags)5027 bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags)
5028 {
5029     ImGuiContext& g = *GImGui;
5030     ImGuiWindow* parent_window = g.CurrentWindow;
5031 
5032     flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_ChildWindow;
5033     flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove);  // Inherit the NoMove flag
5034 
5035     // Size
5036     const ImVec2 content_avail = GetContentRegionAvail();
5037     ImVec2 size = ImFloor(size_arg);
5038     const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
5039     if (size.x <= 0.0f)
5040         size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
5041     if (size.y <= 0.0f)
5042         size.y = ImMax(content_avail.y + size.y, 4.0f);
5043     SetNextWindowSize(size);
5044 
5045     // 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.
5046     if (name)
5047         ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%s/%s_%08X", parent_window->Name, name, id);
5048     else
5049         ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%s/%08X", parent_window->Name, id);
5050 
5051     const float backup_border_size = g.Style.ChildBorderSize;
5052     if (!border)
5053         g.Style.ChildBorderSize = 0.0f;
5054     bool ret = Begin(g.TempBuffer, NULL, flags);
5055     g.Style.ChildBorderSize = backup_border_size;
5056 
5057     ImGuiWindow* child_window = g.CurrentWindow;
5058     child_window->ChildId = id;
5059     child_window->AutoFitChildAxises = (ImS8)auto_fit_axises;
5060 
5061     // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
5062     // While this is not really documented/defined, it seems that the expected thing to do.
5063     if (child_window->BeginCount == 1)
5064         parent_window->DC.CursorPos = child_window->Pos;
5065 
5066     // Process navigation-in immediately so NavInit can run on first frame
5067     if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavHasScroll))
5068     {
5069         FocusWindow(child_window);
5070         NavInitWindow(child_window, false);
5071         SetActiveID(id + 1, child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item
5072         g.ActiveIdSource = ImGuiInputSource_Nav;
5073     }
5074     return ret;
5075 }
5076 
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)5077 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
5078 {
5079     ImGuiWindow* window = GetCurrentWindow();
5080     return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
5081 }
5082 
BeginChild(ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)5083 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
5084 {
5085     IM_ASSERT(id != 0);
5086     return BeginChildEx(NULL, id, size_arg, border, extra_flags);
5087 }
5088 
EndChild()5089 void ImGui::EndChild()
5090 {
5091     ImGuiContext& g = *GImGui;
5092     ImGuiWindow* window = g.CurrentWindow;
5093 
5094     IM_ASSERT(g.WithinEndChild == false);
5095     IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow);   // Mismatched BeginChild()/EndChild() calls
5096 
5097     g.WithinEndChild = true;
5098     if (window->BeginCount > 1)
5099     {
5100         End();
5101     }
5102     else
5103     {
5104         ImVec2 sz = window->Size;
5105         if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
5106             sz.x = ImMax(4.0f, sz.x);
5107         if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
5108             sz.y = ImMax(4.0f, sz.y);
5109         End();
5110 
5111         ImGuiWindow* parent_window = g.CurrentWindow;
5112         ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
5113         ItemSize(sz);
5114         if ((window->DC.NavLayersActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
5115         {
5116             ItemAdd(bb, window->ChildId);
5117             RenderNavHighlight(bb, window->ChildId);
5118 
5119             // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
5120             if (window->DC.NavLayersActiveMask == 0 && window == g.NavWindow)
5121                 RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
5122         }
5123         else
5124         {
5125             // Not navigable into
5126             ItemAdd(bb, 0);
5127         }
5128         if (g.HoveredWindow == window)
5129             g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
5130     }
5131     g.WithinEndChild = false;
5132     g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
5133 }
5134 
5135 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)5136 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
5137 {
5138     ImGuiContext& g = *GImGui;
5139     const ImGuiStyle& style = g.Style;
5140     PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
5141     PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
5142     PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
5143     PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
5144     bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
5145     PopStyleVar(3);
5146     PopStyleColor();
5147     return ret;
5148 }
5149 
EndChildFrame()5150 void ImGui::EndChildFrame()
5151 {
5152     EndChild();
5153 }
5154 
SetWindowConditionAllowFlags(ImGuiWindow * window,ImGuiCond flags,bool enabled)5155 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
5156 {
5157     window->SetWindowPosAllowFlags       = enabled ? (window->SetWindowPosAllowFlags       | flags) : (window->SetWindowPosAllowFlags       & ~flags);
5158     window->SetWindowSizeAllowFlags      = enabled ? (window->SetWindowSizeAllowFlags      | flags) : (window->SetWindowSizeAllowFlags      & ~flags);
5159     window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
5160 }
5161 
FindWindowByID(ImGuiID id)5162 ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
5163 {
5164     ImGuiContext& g = *GImGui;
5165     return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
5166 }
5167 
FindWindowByName(const char * name)5168 ImGuiWindow* ImGui::FindWindowByName(const char* name)
5169 {
5170     ImGuiID id = ImHashStr(name);
5171     return FindWindowByID(id);
5172 }
5173 
ApplyWindowSettings(ImGuiWindow * window,ImGuiWindowSettings * settings)5174 static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
5175 {
5176     window->Pos = ImFloor(ImVec2(settings->Pos.x, settings->Pos.y));
5177     if (settings->Size.x > 0 && settings->Size.y > 0)
5178         window->Size = window->SizeFull = ImFloor(ImVec2(settings->Size.x, settings->Size.y));
5179     window->Collapsed = settings->Collapsed;
5180 }
5181 
CreateNewWindow(const char * name,ImGuiWindowFlags flags)5182 static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags)
5183 {
5184     ImGuiContext& g = *GImGui;
5185     //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags);
5186 
5187     // Create window the first time
5188     ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
5189     window->Flags = flags;
5190     g.WindowsById.SetVoidPtr(window->ID, window);
5191 
5192     // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
5193     const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
5194     window->Pos = main_viewport->Pos + ImVec2(60, 60);
5195 
5196     // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
5197     if (!(flags & ImGuiWindowFlags_NoSavedSettings))
5198         if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
5199         {
5200             // Retrieve settings from .ini file
5201             window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
5202             SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
5203             ApplyWindowSettings(window, settings);
5204         }
5205     window->DC.CursorStartPos = window->DC.CursorMaxPos = window->Pos; // So first call to CalcContentSize() doesn't return crazy values
5206 
5207     if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
5208     {
5209         window->AutoFitFramesX = window->AutoFitFramesY = 2;
5210         window->AutoFitOnlyGrows = false;
5211     }
5212     else
5213     {
5214         if (window->Size.x <= 0.0f)
5215             window->AutoFitFramesX = 2;
5216         if (window->Size.y <= 0.0f)
5217             window->AutoFitFramesY = 2;
5218         window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
5219     }
5220 
5221     if (!(flags & ImGuiWindowFlags_ChildWindow))
5222     {
5223         g.WindowsFocusOrder.push_back(window);
5224         window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1);
5225     }
5226 
5227     if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
5228         g.Windows.push_front(window); // Quite slow but rare and only once
5229     else
5230         g.Windows.push_back(window);
5231     return window;
5232 }
5233 
CalcWindowSizeAfterConstraint(ImGuiWindow * window,const ImVec2 & size_desired)5234 static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& size_desired)
5235 {
5236     ImGuiContext& g = *GImGui;
5237     ImVec2 new_size = size_desired;
5238     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)
5239     {
5240         // Using -1,-1 on either X/Y axis to preserve the current size.
5241         ImRect cr = g.NextWindowData.SizeConstraintRect;
5242         new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
5243         new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
5244         if (g.NextWindowData.SizeCallback)
5245         {
5246             ImGuiSizeCallbackData data;
5247             data.UserData = g.NextWindowData.SizeCallbackUserData;
5248             data.Pos = window->Pos;
5249             data.CurrentSize = window->SizeFull;
5250             data.DesiredSize = new_size;
5251             g.NextWindowData.SizeCallback(&data);
5252             new_size = data.DesiredSize;
5253         }
5254         new_size.x = IM_FLOOR(new_size.x);
5255         new_size.y = IM_FLOOR(new_size.y);
5256     }
5257 
5258     // Minimum size
5259     if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
5260     {
5261         ImGuiWindow* window_for_height = window;
5262         const float decoration_up_height = window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight();
5263         new_size = ImMax(new_size, g.Style.WindowMinSize);
5264         new_size.y = ImMax(new_size.y, decoration_up_height + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows
5265     }
5266     return new_size;
5267 }
5268 
CalcWindowContentSizes(ImGuiWindow * window,ImVec2 * content_size_current,ImVec2 * content_size_ideal)5269 static void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_current, ImVec2* content_size_ideal)
5270 {
5271     bool preserve_old_content_sizes = false;
5272     if (window->Collapsed && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5273         preserve_old_content_sizes = true;
5274     else if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0)
5275         preserve_old_content_sizes = true;
5276     if (preserve_old_content_sizes)
5277     {
5278         *content_size_current = window->ContentSize;
5279         *content_size_ideal = window->ContentSizeIdeal;
5280         return;
5281     }
5282 
5283     content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_FLOOR(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);
5284     content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_FLOOR(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);
5285     content_size_ideal->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_FLOOR(ImMax(window->DC.CursorMaxPos.x, window->DC.IdealMaxPos.x) - window->DC.CursorStartPos.x);
5286     content_size_ideal->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_FLOOR(ImMax(window->DC.CursorMaxPos.y, window->DC.IdealMaxPos.y) - window->DC.CursorStartPos.y);
5287 }
5288 
CalcWindowAutoFitSize(ImGuiWindow * window,const ImVec2 & size_contents)5289 static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents)
5290 {
5291     ImGuiContext& g = *GImGui;
5292     ImGuiStyle& style = g.Style;
5293     const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
5294     ImVec2 size_pad = window->WindowPadding * 2.0f;
5295     ImVec2 size_desired = size_contents + size_pad + ImVec2(0.0f, decoration_up_height);
5296     if (window->Flags & ImGuiWindowFlags_Tooltip)
5297     {
5298         // Tooltip always resize
5299         return size_desired;
5300     }
5301     else
5302     {
5303         // Maximum window size is determined by the viewport size or monitor size
5304         const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0;
5305         const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0;
5306         ImVec2 size_min = style.WindowMinSize;
5307         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)
5308             size_min = ImMin(size_min, ImVec2(4.0f, 4.0f));
5309 
5310         // FIXME-VIEWPORT-WORKAREA: May want to use GetWorkSize() instead of Size depending on the type of windows?
5311         ImVec2 avail_size = ImGui::GetMainViewport()->Size;
5312         ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, avail_size - style.DisplaySafeAreaPadding * 2.0f));
5313 
5314         // When the window cannot fit all contents (either because of constraints, either because screen is too small),
5315         // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
5316         ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5317         bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - 0.0f                 < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar);
5318         bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - decoration_up_height < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar);
5319         if (will_have_scrollbar_x)
5320             size_auto_fit.y += style.ScrollbarSize;
5321         if (will_have_scrollbar_y)
5322             size_auto_fit.x += style.ScrollbarSize;
5323         return size_auto_fit;
5324     }
5325 }
5326 
CalcWindowNextAutoFitSize(ImGuiWindow * window)5327 ImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window)
5328 {
5329     ImVec2 size_contents_current;
5330     ImVec2 size_contents_ideal;
5331     CalcWindowContentSizes(window, &size_contents_current, &size_contents_ideal);
5332     ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents_ideal);
5333     ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5334     return size_final;
5335 }
5336 
GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)5337 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
5338 {
5339     if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
5340         return ImGuiCol_PopupBg;
5341     if (flags & ImGuiWindowFlags_ChildWindow)
5342         return ImGuiCol_ChildBg;
5343     return ImGuiCol_WindowBg;
5344 }
5345 
CalcResizePosSizeFromAnyCorner(ImGuiWindow * window,const ImVec2 & corner_target,const ImVec2 & corner_norm,ImVec2 * out_pos,ImVec2 * out_size)5346 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
5347 {
5348     ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm);                // Expected window upper-left
5349     ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
5350     ImVec2 size_expected = pos_max - pos_min;
5351     ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected);
5352     *out_pos = pos_min;
5353     if (corner_norm.x == 0.0f)
5354         out_pos->x -= (size_constrained.x - size_expected.x);
5355     if (corner_norm.y == 0.0f)
5356         out_pos->y -= (size_constrained.y - size_expected.y);
5357     *out_size = size_constrained;
5358 }
5359 
5360 // Data for resizing from corner
5361 struct ImGuiResizeGripDef
5362 {
5363     ImVec2  CornerPosN;
5364     ImVec2  InnerDir;
5365     int     AngleMin12, AngleMax12;
5366 };
5367 static const ImGuiResizeGripDef resize_grip_def[4] =
5368 {
5369     { ImVec2(1, 1), ImVec2(-1, -1), 0, 3 },  // Lower-right
5370     { ImVec2(0, 1), ImVec2(+1, -1), 3, 6 },  // Lower-left
5371     { ImVec2(0, 0), ImVec2(+1, +1), 6, 9 },  // Upper-left (Unused)
5372     { ImVec2(1, 0), ImVec2(-1, +1), 9, 12 }  // Upper-right (Unused)
5373 };
5374 
5375 // Data for resizing from borders
5376 struct ImGuiResizeBorderDef
5377 {
5378     ImVec2 InnerDir;
5379     ImVec2 SegmentN1, SegmentN2;
5380     float  OuterAngle;
5381 };
5382 static const ImGuiResizeBorderDef resize_border_def[4] =
5383 {
5384     { ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f }, // Left
5385     { ImVec2(-1, 0), ImVec2(1, 0), ImVec2(1, 1), IM_PI * 0.00f }, // Right
5386     { ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Up
5387     { ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f }  // Down
5388 };
5389 
GetResizeBorderRect(ImGuiWindow * window,int border_n,float perp_padding,float thickness)5390 static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
5391 {
5392     ImRect rect = window->Rect();
5393     if (thickness == 0.0f)
5394         rect.Max -= ImVec2(1, 1);
5395     if (border_n == ImGuiDir_Left)  { return ImRect(rect.Min.x - thickness,    rect.Min.y + perp_padding, rect.Min.x + thickness,    rect.Max.y - perp_padding); }
5396     if (border_n == ImGuiDir_Right) { return ImRect(rect.Max.x - thickness,    rect.Min.y + perp_padding, rect.Max.x + thickness,    rect.Max.y - perp_padding); }
5397     if (border_n == ImGuiDir_Up)    { return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness,    rect.Max.x - perp_padding, rect.Min.y + thickness);    }
5398     if (border_n == ImGuiDir_Down)  { return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness,    rect.Max.x - perp_padding, rect.Max.y + thickness);    }
5399     IM_ASSERT(0);
5400     return ImRect();
5401 }
5402 
5403 // 0..3: corners (Lower-right, Lower-left, Unused, Unused)
GetWindowResizeCornerID(ImGuiWindow * window,int n)5404 ImGuiID ImGui::GetWindowResizeCornerID(ImGuiWindow* window, int n)
5405 {
5406     IM_ASSERT(n >= 0 && n < 4);
5407     ImGuiID id = window->ID;
5408     id = ImHashStr("#RESIZE", 0, id);
5409     id = ImHashData(&n, sizeof(int), id);
5410     return id;
5411 }
5412 
5413 // Borders (Left, Right, Up, Down)
GetWindowResizeBorderID(ImGuiWindow * window,ImGuiDir dir)5414 ImGuiID ImGui::GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir)
5415 {
5416     IM_ASSERT(dir >= 0 && dir < 4);
5417     int n = (int)dir + 4;
5418     ImGuiID id = window->ID;
5419     id = ImHashStr("#RESIZE", 0, id);
5420     id = ImHashData(&n, sizeof(int), id);
5421     return id;
5422 }
5423 
5424 // Handle resize for: Resize Grips, Borders, Gamepad
5425 // Return true when using auto-fit (double click on resize grip)
UpdateWindowManualResize(ImGuiWindow * window,const ImVec2 & size_auto_fit,int * border_held,int resize_grip_count,ImU32 resize_grip_col[4],const ImRect & visibility_rect)5426 static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect)
5427 {
5428     ImGuiContext& g = *GImGui;
5429     ImGuiWindowFlags flags = window->Flags;
5430 
5431     if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
5432         return false;
5433     if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window.
5434         return false;
5435 
5436     bool ret_auto_fit = false;
5437     const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0;
5438     const float grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
5439     const float grip_hover_inner_size = IM_FLOOR(grip_draw_size * 0.75f);
5440     const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_HOVER_PADDING : 0.0f;
5441 
5442     ImVec2 pos_target(FLT_MAX, FLT_MAX);
5443     ImVec2 size_target(FLT_MAX, FLT_MAX);
5444 
5445     // Resize grips and borders are on layer 1
5446     window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5447 
5448     // Manual resize grips
5449     PushID("#RESIZE");
5450     for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5451     {
5452         const ImGuiResizeGripDef& def = resize_grip_def[resize_grip_n];
5453         const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, def.CornerPosN);
5454 
5455         // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
5456         bool hovered, held;
5457         ImRect resize_rect(corner - def.InnerDir * grip_hover_outer_size, corner + def.InnerDir * grip_hover_inner_size);
5458         if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
5459         if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
5460         ImGuiID resize_grip_id = window->GetID(resize_grip_n); // == GetWindowResizeCornerID()
5461         ButtonBehavior(resize_rect, resize_grip_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
5462         //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
5463         if (hovered || held)
5464             g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
5465 
5466         if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
5467         {
5468             // Manual auto-fit when double-clicking
5469             size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5470             ret_auto_fit = true;
5471             ClearActiveID();
5472         }
5473         else if (held)
5474         {
5475             // Resize from any of the four corners
5476             // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
5477             ImVec2 clamp_min = ImVec2(def.CornerPosN.x == 1.0f ? visibility_rect.Min.x : -FLT_MAX, def.CornerPosN.y == 1.0f ? visibility_rect.Min.y : -FLT_MAX);
5478             ImVec2 clamp_max = ImVec2(def.CornerPosN.x == 0.0f ? visibility_rect.Max.x : +FLT_MAX, def.CornerPosN.y == 0.0f ? visibility_rect.Max.y : +FLT_MAX);
5479             ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(def.InnerDir * grip_hover_outer_size, def.InnerDir * -grip_hover_inner_size, def.CornerPosN); // Corner of the window corresponding to our corner grip
5480             corner_target = ImClamp(corner_target, clamp_min, clamp_max);
5481             CalcResizePosSizeFromAnyCorner(window, corner_target, def.CornerPosN, &pos_target, &size_target);
5482         }
5483 
5484         // Only lower-left grip is visible before hovering/activating
5485         if (resize_grip_n == 0 || held || hovered)
5486             resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
5487     }
5488     for (int border_n = 0; border_n < resize_border_count; border_n++)
5489     {
5490         const ImGuiResizeBorderDef& def = resize_border_def[border_n];
5491         const ImGuiAxis axis = (border_n == ImGuiDir_Left || border_n == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
5492 
5493         bool hovered, held;
5494         ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_HOVER_PADDING);
5495         ImGuiID border_id = window->GetID(border_n + 4); // == GetWindowResizeBorderID()
5496         ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren);
5497         //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
5498         if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held)
5499         {
5500             g.MouseCursor = (axis == ImGuiAxis_X) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
5501             if (held)
5502                 *border_held = border_n;
5503         }
5504         if (held)
5505         {
5506             ImVec2 clamp_min(border_n == ImGuiDir_Right ? visibility_rect.Min.x : -FLT_MAX, border_n == ImGuiDir_Down ? visibility_rect.Min.y : -FLT_MAX);
5507             ImVec2 clamp_max(border_n == ImGuiDir_Left  ? visibility_rect.Max.x : +FLT_MAX, border_n == ImGuiDir_Up   ? visibility_rect.Max.y : +FLT_MAX);
5508             ImVec2 border_target = window->Pos;
5509             border_target[axis] = g.IO.MousePos[axis] - g.ActiveIdClickOffset[axis] + WINDOWS_HOVER_PADDING;
5510             border_target = ImClamp(border_target, clamp_min, clamp_max);
5511             CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2), &pos_target, &size_target);
5512         }
5513     }
5514     PopID();
5515 
5516     // Restore nav layer
5517     window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5518 
5519     // Navigation resize (keyboard/gamepad)
5520     if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
5521     {
5522         ImVec2 nav_resize_delta;
5523         if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift)
5524             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
5525         if (g.NavInputSource == ImGuiInputSource_Gamepad)
5526             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down);
5527         if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f)
5528         {
5529             const float NAV_RESIZE_SPEED = 600.0f;
5530             nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
5531             nav_resize_delta = ImMax(nav_resize_delta, visibility_rect.Min - window->Pos - window->Size);
5532             g.NavWindowingToggleLayer = false;
5533             g.NavDisableMouseHover = true;
5534             resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
5535             // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
5536             size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + nav_resize_delta);
5537         }
5538     }
5539 
5540     // Apply back modified position/size to window
5541     if (size_target.x != FLT_MAX)
5542     {
5543         window->SizeFull = size_target;
5544         MarkIniSettingsDirty(window);
5545     }
5546     if (pos_target.x != FLT_MAX)
5547     {
5548         window->Pos = ImFloor(pos_target);
5549         MarkIniSettingsDirty(window);
5550     }
5551 
5552     window->Size = window->SizeFull;
5553     return ret_auto_fit;
5554 }
5555 
ClampWindowRect(ImGuiWindow * window,const ImRect & visibility_rect)5556 static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& visibility_rect)
5557 {
5558     ImGuiContext& g = *GImGui;
5559     ImVec2 size_for_clamping = window->Size;
5560     if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
5561         size_for_clamping.y = window->TitleBarHeight();
5562     window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max);
5563 }
5564 
RenderWindowOuterBorders(ImGuiWindow * window)5565 static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
5566 {
5567     ImGuiContext& g = *GImGui;
5568     float rounding = window->WindowRounding;
5569     float border_size = window->WindowBorderSize;
5570     if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground))
5571         window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
5572 
5573     int border_held = window->ResizeBorderHeld;
5574     if (border_held != -1)
5575     {
5576         const ImGuiResizeBorderDef& def = resize_border_def[border_held];
5577         ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f);
5578         window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI * 0.25f, def.OuterAngle);
5579         window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f);
5580         window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), 0, ImMax(2.0f, border_size)); // Thicker than usual
5581     }
5582     if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
5583     {
5584         float y = window->Pos.y + window->TitleBarHeight() - 1;
5585         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);
5586     }
5587 }
5588 
5589 // Draw background and borders
5590 // Draw and handle scrollbars
RenderWindowDecorations(ImGuiWindow * window,const ImRect & title_bar_rect,bool title_bar_is_highlight,int resize_grip_count,const ImU32 resize_grip_col[4],float resize_grip_draw_size)5591 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)
5592 {
5593     ImGuiContext& g = *GImGui;
5594     ImGuiStyle& style = g.Style;
5595     ImGuiWindowFlags flags = window->Flags;
5596 
5597     // Ensure that ScrollBar doesn't read last frame's SkipItems
5598     IM_ASSERT(window->BeginCount == 0);
5599     window->SkipItems = false;
5600 
5601     // Draw window + handle manual resize
5602     // 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.
5603     const float window_rounding = window->WindowRounding;
5604     const float window_border_size = window->WindowBorderSize;
5605     if (window->Collapsed)
5606     {
5607         // Title bar only
5608         float backup_border_size = style.FrameBorderSize;
5609         g.Style.FrameBorderSize = window->WindowBorderSize;
5610         ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
5611         RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
5612         g.Style.FrameBorderSize = backup_border_size;
5613     }
5614     else
5615     {
5616         // Window background
5617         if (!(flags & ImGuiWindowFlags_NoBackground))
5618         {
5619             ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
5620             bool override_alpha = false;
5621             float alpha = 1.0f;
5622             if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha)
5623             {
5624                 alpha = g.NextWindowData.BgAlphaVal;
5625                 override_alpha = true;
5626             }
5627             if (override_alpha)
5628                 bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
5629             window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom);
5630         }
5631 
5632         // Title bar
5633         if (!(flags & ImGuiWindowFlags_NoTitleBar))
5634         {
5635             ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
5636             window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawFlags_RoundCornersTop);
5637         }
5638 
5639         // Menu bar
5640         if (flags & ImGuiWindowFlags_MenuBar)
5641         {
5642             ImRect menu_bar_rect = window->MenuBarRect();
5643             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.
5644             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, ImDrawFlags_RoundCornersTop);
5645             if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
5646                 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
5647         }
5648 
5649         // Scrollbars
5650         if (window->ScrollbarX)
5651             Scrollbar(ImGuiAxis_X);
5652         if (window->ScrollbarY)
5653             Scrollbar(ImGuiAxis_Y);
5654 
5655         // Render resize grips (after their input handling so we don't have a frame of latency)
5656         if (!(flags & ImGuiWindowFlags_NoResize))
5657         {
5658             for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5659             {
5660                 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5661                 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
5662                 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)));
5663                 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)));
5664                 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);
5665                 window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]);
5666             }
5667         }
5668 
5669         // Borders
5670         RenderWindowOuterBorders(window);
5671     }
5672 }
5673 
5674 // Render title text, collapse button, close button
RenderWindowTitleBarContents(ImGuiWindow * window,const ImRect & title_bar_rect,const char * name,bool * p_open)5675 void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)
5676 {
5677     ImGuiContext& g = *GImGui;
5678     ImGuiStyle& style = g.Style;
5679     ImGuiWindowFlags flags = window->Flags;
5680 
5681     const bool has_close_button = (p_open != NULL);
5682     const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None);
5683 
5684     // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)
5685     const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags;
5686     g.CurrentItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
5687     window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5688 
5689     // Layout buttons
5690     // FIXME: Would be nice to generalize the subtleties expressed here into reusable code.
5691     float pad_l = style.FramePadding.x;
5692     float pad_r = style.FramePadding.x;
5693     float button_sz = g.FontSize;
5694     ImVec2 close_button_pos;
5695     ImVec2 collapse_button_pos;
5696     if (has_close_button)
5697     {
5698         pad_r += button_sz;
5699         close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5700     }
5701     if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)
5702     {
5703         pad_r += button_sz;
5704         collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5705     }
5706     if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)
5707     {
5708         collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l - style.FramePadding.x, title_bar_rect.Min.y);
5709         pad_l += button_sz;
5710     }
5711 
5712     // Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
5713     if (has_collapse_button)
5714         if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos))
5715             window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function
5716 
5717     // Close button
5718     if (has_close_button)
5719         if (CloseButton(window->GetID("#CLOSE"), close_button_pos))
5720             *p_open = false;
5721 
5722     window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5723     g.CurrentItemFlags = item_flags_backup;
5724 
5725     // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
5726     // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
5727     const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? button_sz * 0.80f : 0.0f;
5728     const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
5729 
5730     // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
5731     // while uncentered title text will still reach edges correctly.
5732     if (pad_l > style.FramePadding.x)
5733         pad_l += g.Style.ItemInnerSpacing.x;
5734     if (pad_r > style.FramePadding.x)
5735         pad_r += g.Style.ItemInnerSpacing.x;
5736     if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f)
5737     {
5738         float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center
5739         float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x);
5740         pad_l = ImMax(pad_l, pad_extend * centerness);
5741         pad_r = ImMax(pad_r, pad_extend * centerness);
5742     }
5743 
5744     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);
5745     ImRect clip_r(layout_r.Min.x, layout_r.Min.y, ImMin(layout_r.Max.x + g.Style.ItemInnerSpacing.x, title_bar_rect.Max.x), layout_r.Max.y);
5746     if (flags & ImGuiWindowFlags_UnsavedDocument)
5747     {
5748         ImVec2 marker_pos;
5749         marker_pos.x = ImClamp(layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x + text_size.x, layout_r.Min.x, layout_r.Max.x);
5750         marker_pos.y = (layout_r.Min.y + layout_r.Max.y) * 0.5f;
5751         if (marker_pos.x > layout_r.Min.x)
5752         {
5753             RenderBullet(window->DrawList, marker_pos, GetColorU32(ImGuiCol_Text));
5754             clip_r.Max.x = ImMin(clip_r.Max.x, marker_pos.x - (int)(marker_size_x * 0.5f));
5755         }
5756     }
5757     //if (g.IO.KeyShift) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
5758     //if (g.IO.KeyCtrl) window->DrawList->AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
5759     RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r);
5760 }
5761 
UpdateWindowParentAndRootLinks(ImGuiWindow * window,ImGuiWindowFlags flags,ImGuiWindow * parent_window)5762 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
5763 {
5764     window->ParentWindow = parent_window;
5765     window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
5766     if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
5767         window->RootWindow = parent_window->RootWindow;
5768     if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
5769         window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
5770     while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
5771     {
5772         IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);
5773         window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
5774     }
5775 }
5776 
5777 // Push a new Dear ImGui window to add widgets to.
5778 // - 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.
5779 // - Begin/End can be called multiple times during the frame with the same window name to append content.
5780 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
5781 //   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.
5782 // - 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.
5783 // - 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)5784 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
5785 {
5786     ImGuiContext& g = *GImGui;
5787     const ImGuiStyle& style = g.Style;
5788     IM_ASSERT(name != NULL && name[0] != '\0');     // Window name required
5789     IM_ASSERT(g.WithinFrameScope);                  // Forgot to call ImGui::NewFrame()
5790     IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
5791 
5792     // Find or create
5793     ImGuiWindow* window = FindWindowByName(name);
5794     const bool window_just_created = (window == NULL);
5795     if (window_just_created)
5796         window = CreateNewWindow(name, flags);
5797 
5798     // Automatically disable manual moving/resizing when NoInputs is set
5799     if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
5800         flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
5801 
5802     if (flags & ImGuiWindowFlags_NavFlattened)
5803         IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
5804 
5805     const int current_frame = g.FrameCount;
5806     const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
5807     window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow);
5808 
5809     // Update the Appearing flag
5810     bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1);   // Not using !WasActive because the implicit "Debug" window would always toggle off->on
5811     if (flags & ImGuiWindowFlags_Popup)
5812     {
5813         ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5814         window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
5815         window_just_activated_by_user |= (window != popup_ref.Window);
5816     }
5817     window->Appearing = window_just_activated_by_user;
5818     if (window->Appearing)
5819         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
5820 
5821     // Update Flags, LastFrameActive, BeginOrderXXX fields
5822     if (first_begin_of_the_frame)
5823     {
5824         window->Flags = (ImGuiWindowFlags)flags;
5825         window->LastFrameActive = current_frame;
5826         window->LastTimeActive = (float)g.Time;
5827         window->BeginOrderWithinParent = 0;
5828         window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
5829     }
5830     else
5831     {
5832         flags = window->Flags;
5833     }
5834 
5835     // 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
5836     ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back().Window;
5837     ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
5838     IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
5839 
5840     // We allow window memory to be compacted so recreate the base stack when needed.
5841     if (window->IDStack.Size == 0)
5842         window->IDStack.push_back(window->ID);
5843 
5844     // Add to stack
5845     // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
5846     ImGuiWindowStackData window_stack_data;
5847     window_stack_data.Window = window;
5848     window_stack_data.ParentLastItemDataBackup = g.LastItemData;
5849     g.CurrentWindowStack.push_back(window_stack_data);
5850     g.CurrentWindow = window;
5851     window->DC.StackSizesOnBegin.SetToCurrentState();
5852     g.CurrentWindow = NULL;
5853 
5854     if (flags & ImGuiWindowFlags_Popup)
5855     {
5856         ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5857         popup_ref.Window = window;
5858         g.BeginPopupStack.push_back(popup_ref);
5859         window->PopupId = popup_ref.PopupId;
5860     }
5861 
5862     // Update ->RootWindow and others pointers (before any possible call to FocusWindow)
5863     if (first_begin_of_the_frame)
5864         UpdateWindowParentAndRootLinks(window, flags, parent_window);
5865 
5866     // Process SetNextWindow***() calls
5867     // (FIXME: Consider splitting the HasXXX flags into X/Y components
5868     bool window_pos_set_by_api = false;
5869     bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
5870     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos)
5871     {
5872         window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
5873         if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
5874         {
5875             // May be processed on the next frame if this is our first frame and we are measuring size
5876             // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
5877             window->SetWindowPosVal = g.NextWindowData.PosVal;
5878             window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
5879             window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5880         }
5881         else
5882         {
5883             SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
5884         }
5885     }
5886     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)
5887     {
5888         window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
5889         window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
5890         SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
5891     }
5892     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasScroll)
5893     {
5894         if (g.NextWindowData.ScrollVal.x >= 0.0f)
5895         {
5896             window->ScrollTarget.x = g.NextWindowData.ScrollVal.x;
5897             window->ScrollTargetCenterRatio.x = 0.0f;
5898         }
5899         if (g.NextWindowData.ScrollVal.y >= 0.0f)
5900         {
5901             window->ScrollTarget.y = g.NextWindowData.ScrollVal.y;
5902             window->ScrollTargetCenterRatio.y = 0.0f;
5903         }
5904     }
5905     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize)
5906         window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;
5907     else if (first_begin_of_the_frame)
5908         window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);
5909     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed)
5910         SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
5911     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus)
5912         FocusWindow(window);
5913     if (window->Appearing)
5914         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
5915 
5916     // When reusing window again multiple times a frame, just append content (don't need to setup again)
5917     if (first_begin_of_the_frame)
5918     {
5919         // Initialize
5920         const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
5921         window->Active = true;
5922         window->HasCloseButton = (p_open != NULL);
5923         window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX);
5924         window->IDStack.resize(1);
5925         window->DrawList->_ResetForNewFrame();
5926         window->DC.CurrentTableIdx = -1;
5927 
5928         // Restore buffer capacity when woken from a compacted state, to avoid
5929         if (window->MemoryCompacted)
5930             GcAwakeTransientWindowBuffers(window);
5931 
5932         // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
5933         // 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.
5934         bool window_title_visible_elsewhere = false;
5935         if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0)   // Window titles visible when using CTRL+TAB
5936             window_title_visible_elsewhere = true;
5937         if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
5938         {
5939             size_t buf_len = (size_t)window->NameBufLen;
5940             window->Name = ImStrdupcpy(window->Name, &buf_len, name);
5941             window->NameBufLen = (int)buf_len;
5942         }
5943 
5944         // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
5945 
5946         // Update contents size from last frame for auto-fitting (or use explicit size)
5947         const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);
5948         CalcWindowContentSizes(window, &window->ContentSize, &window->ContentSizeIdeal);
5949         if (window->HiddenFramesCanSkipItems > 0)
5950             window->HiddenFramesCanSkipItems--;
5951         if (window->HiddenFramesCannotSkipItems > 0)
5952             window->HiddenFramesCannotSkipItems--;
5953         if (window->HiddenFramesForRenderOnly > 0)
5954             window->HiddenFramesForRenderOnly--;
5955 
5956         // Hide new windows for one frame until they calculate their size
5957         if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
5958             window->HiddenFramesCannotSkipItems = 1;
5959 
5960         // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
5961         // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
5962         if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
5963         {
5964             window->HiddenFramesCannotSkipItems = 1;
5965             if (flags & ImGuiWindowFlags_AlwaysAutoResize)
5966             {
5967                 if (!window_size_x_set_by_api)
5968                     window->Size.x = window->SizeFull.x = 0.f;
5969                 if (!window_size_y_set_by_api)
5970                     window->Size.y = window->SizeFull.y = 0.f;
5971                 window->ContentSize = window->ContentSizeIdeal = ImVec2(0.f, 0.f);
5972             }
5973         }
5974 
5975         // SELECT VIEWPORT
5976         // FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style)
5977         SetCurrentWindow(window);
5978 
5979         // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)
5980 
5981         if (flags & ImGuiWindowFlags_ChildWindow)
5982             window->WindowBorderSize = style.ChildBorderSize;
5983         else
5984             window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
5985         window->WindowPadding = style.WindowPadding;
5986         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
5987             window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
5988 
5989         // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size.
5990         window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
5991         window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
5992 
5993         // Collapse window by double-clicking on title bar
5994         // 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
5995         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
5996         {
5997             // 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.
5998             ImRect title_bar_rect = window->TitleBarRect();
5999             if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
6000                 window->WantCollapseToggle = true;
6001             if (window->WantCollapseToggle)
6002             {
6003                 window->Collapsed = !window->Collapsed;
6004                 MarkIniSettingsDirty(window);
6005             }
6006         }
6007         else
6008         {
6009             window->Collapsed = false;
6010         }
6011         window->WantCollapseToggle = false;
6012 
6013         // SIZE
6014 
6015         // Calculate auto-fit size, handle automatic resize
6016         const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal);
6017         bool use_current_size_for_scrollbar_x = window_just_created;
6018         bool use_current_size_for_scrollbar_y = window_just_created;
6019         if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
6020         {
6021             // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
6022             if (!window_size_x_set_by_api)
6023             {
6024                 window->SizeFull.x = size_auto_fit.x;
6025                 use_current_size_for_scrollbar_x = true;
6026             }
6027             if (!window_size_y_set_by_api)
6028             {
6029                 window->SizeFull.y = size_auto_fit.y;
6030                 use_current_size_for_scrollbar_y = true;
6031             }
6032         }
6033         else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
6034         {
6035             // Auto-fit may only grow window during the first few frames
6036             // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
6037             if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
6038             {
6039                 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
6040                 use_current_size_for_scrollbar_x = true;
6041             }
6042             if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
6043             {
6044                 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
6045                 use_current_size_for_scrollbar_y = true;
6046             }
6047             if (!window->Collapsed)
6048                 MarkIniSettingsDirty(window);
6049         }
6050 
6051         // Apply minimum/maximum window size constraints and final size
6052         window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull);
6053         window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
6054 
6055         // Decoration size
6056         const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
6057 
6058         // POSITION
6059 
6060         // Popup latch its initial position, will position itself when it appears next frame
6061         if (window_just_activated_by_user)
6062         {
6063             window->AutoPosLastDirection = ImGuiDir_None;
6064             if ((flags & ImGuiWindowFlags_Popup) != 0 && !(flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api) // FIXME: BeginPopup() could use SetNextWindowPos()
6065                 window->Pos = g.BeginPopupStack.back().OpenPopupPos;
6066         }
6067 
6068         // Position child window
6069         if (flags & ImGuiWindowFlags_ChildWindow)
6070         {
6071             IM_ASSERT(parent_window && parent_window->Active);
6072             window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
6073             parent_window->DC.ChildWindows.push_back(window);
6074             if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
6075                 window->Pos = parent_window->DC.CursorPos;
6076         }
6077 
6078         const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0);
6079         if (window_pos_with_pivot)
6080             SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering)
6081         else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
6082             window->Pos = FindBestWindowPosForPopup(window);
6083         else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
6084             window->Pos = FindBestWindowPosForPopup(window);
6085         else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
6086             window->Pos = FindBestWindowPosForPopup(window);
6087 
6088         // Calculate the range of allowed position for that window (to be movable and visible past safe area padding)
6089         // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect.
6090         ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport();
6091         ImRect viewport_rect(viewport->GetMainRect());
6092         ImRect viewport_work_rect(viewport->GetWorkRect());
6093         ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
6094         ImRect visibility_rect(viewport_work_rect.Min + visibility_padding, viewport_work_rect.Max - visibility_padding);
6095 
6096         // Clamp position/size so window stays visible within its viewport or monitor
6097         // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
6098         if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
6099             if (viewport_rect.GetWidth() > 0.0f && viewport_rect.GetHeight() > 0.0f)
6100                 ClampWindowRect(window, visibility_rect);
6101         window->Pos = ImFloor(window->Pos);
6102 
6103         // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
6104         // Large values tend to lead to variety of artifacts and are not recommended.
6105         window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
6106 
6107         // For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts.
6108         //if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar))
6109         //    window->WindowRounding = ImMin(window->WindowRounding, g.FontSize + style.FramePadding.y * 2.0f);
6110 
6111         // Apply window focus (new and reactivated windows are moved to front)
6112         bool want_focus = false;
6113         if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
6114         {
6115             if (flags & ImGuiWindowFlags_Popup)
6116                 want_focus = true;
6117             else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0)
6118                 want_focus = true;
6119         }
6120 
6121         // Handle manual resize: Resize Grips, Borders, Gamepad
6122         int border_held = -1;
6123         ImU32 resize_grip_col[4] = {};
6124         const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it.
6125         const float resize_grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
6126         if (!window->Collapsed)
6127             if (UpdateWindowManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect))
6128                 use_current_size_for_scrollbar_x = use_current_size_for_scrollbar_y = true;
6129         window->ResizeBorderHeld = (signed char)border_held;
6130 
6131         // SCROLLBAR VISIBILITY
6132 
6133         // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).
6134         if (!window->Collapsed)
6135         {
6136             // When reading the current size we need to read it after size constraints have been applied.
6137             // When we use InnerRect here we are intentionally reading last frame size, same for ScrollbarSizes values before we set them again.
6138             ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - decoration_up_height);
6139             ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + window->ScrollbarSizes;
6140             ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;
6141             float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;
6142             float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;
6143             //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?
6144             window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
6145             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));
6146             if (window->ScrollbarX && !window->ScrollbarY)
6147                 window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar);
6148             window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
6149         }
6150 
6151         // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)
6152         // Update various regions. Variables they depends on should be set above in this function.
6153         // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.
6154 
6155         // Outer rectangle
6156         // Not affected by window border size. Used by:
6157         // - FindHoveredWindow() (w/ extra padding when border resize is enabled)
6158         // - Begin() initial clipping rect for drawing window background and borders.
6159         // - Begin() clipping whole child
6160         const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;
6161         const ImRect outer_rect = window->Rect();
6162         const ImRect title_bar_rect = window->TitleBarRect();
6163         window->OuterRectClipped = outer_rect;
6164         window->OuterRectClipped.ClipWith(host_rect);
6165 
6166         // Inner rectangle
6167         // Not affected by window border size. Used by:
6168         // - InnerClipRect
6169         // - ScrollToBringRectIntoView()
6170         // - NavUpdatePageUpPageDown()
6171         // - Scrollbar()
6172         window->InnerRect.Min.x = window->Pos.x;
6173         window->InnerRect.Min.y = window->Pos.y + decoration_up_height;
6174         window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x;
6175         window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y;
6176 
6177         // Inner clipping rectangle.
6178         // Will extend a little bit outside the normal work region.
6179         // This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
6180         // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
6181         // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
6182         // Affected by window/frame border size. Used by:
6183         // - Begin() initial clip rect
6184         float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
6185         window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
6186         window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size);
6187         window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
6188         window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize);
6189         window->InnerClipRect.ClipWithFull(host_rect);
6190 
6191         // Default item width. Make it proportional to window size if window manually resizes
6192         if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
6193             window->ItemWidthDefault = ImFloor(window->Size.x * 0.65f);
6194         else
6195             window->ItemWidthDefault = ImFloor(g.FontSize * 16.0f);
6196 
6197         // SCROLLING
6198 
6199         // Lock down maximum scrolling
6200         // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate
6201         // for right/bottom aligned items without creating a scrollbar.
6202         window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth());
6203         window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());
6204 
6205         // Apply scrolling
6206         window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);
6207         window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
6208 
6209         // DRAWING
6210 
6211         // Setup draw list and outer clipping rectangle
6212         IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0);
6213         window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
6214         PushClipRect(host_rect.Min, host_rect.Max, false);
6215 
6216         // Draw modal window background (darkens what is behind them, all viewports)
6217         const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0;
6218         const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow);
6219         if (dim_bg_for_modal || dim_bg_for_window_list)
6220         {
6221             const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
6222             window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col);
6223         }
6224 
6225         // Draw navigation selection/windowing rectangle background
6226         if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim)
6227         {
6228             ImRect bb = window->Rect();
6229             bb.Expand(g.FontSize);
6230             if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway
6231                 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
6232         }
6233 
6234         // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71)
6235         // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
6236         // FIXME: User 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 (github #4493)
6237         {
6238             bool render_decorations_in_parent = false;
6239             if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
6240             {
6241                 // - We test overlap with the previous child window only (testing all would end up being O(log N) not a good investment here)
6242                 // - We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping childs
6243                 ImGuiWindow* previous_child = parent_window->DC.ChildWindows.Size >= 2 ? parent_window->DC.ChildWindows[parent_window->DC.ChildWindows.Size - 2] : NULL;
6244                 bool previous_child_overlapping = previous_child ? previous_child->Rect().Overlaps(window->Rect()) : false;
6245                 bool parent_is_empty = parent_window->DrawList->VtxBuffer.Size > 0;
6246                 if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_is_empty && !previous_child_overlapping)
6247                     render_decorations_in_parent = true;
6248             }
6249             if (render_decorations_in_parent)
6250                 window->DrawList = parent_window->DrawList;
6251 
6252             // Handle title bar, scrollbar, resize grips and resize borders
6253             const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
6254             const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
6255             RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, resize_grip_count, resize_grip_col, resize_grip_draw_size);
6256 
6257             if (render_decorations_in_parent)
6258                 window->DrawList = &window->DrawListInst;
6259         }
6260 
6261         // Draw navigation selection/windowing rectangle border
6262         if (g.NavWindowingTargetAnim == window)
6263         {
6264             float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
6265             ImRect bb = window->Rect();
6266             bb.Expand(g.FontSize);
6267             if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward
6268             {
6269                 bb.Expand(-g.FontSize - 1.0f);
6270                 rounding = window->WindowRounding;
6271             }
6272             window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, 0, 3.0f);
6273         }
6274 
6275         // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
6276 
6277         // Work rectangle.
6278         // Affected by window padding and border size. Used by:
6279         // - Columns() for right-most edge
6280         // - TreeNode(), CollapsingHeader() for right-most edge
6281         // - BeginTabBar() for right-most edge
6282         const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);
6283         const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);
6284         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));
6285         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));
6286         window->WorkRect.Min.x = ImFloor(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));
6287         window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
6288         window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
6289         window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
6290         window->ParentWorkRect = window->WorkRect;
6291 
6292         // [LEGACY] Content Region
6293         // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
6294         // Used by:
6295         // - Mouse wheel scrolling + many other things
6296         window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
6297         window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + decoration_up_height;
6298         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));
6299         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));
6300 
6301         // Setup drawing context
6302         // (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.)
6303         window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x;
6304         window->DC.GroupOffset.x = 0.0f;
6305         window->DC.ColumnsOffset.x = 0.0f;
6306         window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, decoration_up_height + window->WindowPadding.y - window->Scroll.y);
6307         window->DC.CursorPos = window->DC.CursorStartPos;
6308         window->DC.CursorPosPrevLine = window->DC.CursorPos;
6309         window->DC.CursorMaxPos = window->DC.CursorStartPos;
6310         window->DC.IdealMaxPos = window->DC.CursorStartPos;
6311         window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
6312         window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
6313 
6314         window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
6315         window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext;
6316         window->DC.NavLayersActiveMaskNext = 0x00;
6317         window->DC.NavHideHighlightOneFrame = false;
6318         window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f);
6319 
6320         window->DC.MenuBarAppending = false;
6321         window->DC.MenuColumns.Update(style.ItemSpacing.x, window_just_activated_by_user);
6322         window->DC.TreeDepth = 0;
6323         window->DC.TreeJumpToParentOnPopMask = 0x00;
6324         window->DC.ChildWindows.resize(0);
6325         window->DC.StateStorage = &window->StateStorage;
6326         window->DC.CurrentColumns = NULL;
6327         window->DC.LayoutType = ImGuiLayoutType_Vertical;
6328         window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
6329         window->DC.FocusCounterRegular = window->DC.FocusCounterTabStop = -1;
6330 
6331         window->DC.ItemWidth = window->ItemWidthDefault;
6332         window->DC.TextWrapPos = -1.0f; // disabled
6333         window->DC.ItemWidthStack.resize(0);
6334         window->DC.TextWrapPosStack.resize(0);
6335 
6336         if (window->AutoFitFramesX > 0)
6337             window->AutoFitFramesX--;
6338         if (window->AutoFitFramesY > 0)
6339             window->AutoFitFramesY--;
6340 
6341         // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
6342         if (want_focus)
6343         {
6344             FocusWindow(window);
6345             NavInitWindow(window, false); // <-- this is in the way for us to be able to defer and sort reappearing FocusWindow() calls
6346         }
6347 
6348         // Title bar
6349         if (!(flags & ImGuiWindowFlags_NoTitleBar))
6350             RenderWindowTitleBarContents(window, ImRect(title_bar_rect.Min.x + window->WindowBorderSize, title_bar_rect.Min.y, title_bar_rect.Max.x - window->WindowBorderSize, title_bar_rect.Max.y), name, p_open);
6351 
6352         // Clear hit test shape every frame
6353         window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0;
6354 
6355         // Pressing CTRL+C while holding on a window copy its content to the clipboard
6356         // 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.
6357         // Maybe we can support CTRL+C on every element?
6358         /*
6359         //if (g.NavWindow == window && g.ActiveId == 0)
6360         if (g.ActiveId == window->MoveId)
6361             if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
6362                 LogToClipboard();
6363         */
6364 
6365         // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
6366         // This is useful to allow creating context menus on title bar only, etc.
6367         g.LastItemData.ID = window->MoveId;
6368         g.LastItemData.InFlags = g.CurrentItemFlags;
6369         g.LastItemData.StatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;
6370         g.LastItemData.Rect = title_bar_rect;
6371 
6372 #ifdef IMGUI_ENABLE_TEST_ENGINE
6373         if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
6374             IMGUI_TEST_ENGINE_ITEM_ADD(g.LastItemData.Rect, g.LastItemData.ID);
6375 #endif
6376     }
6377     else
6378     {
6379         // Append
6380         SetCurrentWindow(window);
6381     }
6382 
6383     // Pull/inherit current state
6384     window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : window->GetID("#FOCUSSCOPE"); // Inherit from parent only // -V595
6385 
6386     PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
6387 
6388     // 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)
6389     window->WriteAccessed = false;
6390     window->BeginCount++;
6391     g.NextWindowData.ClearFlags();
6392 
6393     // Update visibility
6394     if (first_begin_of_the_frame)
6395     {
6396         if (flags & ImGuiWindowFlags_ChildWindow)
6397         {
6398             // Child window can be out of sight and have "negative" clip windows.
6399             // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
6400             IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
6401             if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) // FIXME: Doesn't make sense for ChildWindow??
6402                 if (!g.LogEnabled)
6403                     if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
6404                         window->HiddenFramesCanSkipItems = 1;
6405 
6406             // Hide along with parent or if parent is collapsed
6407             if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))
6408                 window->HiddenFramesCanSkipItems = 1;
6409             if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0))
6410                 window->HiddenFramesCannotSkipItems = 1;
6411         }
6412 
6413         // 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)
6414         if (style.Alpha <= 0.0f)
6415             window->HiddenFramesCanSkipItems = 1;
6416 
6417         // Update the Hidden flag
6418         window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0) || (window->HiddenFramesForRenderOnly > 0);
6419 
6420         // Disable inputs for requested number of frames
6421         if (window->DisableInputsFrames > 0)
6422         {
6423             window->DisableInputsFrames--;
6424             window->Flags |= ImGuiWindowFlags_NoInputs;
6425         }
6426 
6427         // Update the SkipItems flag, used to early out of all items functions (no layout required)
6428         bool skip_items = false;
6429         if (window->Collapsed || !window->Active || window->Hidden)
6430             if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0)
6431                 skip_items = true;
6432         window->SkipItems = skip_items;
6433     }
6434 
6435     return !window->SkipItems;
6436 }
6437 
End()6438 void ImGui::End()
6439 {
6440     ImGuiContext& g = *GImGui;
6441     ImGuiWindow* window = g.CurrentWindow;
6442 
6443     // Error checking: verify that user hasn't called End() too many times!
6444     if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow)
6445     {
6446         IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!");
6447         return;
6448     }
6449     IM_ASSERT(g.CurrentWindowStack.Size > 0);
6450 
6451     // Error checking: verify that user doesn't directly call End() on a child window.
6452     if (window->Flags & ImGuiWindowFlags_ChildWindow)
6453         IM_ASSERT_USER_ERROR(g.WithinEndChild, "Must call EndChild() and not End()!");
6454 
6455     // Close anything that is open
6456     if (window->DC.CurrentColumns)
6457         EndColumns();
6458     PopClipRect();   // Inner window clip rectangle
6459 
6460     // Stop logging
6461     if (!(window->Flags & ImGuiWindowFlags_ChildWindow))    // FIXME: add more options for scope of logging
6462         LogFinish();
6463 
6464     // Pop from window stack
6465     g.LastItemData = g.CurrentWindowStack.back().ParentLastItemDataBackup;
6466     g.CurrentWindowStack.pop_back();
6467     if (window->Flags & ImGuiWindowFlags_Popup)
6468         g.BeginPopupStack.pop_back();
6469     window->DC.StackSizesOnBegin.CompareWithCurrentState();
6470     SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window);
6471 }
6472 
BringWindowToFocusFront(ImGuiWindow * window)6473 void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
6474 {
6475     ImGuiContext& g = *GImGui;
6476     IM_ASSERT(window == window->RootWindow);
6477 
6478     const int cur_order = window->FocusOrder;
6479     IM_ASSERT(g.WindowsFocusOrder[cur_order] == window);
6480     if (g.WindowsFocusOrder.back() == window)
6481         return;
6482 
6483     const int new_order = g.WindowsFocusOrder.Size - 1;
6484     for (int n = cur_order; n < new_order; n++)
6485     {
6486         g.WindowsFocusOrder[n] = g.WindowsFocusOrder[n + 1];
6487         g.WindowsFocusOrder[n]->FocusOrder--;
6488         IM_ASSERT(g.WindowsFocusOrder[n]->FocusOrder == n);
6489     }
6490     g.WindowsFocusOrder[new_order] = window;
6491     window->FocusOrder = (short)new_order;
6492 }
6493 
BringWindowToDisplayFront(ImGuiWindow * window)6494 void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
6495 {
6496     ImGuiContext& g = *GImGui;
6497     ImGuiWindow* current_front_window = g.Windows.back();
6498     if (current_front_window == window || current_front_window->RootWindow == window) // Cheap early out (could be better)
6499         return;
6500     for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window
6501         if (g.Windows[i] == window)
6502         {
6503             memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
6504             g.Windows[g.Windows.Size - 1] = window;
6505             break;
6506         }
6507 }
6508 
BringWindowToDisplayBack(ImGuiWindow * window)6509 void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
6510 {
6511     ImGuiContext& g = *GImGui;
6512     if (g.Windows[0] == window)
6513         return;
6514     for (int i = 0; i < g.Windows.Size; i++)
6515         if (g.Windows[i] == window)
6516         {
6517             memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
6518             g.Windows[0] = window;
6519             break;
6520         }
6521 }
6522 
6523 // Moving window to front of display and set focus (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)6524 void ImGui::FocusWindow(ImGuiWindow* window)
6525 {
6526     ImGuiContext& g = *GImGui;
6527 
6528     if (g.NavWindow != window)
6529     {
6530         g.NavWindow = window;
6531         if (window && g.NavDisableMouseHover)
6532             g.NavMousePosDirty = true;
6533         g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
6534         g.NavFocusScopeId = 0;
6535         g.NavIdIsAlive = false;
6536         g.NavLayer = ImGuiNavLayer_Main;
6537         g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false;
6538         NavUpdateAnyRequestFlag();
6539         //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL);
6540     }
6541 
6542     // Close popups if any
6543     ClosePopupsOverWindow(window, false);
6544 
6545     // Move the root window to the top of the pile
6546     IM_ASSERT(window == NULL || window->RootWindow != NULL);
6547     ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL; // NB: In docking branch this is window->RootWindowDockStop
6548     ImGuiWindow* display_front_window = window ? window->RootWindow : NULL;
6549 
6550     // Steal active widgets. Some of the cases it triggers includes:
6551     // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run.
6552     // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId)
6553     if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window)
6554         if (!g.ActiveIdNoClearOnFocusLoss)
6555             ClearActiveID();
6556 
6557     // Passing NULL allow to disable keyboard focus
6558     if (!window)
6559         return;
6560 
6561     // Bring to front
6562     BringWindowToFocusFront(focus_front_window);
6563     if (((window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
6564         BringWindowToDisplayFront(display_front_window);
6565 }
6566 
FocusTopMostWindowUnderOne(ImGuiWindow * under_this_window,ImGuiWindow * ignore_window)6567 void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window)
6568 {
6569     ImGuiContext& g = *GImGui;
6570 
6571     const int start_idx = ((under_this_window != NULL) ? FindWindowFocusIndex(under_this_window) : g.WindowsFocusOrder.Size) - 1;
6572     for (int i = start_idx; i >= 0; i--)
6573     {
6574         // 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.
6575         ImGuiWindow* window = g.WindowsFocusOrder[i];
6576         IM_ASSERT(window == window->RootWindow);
6577         if (window != ignore_window && window->WasActive)
6578             if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
6579             {
6580                 ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window);
6581                 FocusWindow(focus_window);
6582                 return;
6583             }
6584     }
6585     FocusWindow(NULL);
6586 }
6587 
6588 // Important: this alone doesn't alter current ImDrawList state. This is called by PushFont/PopFont only.
SetCurrentFont(ImFont * font)6589 void ImGui::SetCurrentFont(ImFont* font)
6590 {
6591     ImGuiContext& g = *GImGui;
6592     IM_ASSERT(font && font->IsLoaded());    // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
6593     IM_ASSERT(font->Scale > 0.0f);
6594     g.Font = font;
6595     g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale);
6596     g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
6597 
6598     ImFontAtlas* atlas = g.Font->ContainerAtlas;
6599     g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
6600     g.DrawListSharedData.TexUvLines = atlas->TexUvLines;
6601     g.DrawListSharedData.Font = g.Font;
6602     g.DrawListSharedData.FontSize = g.FontSize;
6603 }
6604 
PushFont(ImFont * font)6605 void ImGui::PushFont(ImFont* font)
6606 {
6607     ImGuiContext& g = *GImGui;
6608     if (!font)
6609         font = GetDefaultFont();
6610     SetCurrentFont(font);
6611     g.FontStack.push_back(font);
6612     g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
6613 }
6614 
PopFont()6615 void  ImGui::PopFont()
6616 {
6617     ImGuiContext& g = *GImGui;
6618     g.CurrentWindow->DrawList->PopTextureID();
6619     g.FontStack.pop_back();
6620     SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
6621 }
6622 
PushItemFlag(ImGuiItemFlags option,bool enabled)6623 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
6624 {
6625     ImGuiContext& g = *GImGui;
6626     ImGuiItemFlags item_flags = g.CurrentItemFlags;
6627     IM_ASSERT(item_flags == g.ItemFlagsStack.back());
6628     if (enabled)
6629         item_flags |= option;
6630     else
6631         item_flags &= ~option;
6632     g.CurrentItemFlags = item_flags;
6633     g.ItemFlagsStack.push_back(item_flags);
6634 }
6635 
PopItemFlag()6636 void ImGui::PopItemFlag()
6637 {
6638     ImGuiContext& g = *GImGui;
6639     IM_ASSERT(g.ItemFlagsStack.Size > 1); // Too many calls to PopItemFlag() - we always leave a 0 at the bottom of the stack.
6640     g.ItemFlagsStack.pop_back();
6641     g.CurrentItemFlags = g.ItemFlagsStack.back();
6642 }
6643 
6644 // BeginDisabled()/EndDisabled()
6645 // - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled)
6646 // - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently.
6647 // - Feedback welcome at https://github.com/ocornut/imgui/issues/211
6648 // - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it.
6649 // - Optimized shortcuts instead of PushStyleVar() + PushItemFlag()
BeginDisabled(bool disabled)6650 void ImGui::BeginDisabled(bool disabled)
6651 {
6652     ImGuiContext& g = *GImGui;
6653     bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
6654     if (!was_disabled && disabled)
6655     {
6656         g.DisabledAlphaBackup = g.Style.Alpha;
6657         g.Style.Alpha *= g.Style.DisabledAlpha; // PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha);
6658     }
6659     if (was_disabled || disabled)
6660         g.CurrentItemFlags |= ImGuiItemFlags_Disabled;
6661     g.ItemFlagsStack.push_back(g.CurrentItemFlags);
6662 }
6663 
EndDisabled()6664 void ImGui::EndDisabled()
6665 {
6666     ImGuiContext& g = *GImGui;
6667     bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
6668     //PopItemFlag();
6669     g.ItemFlagsStack.pop_back();
6670     g.CurrentItemFlags = g.ItemFlagsStack.back();
6671     if (was_disabled && (g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0)
6672         g.Style.Alpha = g.DisabledAlphaBackup; //PopStyleVar();
6673 }
6674 
6675 // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system.
PushAllowKeyboardFocus(bool allow_keyboard_focus)6676 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
6677 {
6678     PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus);
6679 }
6680 
PopAllowKeyboardFocus()6681 void ImGui::PopAllowKeyboardFocus()
6682 {
6683     PopItemFlag();
6684 }
6685 
PushButtonRepeat(bool repeat)6686 void ImGui::PushButtonRepeat(bool repeat)
6687 {
6688     PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
6689 }
6690 
PopButtonRepeat()6691 void ImGui::PopButtonRepeat()
6692 {
6693     PopItemFlag();
6694 }
6695 
PushTextWrapPos(float wrap_pos_x)6696 void ImGui::PushTextWrapPos(float wrap_pos_x)
6697 {
6698     ImGuiWindow* window = GetCurrentWindow();
6699     window->DC.TextWrapPosStack.push_back(window->DC.TextWrapPos);
6700     window->DC.TextWrapPos = wrap_pos_x;
6701 }
6702 
PopTextWrapPos()6703 void ImGui::PopTextWrapPos()
6704 {
6705     ImGuiWindow* window = GetCurrentWindow();
6706     window->DC.TextWrapPos = window->DC.TextWrapPosStack.back();
6707     window->DC.TextWrapPosStack.pop_back();
6708 }
6709 
IsWindowChildOf(ImGuiWindow * window,ImGuiWindow * potential_parent)6710 bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
6711 {
6712     if (window->RootWindow == potential_parent)
6713         return true;
6714     while (window != NULL)
6715     {
6716         if (window == potential_parent)
6717             return true;
6718         window = window->ParentWindow;
6719     }
6720     return false;
6721 }
6722 
IsWindowAbove(ImGuiWindow * potential_above,ImGuiWindow * potential_below)6723 bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below)
6724 {
6725     ImGuiContext& g = *GImGui;
6726     for (int i = g.Windows.Size - 1; i >= 0; i--)
6727     {
6728         ImGuiWindow* candidate_window = g.Windows[i];
6729         if (candidate_window == potential_above)
6730             return true;
6731         if (candidate_window == potential_below)
6732             return false;
6733     }
6734     return false;
6735 }
6736 
IsWindowHovered(ImGuiHoveredFlags flags)6737 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
6738 {
6739     IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0);   // Flags not supported by this function
6740     ImGuiContext& g = *GImGui;
6741     if (g.HoveredWindow == NULL)
6742         return false;
6743 
6744     if ((flags & ImGuiHoveredFlags_AnyWindow) == 0)
6745     {
6746         ImGuiWindow* window = g.CurrentWindow;
6747         switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
6748         {
6749         case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
6750             if (g.HoveredWindow->RootWindow != window->RootWindow)
6751                 return false;
6752             break;
6753         case ImGuiHoveredFlags_RootWindow:
6754             if (g.HoveredWindow != window->RootWindow)
6755                 return false;
6756             break;
6757         case ImGuiHoveredFlags_ChildWindows:
6758             if (!IsWindowChildOf(g.HoveredWindow, window))
6759                 return false;
6760             break;
6761         default:
6762             if (g.HoveredWindow != window)
6763                 return false;
6764             break;
6765         }
6766     }
6767 
6768     if (!IsWindowContentHoverable(g.HoveredWindow, flags))
6769         return false;
6770     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
6771         if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
6772             return false;
6773     return true;
6774 }
6775 
IsWindowFocused(ImGuiFocusedFlags flags)6776 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
6777 {
6778     ImGuiContext& g = *GImGui;
6779 
6780     if (flags & ImGuiFocusedFlags_AnyWindow)
6781         return g.NavWindow != NULL;
6782 
6783     IM_ASSERT(g.CurrentWindow);     // Not inside a Begin()/End()
6784     switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
6785     {
6786     case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
6787         return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
6788     case ImGuiFocusedFlags_RootWindow:
6789         return g.NavWindow == g.CurrentWindow->RootWindow;
6790     case ImGuiFocusedFlags_ChildWindows:
6791         return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
6792     default:
6793         return g.NavWindow == g.CurrentWindow;
6794     }
6795 }
6796 
6797 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
6798 // Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmatically.
6799 // If you want a window to never be focused, you may use the e.g. NoInputs flag.
IsWindowNavFocusable(ImGuiWindow * window)6800 bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
6801 {
6802     return window->WasActive && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
6803 }
6804 
GetWindowWidth()6805 float ImGui::GetWindowWidth()
6806 {
6807     ImGuiWindow* window = GImGui->CurrentWindow;
6808     return window->Size.x;
6809 }
6810 
GetWindowHeight()6811 float ImGui::GetWindowHeight()
6812 {
6813     ImGuiWindow* window = GImGui->CurrentWindow;
6814     return window->Size.y;
6815 }
6816 
GetWindowPos()6817 ImVec2 ImGui::GetWindowPos()
6818 {
6819     ImGuiContext& g = *GImGui;
6820     ImGuiWindow* window = g.CurrentWindow;
6821     return window->Pos;
6822 }
6823 
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiCond cond)6824 void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
6825 {
6826     // Test condition (NB: bit 0 is always true) and clear flags for next time
6827     if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
6828         return;
6829 
6830     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6831     window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6832     window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
6833 
6834     // Set
6835     const ImVec2 old_pos = window->Pos;
6836     window->Pos = ImFloor(pos);
6837     ImVec2 offset = window->Pos - old_pos;
6838     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
6839     window->DC.CursorMaxPos += offset;      // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.
6840     window->DC.IdealMaxPos += offset;
6841     window->DC.CursorStartPos += offset;
6842 }
6843 
SetWindowPos(const ImVec2 & pos,ImGuiCond cond)6844 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
6845 {
6846     ImGuiWindow* window = GetCurrentWindowRead();
6847     SetWindowPos(window, pos, cond);
6848 }
6849 
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiCond cond)6850 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
6851 {
6852     if (ImGuiWindow* window = FindWindowByName(name))
6853         SetWindowPos(window, pos, cond);
6854 }
6855 
GetWindowSize()6856 ImVec2 ImGui::GetWindowSize()
6857 {
6858     ImGuiWindow* window = GetCurrentWindowRead();
6859     return window->Size;
6860 }
6861 
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiCond cond)6862 void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
6863 {
6864     // Test condition (NB: bit 0 is always true) and clear flags for next time
6865     if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
6866         return;
6867 
6868     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6869     window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6870 
6871     // Set
6872     if (size.x > 0.0f)
6873     {
6874         window->AutoFitFramesX = 0;
6875         window->SizeFull.x = IM_FLOOR(size.x);
6876     }
6877     else
6878     {
6879         window->AutoFitFramesX = 2;
6880         window->AutoFitOnlyGrows = false;
6881     }
6882     if (size.y > 0.0f)
6883     {
6884         window->AutoFitFramesY = 0;
6885         window->SizeFull.y = IM_FLOOR(size.y);
6886     }
6887     else
6888     {
6889         window->AutoFitFramesY = 2;
6890         window->AutoFitOnlyGrows = false;
6891     }
6892 }
6893 
SetWindowSize(const ImVec2 & size,ImGuiCond cond)6894 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
6895 {
6896     SetWindowSize(GImGui->CurrentWindow, size, cond);
6897 }
6898 
SetWindowSize(const char * name,const ImVec2 & size,ImGuiCond cond)6899 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
6900 {
6901     if (ImGuiWindow* window = FindWindowByName(name))
6902         SetWindowSize(window, size, cond);
6903 }
6904 
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiCond cond)6905 void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
6906 {
6907     // Test condition (NB: bit 0 is always true) and clear flags for next time
6908     if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
6909         return;
6910     window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6911 
6912     // Set
6913     window->Collapsed = collapsed;
6914 }
6915 
SetWindowHitTestHole(ImGuiWindow * window,const ImVec2 & pos,const ImVec2 & size)6916 void ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size)
6917 {
6918     IM_ASSERT(window->HitTestHoleSize.x == 0);     // We don't support multiple holes/hit test filters
6919     window->HitTestHoleSize = ImVec2ih(size);
6920     window->HitTestHoleOffset = ImVec2ih(pos - window->Pos);
6921 }
6922 
SetWindowCollapsed(bool collapsed,ImGuiCond cond)6923 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
6924 {
6925     SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
6926 }
6927 
IsWindowCollapsed()6928 bool ImGui::IsWindowCollapsed()
6929 {
6930     ImGuiWindow* window = GetCurrentWindowRead();
6931     return window->Collapsed;
6932 }
6933 
IsWindowAppearing()6934 bool ImGui::IsWindowAppearing()
6935 {
6936     ImGuiWindow* window = GetCurrentWindowRead();
6937     return window->Appearing;
6938 }
6939 
SetWindowCollapsed(const char * name,bool collapsed,ImGuiCond cond)6940 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
6941 {
6942     if (ImGuiWindow* window = FindWindowByName(name))
6943         SetWindowCollapsed(window, collapsed, cond);
6944 }
6945 
SetWindowFocus()6946 void ImGui::SetWindowFocus()
6947 {
6948     FocusWindow(GImGui->CurrentWindow);
6949 }
6950 
SetWindowFocus(const char * name)6951 void ImGui::SetWindowFocus(const char* name)
6952 {
6953     if (name)
6954     {
6955         if (ImGuiWindow* window = FindWindowByName(name))
6956             FocusWindow(window);
6957     }
6958     else
6959     {
6960         FocusWindow(NULL);
6961     }
6962 }
6963 
SetNextWindowPos(const ImVec2 & pos,ImGuiCond cond,const ImVec2 & pivot)6964 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
6965 {
6966     ImGuiContext& g = *GImGui;
6967     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6968     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos;
6969     g.NextWindowData.PosVal = pos;
6970     g.NextWindowData.PosPivotVal = pivot;
6971     g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
6972 }
6973 
SetNextWindowSize(const ImVec2 & size,ImGuiCond cond)6974 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
6975 {
6976     ImGuiContext& g = *GImGui;
6977     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6978     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize;
6979     g.NextWindowData.SizeVal = size;
6980     g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
6981 }
6982 
SetNextWindowSizeConstraints(const ImVec2 & size_min,const ImVec2 & size_max,ImGuiSizeCallback custom_callback,void * custom_callback_user_data)6983 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
6984 {
6985     ImGuiContext& g = *GImGui;
6986     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint;
6987     g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
6988     g.NextWindowData.SizeCallback = custom_callback;
6989     g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
6990 }
6991 
6992 // Content size = inner scrollable rectangle, padded with WindowPadding.
6993 // SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
SetNextWindowContentSize(const ImVec2 & size)6994 void ImGui::SetNextWindowContentSize(const ImVec2& size)
6995 {
6996     ImGuiContext& g = *GImGui;
6997     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize;
6998     g.NextWindowData.ContentSizeVal = ImFloor(size);
6999 }
7000 
SetNextWindowScroll(const ImVec2 & scroll)7001 void ImGui::SetNextWindowScroll(const ImVec2& scroll)
7002 {
7003     ImGuiContext& g = *GImGui;
7004     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasScroll;
7005     g.NextWindowData.ScrollVal = scroll;
7006 }
7007 
SetNextWindowCollapsed(bool collapsed,ImGuiCond cond)7008 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
7009 {
7010     ImGuiContext& g = *GImGui;
7011     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
7012     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed;
7013     g.NextWindowData.CollapsedVal = collapsed;
7014     g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
7015 }
7016 
SetNextWindowFocus()7017 void ImGui::SetNextWindowFocus()
7018 {
7019     ImGuiContext& g = *GImGui;
7020     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus;
7021 }
7022 
SetNextWindowBgAlpha(float alpha)7023 void ImGui::SetNextWindowBgAlpha(float alpha)
7024 {
7025     ImGuiContext& g = *GImGui;
7026     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha;
7027     g.NextWindowData.BgAlphaVal = alpha;
7028 }
7029 
GetWindowDrawList()7030 ImDrawList* ImGui::GetWindowDrawList()
7031 {
7032     ImGuiWindow* window = GetCurrentWindow();
7033     return window->DrawList;
7034 }
7035 
GetFont()7036 ImFont* ImGui::GetFont()
7037 {
7038     return GImGui->Font;
7039 }
7040 
GetFontSize()7041 float ImGui::GetFontSize()
7042 {
7043     return GImGui->FontSize;
7044 }
7045 
GetFontTexUvWhitePixel()7046 ImVec2 ImGui::GetFontTexUvWhitePixel()
7047 {
7048     return GImGui->DrawListSharedData.TexUvWhitePixel;
7049 }
7050 
SetWindowFontScale(float scale)7051 void ImGui::SetWindowFontScale(float scale)
7052 {
7053     IM_ASSERT(scale > 0.0f);
7054     ImGuiContext& g = *GImGui;
7055     ImGuiWindow* window = GetCurrentWindow();
7056     window->FontWindowScale = scale;
7057     g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
7058 }
7059 
ActivateItem(ImGuiID id)7060 void ImGui::ActivateItem(ImGuiID id)
7061 {
7062     ImGuiContext& g = *GImGui;
7063     g.NavNextActivateId = id;
7064 }
7065 
PushFocusScope(ImGuiID id)7066 void ImGui::PushFocusScope(ImGuiID id)
7067 {
7068     ImGuiContext& g = *GImGui;
7069     ImGuiWindow* window = g.CurrentWindow;
7070     g.FocusScopeStack.push_back(window->DC.NavFocusScopeIdCurrent);
7071     window->DC.NavFocusScopeIdCurrent = id;
7072 }
7073 
PopFocusScope()7074 void ImGui::PopFocusScope()
7075 {
7076     ImGuiContext& g = *GImGui;
7077     ImGuiWindow* window = g.CurrentWindow;
7078     IM_ASSERT(g.FocusScopeStack.Size > 0); // Too many PopFocusScope() ?
7079     window->DC.NavFocusScopeIdCurrent = g.FocusScopeStack.back();
7080     g.FocusScopeStack.pop_back();
7081 }
7082 
SetKeyboardFocusHere(int offset)7083 void ImGui::SetKeyboardFocusHere(int offset)
7084 {
7085     IM_ASSERT(offset >= -1);    // -1 is allowed but not below
7086     ImGuiContext& g = *GImGui;
7087     ImGuiWindow* window = g.CurrentWindow;
7088     g.TabFocusRequestNextWindow = window;
7089     g.TabFocusRequestNextCounterRegular = window->DC.FocusCounterRegular + 1 + offset;
7090     g.TabFocusRequestNextCounterTabStop = INT_MAX;
7091 }
7092 
SetItemDefaultFocus()7093 void ImGui::SetItemDefaultFocus()
7094 {
7095     ImGuiContext& g = *GImGui;
7096     ImGuiWindow* window = g.CurrentWindow;
7097     if (!window->Appearing)
7098         return;
7099     if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == window->DC.NavLayerCurrent)
7100     {
7101         g.NavInitRequest = false;
7102         g.NavInitResultId = g.LastItemData.ID;
7103         g.NavInitResultRectRel = ImRect(g.LastItemData.Rect.Min - window->Pos, g.LastItemData.Rect.Max - window->Pos);
7104         NavUpdateAnyRequestFlag();
7105         if (!IsItemVisible())
7106             SetScrollHereY();
7107     }
7108 }
7109 
SetStateStorage(ImGuiStorage * tree)7110 void ImGui::SetStateStorage(ImGuiStorage* tree)
7111 {
7112     ImGuiWindow* window = GImGui->CurrentWindow;
7113     window->DC.StateStorage = tree ? tree : &window->StateStorage;
7114 }
7115 
GetStateStorage()7116 ImGuiStorage* ImGui::GetStateStorage()
7117 {
7118     ImGuiWindow* window = GImGui->CurrentWindow;
7119     return window->DC.StateStorage;
7120 }
7121 
PushID(const char * str_id)7122 void ImGui::PushID(const char* str_id)
7123 {
7124     ImGuiContext& g = *GImGui;
7125     ImGuiWindow* window = g.CurrentWindow;
7126     ImGuiID id = window->GetIDNoKeepAlive(str_id);
7127     window->IDStack.push_back(id);
7128 }
7129 
PushID(const char * str_id_begin,const char * str_id_end)7130 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
7131 {
7132     ImGuiContext& g = *GImGui;
7133     ImGuiWindow* window = g.CurrentWindow;
7134     ImGuiID id = window->GetIDNoKeepAlive(str_id_begin, str_id_end);
7135     window->IDStack.push_back(id);
7136 }
7137 
PushID(const void * ptr_id)7138 void ImGui::PushID(const void* ptr_id)
7139 {
7140     ImGuiContext& g = *GImGui;
7141     ImGuiWindow* window = g.CurrentWindow;
7142     ImGuiID id = window->GetIDNoKeepAlive(ptr_id);
7143     window->IDStack.push_back(id);
7144 }
7145 
PushID(int int_id)7146 void ImGui::PushID(int int_id)
7147 {
7148     ImGuiContext& g = *GImGui;
7149     ImGuiWindow* window = g.CurrentWindow;
7150     ImGuiID id = window->GetIDNoKeepAlive(int_id);
7151     window->IDStack.push_back(id);
7152 }
7153 
7154 // Push a given id value ignoring the ID stack as a seed.
PushOverrideID(ImGuiID id)7155 void ImGui::PushOverrideID(ImGuiID id)
7156 {
7157     ImGuiContext& g = *GImGui;
7158     ImGuiWindow* window = g.CurrentWindow;
7159     window->IDStack.push_back(id);
7160 }
7161 
7162 // Helper to avoid a common series of PushOverrideID -> GetID() -> PopID() call
7163 // (note that when using this pattern, TestEngine's "Stack Tool" will tend to not display the intermediate stack level.
7164 //  for that to work we would need to do PushOverrideID() -> ItemAdd() -> PopID() which would alter widget code a little more)
GetIDWithSeed(const char * str,const char * str_end,ImGuiID seed)7165 ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed)
7166 {
7167     ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
7168     ImGui::KeepAliveID(id);
7169 #ifdef IMGUI_ENABLE_TEST_ENGINE
7170     ImGuiContext& g = *GImGui;
7171     IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
7172 #endif
7173     return id;
7174 }
7175 
PopID()7176 void ImGui::PopID()
7177 {
7178     ImGuiWindow* window = GImGui->CurrentWindow;
7179     IM_ASSERT(window->IDStack.Size > 1); // Too many PopID(), or could be popping in a wrong/different window?
7180     window->IDStack.pop_back();
7181 }
7182 
GetID(const char * str_id)7183 ImGuiID ImGui::GetID(const char* str_id)
7184 {
7185     ImGuiWindow* window = GImGui->CurrentWindow;
7186     return window->GetID(str_id);
7187 }
7188 
GetID(const char * str_id_begin,const char * str_id_end)7189 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
7190 {
7191     ImGuiWindow* window = GImGui->CurrentWindow;
7192     return window->GetID(str_id_begin, str_id_end);
7193 }
7194 
GetID(const void * ptr_id)7195 ImGuiID ImGui::GetID(const void* ptr_id)
7196 {
7197     ImGuiWindow* window = GImGui->CurrentWindow;
7198     return window->GetID(ptr_id);
7199 }
7200 
IsRectVisible(const ImVec2 & size)7201 bool ImGui::IsRectVisible(const ImVec2& size)
7202 {
7203     ImGuiWindow* window = GImGui->CurrentWindow;
7204     return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
7205 }
7206 
IsRectVisible(const ImVec2 & rect_min,const ImVec2 & rect_max)7207 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
7208 {
7209     ImGuiWindow* window = GImGui->CurrentWindow;
7210     return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
7211 }
7212 
7213 
7214 //-----------------------------------------------------------------------------
7215 // [SECTION] ERROR CHECKING
7216 //-----------------------------------------------------------------------------
7217 
7218 // Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui.
7219 // Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
7220 // If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. your user code
7221 // may see different structures than what imgui.cpp sees, which is problematic.
7222 // 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)7223 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)
7224 {
7225     bool error = false;
7226     if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); }
7227     if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
7228     if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
7229     if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
7230     if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
7231     if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
7232     if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); }
7233     return !error;
7234 }
7235 
ErrorCheckNewFrameSanityChecks()7236 static void ImGui::ErrorCheckNewFrameSanityChecks()
7237 {
7238     ImGuiContext& g = *GImGui;
7239 
7240     // Check user IM_ASSERT macro
7241     // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means your assert macro is incorrectly defined!
7242     //  If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block.
7243     //  This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.)
7244     // #define IM_ASSERT(EXPR)   if (SomeCode(EXPR)) SomeMoreCode();                    // Wrong!
7245     // #define IM_ASSERT(EXPR)   do { if (SomeCode(EXPR)) SomeMoreCode(); } while (0)   // Correct!
7246     if (true) IM_ASSERT(1); else IM_ASSERT(0);
7247 
7248     // Check user data
7249     // (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)
7250     IM_ASSERT(g.Initialized);
7251     IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0)              && "Need a positive DeltaTime!");
7252     IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount)  && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
7253     IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f  && "Invalid DisplaySize value!");
7254     IM_ASSERT(g.IO.Fonts->IsBuilt()                                     && "Font Atlas not built! Make sure you called ImGui_ImplXXXX_NewFrame() function for renderer backend, which should call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()");
7255     IM_ASSERT(g.Style.CurveTessellationTol > 0.0f                       && "Invalid style setting!");
7256     IM_ASSERT(g.Style.CircleTessellationMaxError  > 0.0f                && "Invalid style setting!");
7257     IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f            && "Invalid style setting!"); // Allows us to avoid a few clamps in color computations
7258     IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting.");
7259     IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
7260     for (int n = 0; n < ImGuiKey_COUNT; n++)
7261         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)");
7262 
7263     // Check: required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only added in 1.60 WIP)
7264     if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
7265         IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
7266 
7267     // Check: the io.ConfigWindowsResizeFromEdges option requires backend to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
7268     if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
7269         g.IO.ConfigWindowsResizeFromEdges = false;
7270 }
7271 
ErrorCheckEndFrameSanityChecks()7272 static void ImGui::ErrorCheckEndFrameSanityChecks()
7273 {
7274     ImGuiContext& g = *GImGui;
7275 
7276     // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame()
7277     // One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame().
7278     // It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will
7279     // send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs.
7280     // We silently accommodate for this case by ignoring/ the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0),
7281     // while still correctly asserting on mid-frame key press events.
7282     const ImGuiKeyModFlags key_mod_flags = GetMergedKeyModFlags();
7283     IM_ASSERT((key_mod_flags == 0 || g.IO.KeyMods == key_mod_flags) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
7284     IM_UNUSED(key_mod_flags);
7285 
7286     // Recover from errors
7287     //ErrorCheckEndFrameRecover();
7288 
7289     // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you
7290     // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API).
7291     if (g.CurrentWindowStack.Size != 1)
7292     {
7293         if (g.CurrentWindowStack.Size > 1)
7294         {
7295             IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?");
7296             while (g.CurrentWindowStack.Size > 1)
7297                 End();
7298         }
7299         else
7300         {
7301             IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?");
7302         }
7303     }
7304 
7305     IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, "Missing EndGroup call!");
7306 }
7307 
7308 // Experimental recovery from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.
7309 // Must be called during or before EndFrame().
7310 // This is generally flawed as we are not necessarily End/Popping things in the right order.
7311 // FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.
7312 // FIXME: Can't recover from interleaved BeginTabBar/Begin
ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback,void * user_data)7313 void    ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data)
7314 {
7315     // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations"
7316     ImGuiContext& g = *GImGui;
7317     while (g.CurrentWindowStack.Size > 0)
7318     {
7319         while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow))
7320         {
7321             if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name);
7322             EndTable();
7323         }
7324         ImGuiWindow* window = g.CurrentWindow;
7325         IM_ASSERT(window != NULL);
7326         while (g.CurrentTabBar != NULL) //-V1044
7327         {
7328             if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name);
7329             EndTabBar();
7330         }
7331         while (window->DC.TreeDepth > 0)
7332         {
7333             if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name);
7334             TreePop();
7335         }
7336         while (g.GroupStack.Size > window->DC.StackSizesOnBegin.SizeOfGroupStack)
7337         {
7338             if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name);
7339             EndGroup();
7340         }
7341         while (window->IDStack.Size > 1)
7342         {
7343             if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name);
7344             PopID();
7345         }
7346         while (g.ColorStack.Size > window->DC.StackSizesOnBegin.SizeOfColorStack)
7347         {
7348             if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col));
7349             PopStyleColor();
7350         }
7351         while (g.StyleVarStack.Size > window->DC.StackSizesOnBegin.SizeOfStyleVarStack)
7352         {
7353             if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name);
7354             PopStyleVar();
7355         }
7356         while (g.FocusScopeStack.Size > window->DC.StackSizesOnBegin.SizeOfFocusScopeStack)
7357         {
7358             if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name);
7359             PopFocusScope();
7360         }
7361         if (g.CurrentWindowStack.Size == 1)
7362         {
7363             IM_ASSERT(g.CurrentWindow->IsFallbackWindow);
7364             break;
7365         }
7366         IM_ASSERT(window == g.CurrentWindow);
7367         if (window->Flags & ImGuiWindowFlags_ChildWindow)
7368         {
7369             if (log_callback) log_callback(user_data, "Recovered from missing EndChild() for '%s'", window->Name);
7370             EndChild();
7371         }
7372         else
7373         {
7374             if (log_callback) log_callback(user_data, "Recovered from missing End() for '%s'", window->Name);
7375             End();
7376         }
7377     }
7378 }
7379 
7380 // Save current stack sizes for later compare
SetToCurrentState()7381 void ImGuiStackSizes::SetToCurrentState()
7382 {
7383     ImGuiContext& g = *GImGui;
7384     ImGuiWindow* window = g.CurrentWindow;
7385     SizeOfIDStack = (short)window->IDStack.Size;
7386     SizeOfColorStack = (short)g.ColorStack.Size;
7387     SizeOfStyleVarStack = (short)g.StyleVarStack.Size;
7388     SizeOfFontStack = (short)g.FontStack.Size;
7389     SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size;
7390     SizeOfGroupStack = (short)g.GroupStack.Size;
7391     SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size;
7392 }
7393 
7394 // Compare to detect usage errors
CompareWithCurrentState()7395 void ImGuiStackSizes::CompareWithCurrentState()
7396 {
7397     ImGuiContext& g = *GImGui;
7398     ImGuiWindow* window = g.CurrentWindow;
7399     IM_UNUSED(window);
7400 
7401     // Window stacks
7402     // NOT checking: DC.ItemWidth, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin)
7403     IM_ASSERT(SizeOfIDStack         == window->IDStack.Size     && "PushID/PopID or TreeNode/TreePop Mismatch!");
7404 
7405     // Global stacks
7406     // 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.
7407     IM_ASSERT(SizeOfGroupStack      == g.GroupStack.Size        && "BeginGroup/EndGroup Mismatch!");
7408     IM_ASSERT(SizeOfBeginPopupStack == g.BeginPopupStack.Size   && "BeginPopup/EndPopup or BeginMenu/EndMenu Mismatch!");
7409     IM_ASSERT(SizeOfColorStack      >= g.ColorStack.Size        && "PushStyleColor/PopStyleColor Mismatch!");
7410     IM_ASSERT(SizeOfStyleVarStack   >= g.StyleVarStack.Size     && "PushStyleVar/PopStyleVar Mismatch!");
7411     IM_ASSERT(SizeOfFontStack       >= g.FontStack.Size         && "PushFont/PopFont Mismatch!");
7412     IM_ASSERT(SizeOfFocusScopeStack == g.FocusScopeStack.Size   && "PushFocusScope/PopFocusScope Mismatch!");
7413 }
7414 
7415 
7416 //-----------------------------------------------------------------------------
7417 // [SECTION] LAYOUT
7418 //-----------------------------------------------------------------------------
7419 // - ItemSize()
7420 // - ItemAdd()
7421 // - SameLine()
7422 // - GetCursorScreenPos()
7423 // - SetCursorScreenPos()
7424 // - GetCursorPos(), GetCursorPosX(), GetCursorPosY()
7425 // - SetCursorPos(), SetCursorPosX(), SetCursorPosY()
7426 // - GetCursorStartPos()
7427 // - Indent()
7428 // - Unindent()
7429 // - SetNextItemWidth()
7430 // - PushItemWidth()
7431 // - PushMultiItemsWidths()
7432 // - PopItemWidth()
7433 // - CalcItemWidth()
7434 // - CalcItemSize()
7435 // - GetTextLineHeight()
7436 // - GetTextLineHeightWithSpacing()
7437 // - GetFrameHeight()
7438 // - GetFrameHeightWithSpacing()
7439 // - GetContentRegionMax()
7440 // - GetContentRegionMaxAbs() [Internal]
7441 // - GetContentRegionAvail(),
7442 // - GetWindowContentRegionMin(), GetWindowContentRegionMax()
7443 // - BeginGroup()
7444 // - EndGroup()
7445 // Also see in imgui_widgets: tab bars, columns.
7446 //-----------------------------------------------------------------------------
7447 
7448 // Advance cursor given item size for layout.
7449 // Register minimum needed size so it can extend the bounding box used for auto-fit calculation.
7450 // See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different.
ItemSize(const ImVec2 & size,float text_baseline_y)7451 void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
7452 {
7453     ImGuiContext& g = *GImGui;
7454     ImGuiWindow* window = g.CurrentWindow;
7455     if (window->SkipItems)
7456         return;
7457 
7458     // We increase the height in this function to accommodate for baseline offset.
7459     // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
7460     // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
7461     const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
7462     const float line_height = ImMax(window->DC.CurrLineSize.y, size.y + offset_to_match_baseline_y);
7463 
7464     // Always align ourselves on pixel boundaries
7465     //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]
7466     window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
7467     window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y;
7468     window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);    // Next line
7469     window->DC.CursorPos.y = IM_FLOOR(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y);        // Next line
7470     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
7471     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
7472     //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
7473 
7474     window->DC.PrevLineSize.y = line_height;
7475     window->DC.CurrLineSize.y = 0.0f;
7476     window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
7477     window->DC.CurrLineTextBaseOffset = 0.0f;
7478 
7479     // Horizontal layout mode
7480     if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
7481         SameLine();
7482 }
7483 
ItemSize(const ImRect & bb,float text_baseline_y)7484 void ImGui::ItemSize(const ImRect& bb, float text_baseline_y)
7485 {
7486     ItemSize(bb.GetSize(), text_baseline_y);
7487 }
7488 
7489 // Declare item bounding box for clipping and interaction.
7490 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
7491 // declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction.
ItemAdd(const ImRect & bb,ImGuiID id,const ImRect * nav_bb_arg,ImGuiItemAddFlags flags)7492 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemAddFlags flags)
7493 {
7494     ImGuiContext& g = *GImGui;
7495     ImGuiWindow* window = g.CurrentWindow;
7496 
7497     // Set item data
7498     g.LastItemData.ID = id;
7499     g.LastItemData.Rect = bb;
7500     g.LastItemData.InFlags = g.CurrentItemFlags;
7501     g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None;
7502 
7503     // Directional navigation processing
7504     if (id != 0)
7505     {
7506         // Runs prior to clipping early-out
7507         //  (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
7508         //  (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
7509         //      unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
7510         //      thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
7511         //      We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
7512         //      to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
7513         // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
7514         // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
7515         window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent);
7516         if (g.NavId == id || g.NavAnyRequest)
7517             if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
7518                 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
7519                     NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
7520 
7521         // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd()
7522 #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
7523         if (id == g.DebugItemPickerBreakId)
7524         {
7525             IM_DEBUG_BREAK();
7526             g.DebugItemPickerBreakId = 0;
7527         }
7528 #endif
7529     }
7530     g.NextItemData.Flags = ImGuiNextItemDataFlags_None;
7531 
7532 #ifdef IMGUI_ENABLE_TEST_ENGINE
7533     if (id != 0)
7534         IMGUI_TEST_ENGINE_ITEM_ADD(nav_bb_arg ? *nav_bb_arg : bb, id);
7535 #endif
7536 
7537     // Clipping test
7538     const bool is_clipped = IsClippedEx(bb, id, false);
7539     if (is_clipped)
7540         return false;
7541     //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
7542 
7543     // Tab stop handling (previously was using internal ItemFocusable() api)
7544     // FIXME-NAV: We would now want to move this above the clipping test, but this would require being able to scroll and currently this would mean an extra frame. (#4079, #343)
7545     if (flags & ImGuiItemAddFlags_Focusable)
7546         ItemFocusable(window, id);
7547 
7548     // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
7549     if (IsMouseHoveringRect(bb.Min, bb.Max))
7550         g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect;
7551     return true;
7552 }
7553 
7554 // Gets back to previous line and continue with horizontal layout
7555 //      offset_from_start_x == 0 : follow right after previous item
7556 //      offset_from_start_x != 0 : align to specified x position (relative to window/group left)
7557 //      spacing_w < 0            : use default spacing if pos_x == 0, no spacing if pos_x != 0
7558 //      spacing_w >= 0           : enforce spacing amount
SameLine(float offset_from_start_x,float spacing_w)7559 void ImGui::SameLine(float offset_from_start_x, float spacing_w)
7560 {
7561     ImGuiWindow* window = GetCurrentWindow();
7562     if (window->SkipItems)
7563         return;
7564 
7565     ImGuiContext& g = *GImGui;
7566     if (offset_from_start_x != 0.0f)
7567     {
7568         if (spacing_w < 0.0f) spacing_w = 0.0f;
7569         window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
7570         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
7571     }
7572     else
7573     {
7574         if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
7575         window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
7576         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
7577     }
7578     window->DC.CurrLineSize = window->DC.PrevLineSize;
7579     window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
7580 }
7581 
GetCursorScreenPos()7582 ImVec2 ImGui::GetCursorScreenPos()
7583 {
7584     ImGuiWindow* window = GetCurrentWindowRead();
7585     return window->DC.CursorPos;
7586 }
7587 
SetCursorScreenPos(const ImVec2 & pos)7588 void ImGui::SetCursorScreenPos(const ImVec2& pos)
7589 {
7590     ImGuiWindow* window = GetCurrentWindow();
7591     window->DC.CursorPos = pos;
7592     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
7593 }
7594 
7595 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
7596 // 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()7597 ImVec2 ImGui::GetCursorPos()
7598 {
7599     ImGuiWindow* window = GetCurrentWindowRead();
7600     return window->DC.CursorPos - window->Pos + window->Scroll;
7601 }
7602 
GetCursorPosX()7603 float ImGui::GetCursorPosX()
7604 {
7605     ImGuiWindow* window = GetCurrentWindowRead();
7606     return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
7607 }
7608 
GetCursorPosY()7609 float ImGui::GetCursorPosY()
7610 {
7611     ImGuiWindow* window = GetCurrentWindowRead();
7612     return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
7613 }
7614 
SetCursorPos(const ImVec2 & local_pos)7615 void ImGui::SetCursorPos(const ImVec2& local_pos)
7616 {
7617     ImGuiWindow* window = GetCurrentWindow();
7618     window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
7619     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
7620 }
7621 
SetCursorPosX(float x)7622 void ImGui::SetCursorPosX(float x)
7623 {
7624     ImGuiWindow* window = GetCurrentWindow();
7625     window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
7626     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
7627 }
7628 
SetCursorPosY(float y)7629 void ImGui::SetCursorPosY(float y)
7630 {
7631     ImGuiWindow* window = GetCurrentWindow();
7632     window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
7633     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
7634 }
7635 
GetCursorStartPos()7636 ImVec2 ImGui::GetCursorStartPos()
7637 {
7638     ImGuiWindow* window = GetCurrentWindowRead();
7639     return window->DC.CursorStartPos - window->Pos;
7640 }
7641 
Indent(float indent_w)7642 void ImGui::Indent(float indent_w)
7643 {
7644     ImGuiContext& g = *GImGui;
7645     ImGuiWindow* window = GetCurrentWindow();
7646     window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
7647     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
7648 }
7649 
Unindent(float indent_w)7650 void ImGui::Unindent(float indent_w)
7651 {
7652     ImGuiContext& g = *GImGui;
7653     ImGuiWindow* window = GetCurrentWindow();
7654     window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
7655     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
7656 }
7657 
7658 // Affect large frame+labels widgets only.
SetNextItemWidth(float item_width)7659 void ImGui::SetNextItemWidth(float item_width)
7660 {
7661     ImGuiContext& g = *GImGui;
7662     g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasWidth;
7663     g.NextItemData.Width = item_width;
7664 }
7665 
7666 // FIXME: Remove the == 0.0f behavior?
PushItemWidth(float item_width)7667 void ImGui::PushItemWidth(float item_width)
7668 {
7669     ImGuiContext& g = *GImGui;
7670     ImGuiWindow* window = g.CurrentWindow;
7671     window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width
7672     window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
7673     g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
7674 }
7675 
PushMultiItemsWidths(int components,float w_full)7676 void ImGui::PushMultiItemsWidths(int components, float w_full)
7677 {
7678     ImGuiContext& g = *GImGui;
7679     ImGuiWindow* window = g.CurrentWindow;
7680     const ImGuiStyle& style = g.Style;
7681     const float w_item_one  = ImMax(1.0f, IM_FLOOR((w_full - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components));
7682     const float w_item_last = ImMax(1.0f, IM_FLOOR(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components - 1)));
7683     window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width
7684     window->DC.ItemWidthStack.push_back(w_item_last);
7685     for (int i = 0; i < components - 2; i++)
7686         window->DC.ItemWidthStack.push_back(w_item_one);
7687     window->DC.ItemWidth = (components == 1) ? w_item_last : w_item_one;
7688     g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
7689 }
7690 
PopItemWidth()7691 void ImGui::PopItemWidth()
7692 {
7693     ImGuiWindow* window = GetCurrentWindow();
7694     window->DC.ItemWidth = window->DC.ItemWidthStack.back();
7695     window->DC.ItemWidthStack.pop_back();
7696 }
7697 
7698 // Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().
7699 // The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()
CalcItemWidth()7700 float ImGui::CalcItemWidth()
7701 {
7702     ImGuiContext& g = *GImGui;
7703     ImGuiWindow* window = g.CurrentWindow;
7704     float w;
7705     if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth)
7706         w = g.NextItemData.Width;
7707     else
7708         w = window->DC.ItemWidth;
7709     if (w < 0.0f)
7710     {
7711         float region_max_x = GetContentRegionMaxAbs().x;
7712         w = ImMax(1.0f, region_max_x - window->DC.CursorPos.x + w);
7713     }
7714     w = IM_FLOOR(w);
7715     return w;
7716 }
7717 
7718 // [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().
7719 // Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.
7720 // Note that only CalcItemWidth() is publicly exposed.
7721 // 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)7722 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)
7723 {
7724     ImGuiWindow* window = GImGui->CurrentWindow;
7725 
7726     ImVec2 region_max;
7727     if (size.x < 0.0f || size.y < 0.0f)
7728         region_max = GetContentRegionMaxAbs();
7729 
7730     if (size.x == 0.0f)
7731         size.x = default_w;
7732     else if (size.x < 0.0f)
7733         size.x = ImMax(4.0f, region_max.x - window->DC.CursorPos.x + size.x);
7734 
7735     if (size.y == 0.0f)
7736         size.y = default_h;
7737     else if (size.y < 0.0f)
7738         size.y = ImMax(4.0f, region_max.y - window->DC.CursorPos.y + size.y);
7739 
7740     return size;
7741 }
7742 
GetTextLineHeight()7743 float ImGui::GetTextLineHeight()
7744 {
7745     ImGuiContext& g = *GImGui;
7746     return g.FontSize;
7747 }
7748 
GetTextLineHeightWithSpacing()7749 float ImGui::GetTextLineHeightWithSpacing()
7750 {
7751     ImGuiContext& g = *GImGui;
7752     return g.FontSize + g.Style.ItemSpacing.y;
7753 }
7754 
GetFrameHeight()7755 float ImGui::GetFrameHeight()
7756 {
7757     ImGuiContext& g = *GImGui;
7758     return g.FontSize + g.Style.FramePadding.y * 2.0f;
7759 }
7760 
GetFrameHeightWithSpacing()7761 float ImGui::GetFrameHeightWithSpacing()
7762 {
7763     ImGuiContext& g = *GImGui;
7764     return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
7765 }
7766 
7767 // FIXME: All the Contents Region function are messy or misleading. WE WILL AIM TO OBSOLETE ALL OF THEM WITH A NEW "WORK RECT" API. Thanks for your patience!
7768 
7769 // FIXME: This is in window space (not screen space!).
GetContentRegionMax()7770 ImVec2 ImGui::GetContentRegionMax()
7771 {
7772     ImGuiContext& g = *GImGui;
7773     ImGuiWindow* window = g.CurrentWindow;
7774     ImVec2 mx = window->ContentRegionRect.Max - window->Pos;
7775     if (window->DC.CurrentColumns || g.CurrentTable)
7776         mx.x = window->WorkRect.Max.x - window->Pos.x;
7777     return mx;
7778 }
7779 
7780 // [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features.
GetContentRegionMaxAbs()7781 ImVec2 ImGui::GetContentRegionMaxAbs()
7782 {
7783     ImGuiContext& g = *GImGui;
7784     ImGuiWindow* window = g.CurrentWindow;
7785     ImVec2 mx = window->ContentRegionRect.Max;
7786     if (window->DC.CurrentColumns || g.CurrentTable)
7787         mx.x = window->WorkRect.Max.x;
7788     return mx;
7789 }
7790 
GetContentRegionAvail()7791 ImVec2 ImGui::GetContentRegionAvail()
7792 {
7793     ImGuiWindow* window = GImGui->CurrentWindow;
7794     return GetContentRegionMaxAbs() - window->DC.CursorPos;
7795 }
7796 
7797 // In window space (not screen space!)
GetWindowContentRegionMin()7798 ImVec2 ImGui::GetWindowContentRegionMin()
7799 {
7800     ImGuiWindow* window = GImGui->CurrentWindow;
7801     return window->ContentRegionRect.Min - window->Pos;
7802 }
7803 
GetWindowContentRegionMax()7804 ImVec2 ImGui::GetWindowContentRegionMax()
7805 {
7806     ImGuiWindow* window = GImGui->CurrentWindow;
7807     return window->ContentRegionRect.Max - window->Pos;
7808 }
7809 
7810 // 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.)
7811 // Groups are currently a mishmash of functionalities which should perhaps be clarified and separated.
BeginGroup()7812 void ImGui::BeginGroup()
7813 {
7814     ImGuiContext& g = *GImGui;
7815     ImGuiWindow* window = g.CurrentWindow;
7816 
7817     g.GroupStack.resize(g.GroupStack.Size + 1);
7818     ImGuiGroupData& group_data = g.GroupStack.back();
7819     group_data.WindowID = window->ID;
7820     group_data.BackupCursorPos = window->DC.CursorPos;
7821     group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
7822     group_data.BackupIndent = window->DC.Indent;
7823     group_data.BackupGroupOffset = window->DC.GroupOffset;
7824     group_data.BackupCurrLineSize = window->DC.CurrLineSize;
7825     group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;
7826     group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
7827     group_data.BackupHoveredIdIsAlive = g.HoveredId != 0;
7828     group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
7829     group_data.EmitItem = true;
7830 
7831     window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
7832     window->DC.Indent = window->DC.GroupOffset;
7833     window->DC.CursorMaxPos = window->DC.CursorPos;
7834     window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
7835     if (g.LogEnabled)
7836         g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
7837 }
7838 
EndGroup()7839 void ImGui::EndGroup()
7840 {
7841     ImGuiContext& g = *GImGui;
7842     ImGuiWindow* window = g.CurrentWindow;
7843     IM_ASSERT(g.GroupStack.Size > 0); // Mismatched BeginGroup()/EndGroup() calls
7844 
7845     ImGuiGroupData& group_data = g.GroupStack.back();
7846     IM_ASSERT(group_data.WindowID == window->ID); // EndGroup() in wrong window?
7847 
7848     ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos));
7849 
7850     window->DC.CursorPos = group_data.BackupCursorPos;
7851     window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
7852     window->DC.Indent = group_data.BackupIndent;
7853     window->DC.GroupOffset = group_data.BackupGroupOffset;
7854     window->DC.CurrLineSize = group_data.BackupCurrLineSize;
7855     window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;
7856     if (g.LogEnabled)
7857         g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
7858 
7859     if (!group_data.EmitItem)
7860     {
7861         g.GroupStack.pop_back();
7862         return;
7863     }
7864 
7865     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.
7866     ItemSize(group_bb.GetSize());
7867     ItemAdd(group_bb, 0);
7868 
7869     // 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.
7870     // 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.
7871     // Also if you grep for LastItemId you'll notice it is only used in that context.
7872     // (The two tests not the same because ActiveIdIsAlive is an ID itself, in order to be able to handle ActiveId being overwritten during the frame.)
7873     const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId;
7874     const bool group_contains_prev_active_id = (group_data.BackupActiveIdPreviousFrameIsAlive == false) && (g.ActiveIdPreviousFrameIsAlive == true);
7875     if (group_contains_curr_active_id)
7876         g.LastItemData.ID = g.ActiveId;
7877     else if (group_contains_prev_active_id)
7878         g.LastItemData.ID = g.ActiveIdPreviousFrame;
7879     g.LastItemData.Rect = group_bb;
7880 
7881     // Forward Hovered flag
7882     const bool group_contains_curr_hovered_id = (group_data.BackupHoveredIdIsAlive == false) && g.HoveredId != 0;
7883     if (group_contains_curr_hovered_id)
7884         g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
7885 
7886     // Forward Edited flag
7887     if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame)
7888         g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;
7889 
7890     // Forward Deactivated flag
7891     g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDeactivated;
7892     if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame)
7893         g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Deactivated;
7894 
7895     g.GroupStack.pop_back();
7896     //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255));   // [Debug]
7897 }
7898 
7899 
7900 //-----------------------------------------------------------------------------
7901 // [SECTION] SCROLLING
7902 //-----------------------------------------------------------------------------
7903 
7904 // Helper to snap on edges when aiming at an item very close to the edge,
7905 // So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling.
7906 // When we refactor the scrolling API this may be configurable with a flag?
7907 // Note that the effect for this won't be visible on X axis with default Style settings as WindowPadding.x == ItemSpacing.x by default.
CalcScrollEdgeSnap(float target,float snap_min,float snap_max,float snap_threshold,float center_ratio)7908 static float CalcScrollEdgeSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio)
7909 {
7910     if (target <= snap_min + snap_threshold)
7911         return ImLerp(snap_min, target, center_ratio);
7912     if (target >= snap_max - snap_threshold)
7913         return ImLerp(target, snap_max, center_ratio);
7914     return target;
7915 }
7916 
CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow * window)7917 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
7918 {
7919     ImVec2 scroll = window->Scroll;
7920     if (window->ScrollTarget.x < FLT_MAX)
7921     {
7922         float decoration_total_width = window->ScrollbarSizes.x;
7923         float center_x_ratio = window->ScrollTargetCenterRatio.x;
7924         float scroll_target_x = window->ScrollTarget.x;
7925         if (window->ScrollTargetEdgeSnapDist.x > 0.0f)
7926         {
7927             float snap_x_min = 0.0f;
7928             float snap_x_max = window->ScrollMax.x + window->SizeFull.x - decoration_total_width;
7929             scroll_target_x = CalcScrollEdgeSnap(scroll_target_x, snap_x_min, snap_x_max, window->ScrollTargetEdgeSnapDist.x, center_x_ratio);
7930         }
7931         scroll.x = scroll_target_x - center_x_ratio * (window->SizeFull.x - decoration_total_width);
7932     }
7933     if (window->ScrollTarget.y < FLT_MAX)
7934     {
7935         float decoration_total_height = window->TitleBarHeight() + window->MenuBarHeight() + window->ScrollbarSizes.y;
7936         float center_y_ratio = window->ScrollTargetCenterRatio.y;
7937         float scroll_target_y = window->ScrollTarget.y;
7938         if (window->ScrollTargetEdgeSnapDist.y > 0.0f)
7939         {
7940             float snap_y_min = 0.0f;
7941             float snap_y_max = window->ScrollMax.y + window->SizeFull.y - decoration_total_height;
7942             scroll_target_y = CalcScrollEdgeSnap(scroll_target_y, snap_y_min, snap_y_max, window->ScrollTargetEdgeSnapDist.y, center_y_ratio);
7943         }
7944         scroll.y = scroll_target_y - center_y_ratio * (window->SizeFull.y - decoration_total_height);
7945     }
7946     scroll.x = IM_FLOOR(ImMax(scroll.x, 0.0f));
7947     scroll.y = IM_FLOOR(ImMax(scroll.y, 0.0f));
7948     if (!window->Collapsed && !window->SkipItems)
7949     {
7950         scroll.x = ImMin(scroll.x, window->ScrollMax.x);
7951         scroll.y = ImMin(scroll.y, window->ScrollMax.y);
7952     }
7953     return scroll;
7954 }
7955 
7956 // Scroll to keep newly navigated item fully into view
ScrollToBringRectIntoView(ImGuiWindow * window,const ImRect & item_rect)7957 ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect)
7958 {
7959     ImGuiContext& g = *GImGui;
7960     ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));
7961     //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
7962 
7963     ImVec2 delta_scroll;
7964     if (!window_rect.Contains(item_rect))
7965     {
7966         if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x)
7967             SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x - g.Style.ItemSpacing.x, 0.0f);
7968         else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x)
7969             SetScrollFromPosX(window, item_rect.Max.x - window->Pos.x + g.Style.ItemSpacing.x, 1.0f);
7970         if (item_rect.Min.y < window_rect.Min.y)
7971             SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y - g.Style.ItemSpacing.y, 0.0f);
7972         else if (item_rect.Max.y >= window_rect.Max.y)
7973             SetScrollFromPosY(window, item_rect.Max.y - window->Pos.y + g.Style.ItemSpacing.y, 1.0f);
7974 
7975         ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
7976         delta_scroll = next_scroll - window->Scroll;
7977     }
7978 
7979     // Also scroll parent window to keep us into view if necessary
7980     if (window->Flags & ImGuiWindowFlags_ChildWindow)
7981         delta_scroll += ScrollToBringRectIntoView(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll));
7982 
7983     return delta_scroll;
7984 }
7985 
GetScrollX()7986 float ImGui::GetScrollX()
7987 {
7988     ImGuiWindow* window = GImGui->CurrentWindow;
7989     return window->Scroll.x;
7990 }
7991 
GetScrollY()7992 float ImGui::GetScrollY()
7993 {
7994     ImGuiWindow* window = GImGui->CurrentWindow;
7995     return window->Scroll.y;
7996 }
7997 
GetScrollMaxX()7998 float ImGui::GetScrollMaxX()
7999 {
8000     ImGuiWindow* window = GImGui->CurrentWindow;
8001     return window->ScrollMax.x;
8002 }
8003 
GetScrollMaxY()8004 float ImGui::GetScrollMaxY()
8005 {
8006     ImGuiWindow* window = GImGui->CurrentWindow;
8007     return window->ScrollMax.y;
8008 }
8009 
SetScrollX(ImGuiWindow * window,float scroll_x)8010 void ImGui::SetScrollX(ImGuiWindow* window, float scroll_x)
8011 {
8012     window->ScrollTarget.x = scroll_x;
8013     window->ScrollTargetCenterRatio.x = 0.0f;
8014     window->ScrollTargetEdgeSnapDist.x = 0.0f;
8015 }
8016 
SetScrollY(ImGuiWindow * window,float scroll_y)8017 void ImGui::SetScrollY(ImGuiWindow* window, float scroll_y)
8018 {
8019     window->ScrollTarget.y = scroll_y;
8020     window->ScrollTargetCenterRatio.y = 0.0f;
8021     window->ScrollTargetEdgeSnapDist.y = 0.0f;
8022 }
8023 
SetScrollX(float scroll_x)8024 void ImGui::SetScrollX(float scroll_x)
8025 {
8026     ImGuiContext& g = *GImGui;
8027     SetScrollX(g.CurrentWindow, scroll_x);
8028 }
8029 
SetScrollY(float scroll_y)8030 void ImGui::SetScrollY(float scroll_y)
8031 {
8032     ImGuiContext& g = *GImGui;
8033     SetScrollY(g.CurrentWindow, scroll_y);
8034 }
8035 
8036 // Note that a local position will vary depending on initial scroll value,
8037 // This is a little bit confusing so bear with us:
8038 //  - local_pos = (absolution_pos - window->Pos)
8039 //  - So local_x/local_y are 0.0f for a position at the upper-left corner of a window,
8040 //    and generally local_x/local_y are >(padding+decoration) && <(size-padding-decoration) when in the visible area.
8041 //  - They mostly exists because of legacy API.
8042 // Following the rules above, when trying to work with scrolling code, consider that:
8043 //  - SetScrollFromPosY(0.0f) == SetScrollY(0.0f + scroll.y) == has no effect!
8044 //  - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0f) == reset scroll. Of course writing SetScrollY(0.0f) directly then makes more sense
8045 // We store a target position so centering and clamping can occur on the next frame when we are guaranteed to have a known window size
SetScrollFromPosX(ImGuiWindow * window,float local_x,float center_x_ratio)8046 void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
8047 {
8048     IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);
8049     window->ScrollTarget.x = IM_FLOOR(local_x + window->Scroll.x); // Convert local position to scroll offset
8050     window->ScrollTargetCenterRatio.x = center_x_ratio;
8051     window->ScrollTargetEdgeSnapDist.x = 0.0f;
8052 }
8053 
SetScrollFromPosY(ImGuiWindow * window,float local_y,float center_y_ratio)8054 void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
8055 {
8056     IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
8057     const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); // FIXME: Would be nice to have a more standardized access to our scrollable/client rect;
8058     local_y -= decoration_up_height;
8059     window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y); // Convert local position to scroll offset
8060     window->ScrollTargetCenterRatio.y = center_y_ratio;
8061     window->ScrollTargetEdgeSnapDist.y = 0.0f;
8062 }
8063 
SetScrollFromPosX(float local_x,float center_x_ratio)8064 void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)
8065 {
8066     ImGuiContext& g = *GImGui;
8067     SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio);
8068 }
8069 
SetScrollFromPosY(float local_y,float center_y_ratio)8070 void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
8071 {
8072     ImGuiContext& g = *GImGui;
8073     SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio);
8074 }
8075 
8076 // 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)8077 void ImGui::SetScrollHereX(float center_x_ratio)
8078 {
8079     ImGuiContext& g = *GImGui;
8080     ImGuiWindow* window = g.CurrentWindow;
8081     float spacing_x = ImMax(window->WindowPadding.x, g.Style.ItemSpacing.x);
8082     float target_pos_x = ImLerp(g.LastItemData.Rect.Min.x - spacing_x, g.LastItemData.Rect.Max.x + spacing_x, center_x_ratio);
8083     SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos
8084 
8085     // Tweak: snap on edges when aiming at an item very close to the edge
8086     window->ScrollTargetEdgeSnapDist.x = ImMax(0.0f, window->WindowPadding.x - spacing_x);
8087 }
8088 
8089 // 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)8090 void ImGui::SetScrollHereY(float center_y_ratio)
8091 {
8092     ImGuiContext& g = *GImGui;
8093     ImGuiWindow* window = g.CurrentWindow;
8094     float spacing_y = ImMax(window->WindowPadding.y, g.Style.ItemSpacing.y);
8095     float target_pos_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio);
8096     SetScrollFromPosY(window, target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos
8097 
8098     // Tweak: snap on edges when aiming at an item very close to the edge
8099     window->ScrollTargetEdgeSnapDist.y = ImMax(0.0f, window->WindowPadding.y - spacing_y);
8100 }
8101 
8102 //-----------------------------------------------------------------------------
8103 // [SECTION] TOOLTIPS
8104 //-----------------------------------------------------------------------------
8105 
BeginTooltip()8106 void ImGui::BeginTooltip()
8107 {
8108     BeginTooltipEx(ImGuiWindowFlags_None, ImGuiTooltipFlags_None);
8109 }
8110 
BeginTooltipEx(ImGuiWindowFlags extra_flags,ImGuiTooltipFlags tooltip_flags)8111 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags)
8112 {
8113     ImGuiContext& g = *GImGui;
8114 
8115     if (g.DragDropWithinSource || g.DragDropWithinTarget)
8116     {
8117         // 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)
8118         // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
8119         // 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.
8120         //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
8121         ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale);
8122         SetNextWindowPos(tooltip_pos);
8123         SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
8124         //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
8125         tooltip_flags |= ImGuiTooltipFlags_OverridePreviousTooltip;
8126     }
8127 
8128     char window_name[16];
8129     ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
8130     if (tooltip_flags & ImGuiTooltipFlags_OverridePreviousTooltip)
8131         if (ImGuiWindow* window = FindWindowByName(window_name))
8132             if (window->Active)
8133             {
8134                 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
8135                 window->Hidden = true;
8136                 window->HiddenFramesCanSkipItems = 1; // FIXME: This may not be necessary?
8137                 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
8138             }
8139     ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize;
8140     Begin(window_name, NULL, flags | extra_flags);
8141 }
8142 
EndTooltip()8143 void ImGui::EndTooltip()
8144 {
8145     IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip);   // Mismatched BeginTooltip()/EndTooltip() calls
8146     End();
8147 }
8148 
SetTooltipV(const char * fmt,va_list args)8149 void ImGui::SetTooltipV(const char* fmt, va_list args)
8150 {
8151     BeginTooltipEx(0, ImGuiTooltipFlags_OverridePreviousTooltip);
8152     TextV(fmt, args);
8153     EndTooltip();
8154 }
8155 
SetTooltip(const char * fmt,...)8156 void ImGui::SetTooltip(const char* fmt, ...)
8157 {
8158     va_list args;
8159     va_start(args, fmt);
8160     SetTooltipV(fmt, args);
8161     va_end(args);
8162 }
8163 
8164 //-----------------------------------------------------------------------------
8165 // [SECTION] POPUPS
8166 //-----------------------------------------------------------------------------
8167 
8168 // Supported flags: ImGuiPopupFlags_AnyPopupId, ImGuiPopupFlags_AnyPopupLevel
IsPopupOpen(ImGuiID id,ImGuiPopupFlags popup_flags)8169 bool ImGui::IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags)
8170 {
8171     ImGuiContext& g = *GImGui;
8172     if (popup_flags & ImGuiPopupFlags_AnyPopupId)
8173     {
8174         // Return true if any popup is open at the current BeginPopup() level of the popup stack
8175         // This may be used to e.g. test for another popups already opened to handle popups priorities at the same level.
8176         IM_ASSERT(id == 0);
8177         if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
8178             return g.OpenPopupStack.Size > 0;
8179         else
8180             return g.OpenPopupStack.Size > g.BeginPopupStack.Size;
8181     }
8182     else
8183     {
8184         if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
8185         {
8186             // Return true if the popup is open anywhere in the popup stack
8187             for (int n = 0; n < g.OpenPopupStack.Size; n++)
8188                 if (g.OpenPopupStack[n].PopupId == id)
8189                     return true;
8190             return false;
8191         }
8192         else
8193         {
8194             // Return true if the popup is open at the current BeginPopup() level of the popup stack (this is the most-common query)
8195             return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
8196         }
8197     }
8198 }
8199 
IsPopupOpen(const char * str_id,ImGuiPopupFlags popup_flags)8200 bool ImGui::IsPopupOpen(const char* str_id, ImGuiPopupFlags popup_flags)
8201 {
8202     ImGuiContext& g = *GImGui;
8203     ImGuiID id = (popup_flags & ImGuiPopupFlags_AnyPopupId) ? 0 : g.CurrentWindow->GetID(str_id);
8204     if ((popup_flags & ImGuiPopupFlags_AnyPopupLevel) && id != 0)
8205         IM_ASSERT(0 && "Cannot use IsPopupOpen() with a string id and ImGuiPopupFlags_AnyPopupLevel."); // But non-string version is legal and used internally
8206     return IsPopupOpen(id, popup_flags);
8207 }
8208 
GetTopMostPopupModal()8209 ImGuiWindow* ImGui::GetTopMostPopupModal()
8210 {
8211     ImGuiContext& g = *GImGui;
8212     for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
8213         if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
8214             if (popup->Flags & ImGuiWindowFlags_Modal)
8215                 return popup;
8216     return NULL;
8217 }
8218 
OpenPopup(const char * str_id,ImGuiPopupFlags popup_flags)8219 void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags)
8220 {
8221     ImGuiContext& g = *GImGui;
8222     OpenPopupEx(g.CurrentWindow->GetID(str_id), popup_flags);
8223 }
8224 
OpenPopup(ImGuiID id,ImGuiPopupFlags popup_flags)8225 void ImGui::OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags)
8226 {
8227     OpenPopupEx(id, popup_flags);
8228 }
8229 
8230 // Mark popup as open (toggle toward open state).
8231 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
8232 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
8233 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
OpenPopupEx(ImGuiID id,ImGuiPopupFlags popup_flags)8234 void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags)
8235 {
8236     ImGuiContext& g = *GImGui;
8237     ImGuiWindow* parent_window = g.CurrentWindow;
8238     const int current_stack_size = g.BeginPopupStack.Size;
8239 
8240     if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup)
8241         if (IsPopupOpen(0u, ImGuiPopupFlags_AnyPopupId))
8242             return;
8243 
8244     ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
8245     popup_ref.PopupId = id;
8246     popup_ref.Window = NULL;
8247     popup_ref.SourceWindow = g.NavWindow;
8248     popup_ref.OpenFrameCount = g.FrameCount;
8249     popup_ref.OpenParentId = parent_window->IDStack.back();
8250     popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
8251     popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
8252 
8253     IMGUI_DEBUG_LOG_POPUP("OpenPopupEx(0x%08X)\n", id);
8254     if (g.OpenPopupStack.Size < current_stack_size + 1)
8255     {
8256         g.OpenPopupStack.push_back(popup_ref);
8257     }
8258     else
8259     {
8260         // 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
8261         // 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
8262         // 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.
8263         if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
8264         {
8265             g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
8266         }
8267         else
8268         {
8269             // Close child popups if any, then flag popup for open/reopen
8270             ClosePopupToLevel(current_stack_size, false);
8271             g.OpenPopupStack.push_back(popup_ref);
8272         }
8273 
8274         // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
8275         // This is equivalent to what ClosePopupToLevel() does.
8276         //if (g.OpenPopupStack[current_stack_size].PopupId == id)
8277         //    FocusWindow(parent_window);
8278     }
8279 }
8280 
8281 // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
8282 // This function closes any popups that are over 'ref_window'.
ClosePopupsOverWindow(ImGuiWindow * ref_window,bool restore_focus_to_window_under_popup)8283 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
8284 {
8285     ImGuiContext& g = *GImGui;
8286     if (g.OpenPopupStack.Size == 0)
8287         return;
8288 
8289     // Don't close our own child popup windows.
8290     int popup_count_to_keep = 0;
8291     if (ref_window)
8292     {
8293         // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
8294         for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
8295         {
8296             ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];
8297             if (!popup.Window)
8298                 continue;
8299             IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
8300             if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
8301                 continue;
8302 
8303             // Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow)
8304             // - With this stack of window, clicking/focusing Popup1 will close Popup2 and Popup3:
8305             //     Window -> Popup1 -> Popup2 -> Popup3
8306             // - Each popups may contain child windows, which is why we compare ->RootWindow!
8307             //     Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child
8308             bool ref_window_is_descendent_of_popup = false;
8309             for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++)
8310                 if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window)
8311                     if (popup_window->RootWindow == ref_window->RootWindow)
8312                     {
8313                         ref_window_is_descendent_of_popup = true;
8314                         break;
8315                     }
8316             if (!ref_window_is_descendent_of_popup)
8317                 break;
8318         }
8319     }
8320     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
8321     {
8322         IMGUI_DEBUG_LOG_POPUP("ClosePopupsOverWindow(\"%s\") -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep);
8323         ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup);
8324     }
8325 }
8326 
ClosePopupToLevel(int remaining,bool restore_focus_to_window_under_popup)8327 void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
8328 {
8329     ImGuiContext& g = *GImGui;
8330     IMGUI_DEBUG_LOG_POPUP("ClosePopupToLevel(%d), restore_focus_to_window_under_popup=%d\n", remaining, restore_focus_to_window_under_popup);
8331     IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
8332 
8333     // Trim open popup stack
8334     ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow;
8335     ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window;
8336     g.OpenPopupStack.resize(remaining);
8337 
8338     if (restore_focus_to_window_under_popup)
8339     {
8340         if (focus_window && !focus_window->WasActive && popup_window)
8341         {
8342             // Fallback
8343             FocusTopMostWindowUnderOne(popup_window, NULL);
8344         }
8345         else
8346         {
8347             if (g.NavLayer == ImGuiNavLayer_Main && focus_window)
8348                 focus_window = NavRestoreLastChildNavWindow(focus_window);
8349             FocusWindow(focus_window);
8350         }
8351     }
8352 }
8353 
8354 // Close the popup we have begin-ed into.
CloseCurrentPopup()8355 void ImGui::CloseCurrentPopup()
8356 {
8357     ImGuiContext& g = *GImGui;
8358     int popup_idx = g.BeginPopupStack.Size - 1;
8359     if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
8360         return;
8361 
8362     // Closing a menu closes its top-most parent popup (unless a modal)
8363     while (popup_idx > 0)
8364     {
8365         ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;
8366         ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
8367         bool close_parent = false;
8368         if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
8369             if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal))
8370                 close_parent = true;
8371         if (!close_parent)
8372             break;
8373         popup_idx--;
8374     }
8375     IMGUI_DEBUG_LOG_POPUP("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx);
8376     ClosePopupToLevel(popup_idx, true);
8377 
8378     // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
8379     // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
8380     // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
8381     if (ImGuiWindow* window = g.NavWindow)
8382         window->DC.NavHideHighlightOneFrame = true;
8383 }
8384 
8385 // Attention! BeginPopup() adds default flags which BeginPopupEx()!
BeginPopupEx(ImGuiID id,ImGuiWindowFlags flags)8386 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags)
8387 {
8388     ImGuiContext& g = *GImGui;
8389     if (!IsPopupOpen(id, ImGuiPopupFlags_None))
8390     {
8391         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
8392         return false;
8393     }
8394 
8395     char name[20];
8396     if (flags & ImGuiWindowFlags_ChildMenu)
8397         ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth
8398     else
8399         ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
8400 
8401     flags |= ImGuiWindowFlags_Popup;
8402     bool is_open = Begin(name, NULL, flags);
8403     if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
8404         EndPopup();
8405 
8406     return is_open;
8407 }
8408 
BeginPopup(const char * str_id,ImGuiWindowFlags flags)8409 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
8410 {
8411     ImGuiContext& g = *GImGui;
8412     if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
8413     {
8414         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
8415         return false;
8416     }
8417     flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
8418     return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags);
8419 }
8420 
8421 // If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
8422 // 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)8423 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
8424 {
8425     ImGuiContext& g = *GImGui;
8426     ImGuiWindow* window = g.CurrentWindow;
8427     const ImGuiID id = window->GetID(name);
8428     if (!IsPopupOpen(id, ImGuiPopupFlags_None))
8429     {
8430         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
8431         return false;
8432     }
8433 
8434     // Center modal windows by default for increased visibility
8435     // (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves)
8436     // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
8437     if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
8438     {
8439         const ImGuiViewport* viewport = GetMainViewport();
8440         SetNextWindowPos(viewport->GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
8441     }
8442 
8443     flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse;
8444     const bool is_open = Begin(name, p_open, flags);
8445     if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
8446     {
8447         EndPopup();
8448         if (is_open)
8449             ClosePopupToLevel(g.BeginPopupStack.Size, true);
8450         return false;
8451     }
8452     return is_open;
8453 }
8454 
EndPopup()8455 void ImGui::EndPopup()
8456 {
8457     ImGuiContext& g = *GImGui;
8458     ImGuiWindow* window = g.CurrentWindow;
8459     IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup);  // Mismatched BeginPopup()/EndPopup() calls
8460     IM_ASSERT(g.BeginPopupStack.Size > 0);
8461 
8462     // Make all menus and popups wrap around for now, may need to expose that policy (e.g. focus scope could include wrap/loop policy flags used by new move requests)
8463     if (g.NavWindow == window)
8464         NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
8465 
8466     // Child-popups don't need to be laid out
8467     IM_ASSERT(g.WithinEndChild == false);
8468     if (window->Flags & ImGuiWindowFlags_ChildWindow)
8469         g.WithinEndChild = true;
8470     End();
8471     g.WithinEndChild = false;
8472 }
8473 
8474 // Helper to open a popup if mouse button is released over the item
8475 // - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup()
OpenPopupOnItemClick(const char * str_id,ImGuiPopupFlags popup_flags)8476 void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags)
8477 {
8478     ImGuiContext& g = *GImGui;
8479     ImGuiWindow* window = g.CurrentWindow;
8480     int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8481     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
8482     {
8483         ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID;    // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
8484         IM_ASSERT(id != 0);                                             // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
8485         OpenPopupEx(id, popup_flags);
8486     }
8487 }
8488 
8489 // This is a helper to handle the simplest case of associating one named popup to one given widget.
8490 // - To create a popup associated to the last item, you generally want to pass a NULL value to str_id.
8491 // - To create a popup with a specific identifier, pass it in str_id.
8492 //    - This is useful when using using BeginPopupContextItem() on an item which doesn't have an identifier, e.g. a Text() call.
8493 //    - This is useful when multiple code locations may want to manipulate/open the same popup, given an explicit id.
8494 // - You may want to handle the whole on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
8495 //   This is essentially the same as:
8496 //       id = str_id ? GetID(str_id) : GetItemID();
8497 //       OpenPopupOnItemClick(str_id);
8498 //       return BeginPopup(id);
8499 //   Which is essentially the same as:
8500 //       id = str_id ? GetID(str_id) : GetItemID();
8501 //       if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))
8502 //           OpenPopup(id);
8503 //       return BeginPopup(id);
8504 //   The main difference being that this is tweaked to avoid computing the ID twice.
BeginPopupContextItem(const char * str_id,ImGuiPopupFlags popup_flags)8505 bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags)
8506 {
8507     ImGuiContext& g = *GImGui;
8508     ImGuiWindow* window = g.CurrentWindow;
8509     if (window->SkipItems)
8510         return false;
8511     ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID;    // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
8512     IM_ASSERT(id != 0);                                             // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
8513     int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8514     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
8515         OpenPopupEx(id, popup_flags);
8516     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
8517 }
8518 
BeginPopupContextWindow(const char * str_id,ImGuiPopupFlags popup_flags)8519 bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags)
8520 {
8521     ImGuiContext& g = *GImGui;
8522     ImGuiWindow* window = g.CurrentWindow;
8523     if (!str_id)
8524         str_id = "window_context";
8525     ImGuiID id = window->GetID(str_id);
8526     int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8527     if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
8528         if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered())
8529             OpenPopupEx(id, popup_flags);
8530     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
8531 }
8532 
BeginPopupContextVoid(const char * str_id,ImGuiPopupFlags popup_flags)8533 bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags)
8534 {
8535     ImGuiContext& g = *GImGui;
8536     ImGuiWindow* window = g.CurrentWindow;
8537     if (!str_id)
8538         str_id = "void_context";
8539     ImGuiID id = window->GetID(str_id);
8540     int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8541     if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
8542         if (GetTopMostPopupModal() == NULL)
8543             OpenPopupEx(id, popup_flags);
8544     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
8545 }
8546 
8547 // 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.)
8548 // 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.
8549 // (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor
8550 //  information are available, it may represent the entire platform monitor from the frame of reference of the current viewport.
8551 //  this allows us to have tooltips/popups displayed out of the parent viewport.)
FindBestWindowPosForPopupEx(const ImVec2 & ref_pos,const ImVec2 & size,ImGuiDir * last_dir,const ImRect & r_outer,const ImRect & r_avoid,ImGuiPopupPositionPolicy policy)8552 ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
8553 {
8554     ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
8555     //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
8556     //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
8557 
8558     // Combo Box policy (we want a connecting edge)
8559     if (policy == ImGuiPopupPositionPolicy_ComboBox)
8560     {
8561         const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
8562         for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
8563         {
8564             const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
8565             if (n != -1 && dir == *last_dir) // Already tried this direction?
8566                 continue;
8567             ImVec2 pos;
8568             if (dir == ImGuiDir_Down)  pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y);          // Below, Toward Right (default)
8569             if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
8570             if (dir == ImGuiDir_Left)  pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
8571             if (dir == ImGuiDir_Up)    pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
8572             if (!r_outer.Contains(ImRect(pos, pos + size)))
8573                 continue;
8574             *last_dir = dir;
8575             return pos;
8576         }
8577     }
8578 
8579     // Tooltip and Default popup policy
8580     // (Always first try the direction we used on the last frame, if any)
8581     if (policy == ImGuiPopupPositionPolicy_Tooltip || policy == ImGuiPopupPositionPolicy_Default)
8582     {
8583         const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
8584         for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
8585         {
8586             const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
8587             if (n != -1 && dir == *last_dir) // Already tried this direction?
8588                 continue;
8589 
8590             const float avail_w = (dir == ImGuiDir_Left ? r_avoid.Min.x : r_outer.Max.x) - (dir == ImGuiDir_Right ? r_avoid.Max.x : r_outer.Min.x);
8591             const float avail_h = (dir == ImGuiDir_Up ? r_avoid.Min.y : r_outer.Max.y) - (dir == ImGuiDir_Down ? r_avoid.Max.y : r_outer.Min.y);
8592 
8593             // If there not enough room on one axis, there's no point in positioning on a side on this axis (e.g. when not enough width, use a top/bottom position to maximize available width)
8594             if (avail_w < size.x && (dir == ImGuiDir_Left || dir == ImGuiDir_Right))
8595                 continue;
8596             if (avail_h < size.y && (dir == ImGuiDir_Up || dir == ImGuiDir_Down))
8597                 continue;
8598 
8599             ImVec2 pos;
8600             pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
8601             pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
8602 
8603             // Clamp top-left corner of popup
8604             pos.x = ImMax(pos.x, r_outer.Min.x);
8605             pos.y = ImMax(pos.y, r_outer.Min.y);
8606 
8607             *last_dir = dir;
8608             return pos;
8609         }
8610     }
8611 
8612     // Fallback when not enough room:
8613     *last_dir = ImGuiDir_None;
8614 
8615     // For tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
8616     if (policy == ImGuiPopupPositionPolicy_Tooltip)
8617         return ref_pos + ImVec2(2, 2);
8618 
8619     // Otherwise try to keep within display
8620     ImVec2 pos = ref_pos;
8621     pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
8622     pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
8623     return pos;
8624 }
8625 
8626 // Note that this is used for popups, which can overlap the non work-area of individual viewports.
GetPopupAllowedExtentRect(ImGuiWindow * window)8627 ImRect ImGui::GetPopupAllowedExtentRect(ImGuiWindow* window)
8628 {
8629     ImGuiContext& g = *GImGui;
8630     IM_UNUSED(window);
8631     ImRect r_screen = ((ImGuiViewportP*)(void*)GetMainViewport())->GetMainRect();
8632     ImVec2 padding = g.Style.DisplaySafeAreaPadding;
8633     r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
8634     return r_screen;
8635 }
8636 
FindBestWindowPosForPopup(ImGuiWindow * window)8637 ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
8638 {
8639     ImGuiContext& g = *GImGui;
8640 
8641     ImRect r_outer = GetPopupAllowedExtentRect(window);
8642     if (window->Flags & ImGuiWindowFlags_ChildMenu)
8643     {
8644         // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
8645         // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
8646         IM_ASSERT(g.CurrentWindow == window);
8647         ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2].Window;
8648         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).
8649         ImRect r_avoid;
8650         if (parent_window->DC.MenuBarAppending)
8651             r_avoid = ImRect(-FLT_MAX, parent_window->ClipRect.Min.y, FLT_MAX, parent_window->ClipRect.Max.y); // Avoid parent menu-bar. If we wanted multi-line menu-bar, we may instead want to have the calling window setup e.g. a NextWindowData.PosConstraintAvoidRect field
8652         else
8653             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);
8654         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default);
8655     }
8656     if (window->Flags & ImGuiWindowFlags_Popup)
8657     {
8658         ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
8659         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default);
8660     }
8661     if (window->Flags & ImGuiWindowFlags_Tooltip)
8662     {
8663         // Position tooltip (always follows mouse)
8664         float sc = g.Style.MouseCursorScale;
8665         ImVec2 ref_pos = NavCalcPreferredRefPos();
8666         ImRect r_avoid;
8667         if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
8668             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
8669         else
8670             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.
8671         return FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip);
8672     }
8673     IM_ASSERT(0);
8674     return window->Pos;
8675 }
8676 
8677 //-----------------------------------------------------------------------------
8678 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
8679 //-----------------------------------------------------------------------------
8680 
8681 // FIXME-NAV: The existence of SetNavID vs SetFocusID properly needs to be clarified/reworked.
SetNavID(ImGuiID id,ImGuiNavLayer nav_layer,ImGuiID focus_scope_id,const ImRect & rect_rel)8682 void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel)
8683 {
8684     ImGuiContext& g = *GImGui;
8685     IM_ASSERT(g.NavWindow != NULL);
8686     IM_ASSERT(nav_layer == ImGuiNavLayer_Main || nav_layer == ImGuiNavLayer_Menu);
8687     g.NavId = id;
8688     g.NavLayer = nav_layer;
8689     g.NavFocusScopeId = focus_scope_id;
8690     g.NavWindow->NavLastIds[nav_layer] = id;
8691     g.NavWindow->NavRectRel[nav_layer] = rect_rel;
8692     //g.NavDisableHighlight = false;
8693     //g.NavDisableMouseHover = g.NavMousePosDirty = true;
8694 }
8695 
SetFocusID(ImGuiID id,ImGuiWindow * window)8696 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
8697 {
8698     ImGuiContext& g = *GImGui;
8699     IM_ASSERT(id != 0);
8700 
8701     // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and window->DC.NavFocusScopeIdCurrent are valid.
8702     // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text)
8703     const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
8704     if (g.NavWindow != window)
8705         g.NavInitRequest = false;
8706     g.NavWindow = window;
8707     g.NavId = id;
8708     g.NavLayer = nav_layer;
8709     g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
8710     window->NavLastIds[nav_layer] = id;
8711     if (g.LastItemData.ID == id)
8712         window->NavRectRel[nav_layer] = ImRect(g.LastItemData.Rect.Min - window->Pos, g.LastItemData.Rect.Max - window->Pos);
8713 
8714     if (g.ActiveIdSource == ImGuiInputSource_Nav)
8715         g.NavDisableMouseHover = true;
8716     else
8717         g.NavDisableHighlight = true;
8718 }
8719 
ImGetDirQuadrantFromDelta(float dx,float dy)8720 ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
8721 {
8722     if (ImFabs(dx) > ImFabs(dy))
8723         return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
8724     return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
8725 }
8726 
NavScoreItemDistInterval(float a0,float a1,float b0,float b1)8727 static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
8728 {
8729     if (a1 < b0)
8730         return a1 - b0;
8731     if (b1 < a0)
8732         return a0 - b1;
8733     return 0.0f;
8734 }
8735 
NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir,ImRect & r,const ImRect & clip_rect)8736 static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect)
8737 {
8738     if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
8739     {
8740         r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y);
8741         r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y);
8742     }
8743     else
8744     {
8745         r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x);
8746         r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x);
8747     }
8748 }
8749 
8750 // Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057
NavScoreItem(ImGuiNavItemData * result,ImRect cand)8751 static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand)
8752 {
8753     ImGuiContext& g = *GImGui;
8754     ImGuiWindow* window = g.CurrentWindow;
8755     if (g.NavLayer != window->DC.NavLayerCurrent)
8756         return false;
8757 
8758     const ImRect curr = g.NavScoringRect; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width)
8759     g.NavScoringCount++;
8760 
8761     // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
8762     if (window->ParentWindow == g.NavWindow)
8763     {
8764         IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened);
8765         if (!window->ClipRect.Overlaps(cand))
8766             return false;
8767         cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
8768     }
8769 
8770     // 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)
8771     // For example, this ensure that items in one column are not reached when moving vertically from items in another column.
8772     NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect);
8773 
8774     // Compute distance between boxes
8775     // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
8776     float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
8777     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
8778     if (dby != 0.0f && dbx != 0.0f)
8779         dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
8780     float dist_box = ImFabs(dbx) + ImFabs(dby);
8781 
8782     // 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)
8783     float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
8784     float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
8785     float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
8786 
8787     // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
8788     ImGuiDir quadrant;
8789     float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
8790     if (dbx != 0.0f || dby != 0.0f)
8791     {
8792         // For non-overlapping boxes, use distance between boxes
8793         dax = dbx;
8794         day = dby;
8795         dist_axial = dist_box;
8796         quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
8797     }
8798     else if (dcx != 0.0f || dcy != 0.0f)
8799     {
8800         // For overlapping boxes with different centers, use distance between centers
8801         dax = dcx;
8802         day = dcy;
8803         dist_axial = dist_center;
8804         quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
8805     }
8806     else
8807     {
8808         // 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)
8809         quadrant = (g.LastItemData.ID < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
8810     }
8811 
8812 #if IMGUI_DEBUG_NAV_SCORING
8813     char buf[128];
8814     if (IsMouseHoveringRect(cand.Min, cand.Max))
8815     {
8816         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]);
8817         ImDrawList* draw_list = GetForegroundDrawList(window);
8818         draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
8819         draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
8820         draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40,0,0,150));
8821         draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
8822     }
8823     else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
8824     {
8825         if (quadrant == g.NavMoveDir)
8826         {
8827             ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
8828             ImDrawList* draw_list = GetForegroundDrawList(window);
8829             draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
8830             draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
8831         }
8832     }
8833 #endif
8834 
8835     // Is it in the quadrant we're interesting in moving to?
8836     bool new_best = false;
8837     const ImGuiDir move_dir = g.NavMoveDir;
8838     if (quadrant == move_dir)
8839     {
8840         // Does it beat the current best candidate?
8841         if (dist_box < result->DistBox)
8842         {
8843             result->DistBox = dist_box;
8844             result->DistCenter = dist_center;
8845             return true;
8846         }
8847         if (dist_box == result->DistBox)
8848         {
8849             // Try using distance between center points to break ties
8850             if (dist_center < result->DistCenter)
8851             {
8852                 result->DistCenter = dist_center;
8853                 new_best = true;
8854             }
8855             else if (dist_center == result->DistCenter)
8856             {
8857                 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
8858                 // (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),
8859                 // 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.
8860                 if (((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
8861                     new_best = true;
8862             }
8863         }
8864     }
8865 
8866     // 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
8867     // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
8868     // 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.
8869     // 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.
8870     // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
8871     if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial)  // Check axial match
8872         if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
8873             if ((move_dir == ImGuiDir_Left && dax < 0.0f) || (move_dir == ImGuiDir_Right && dax > 0.0f) || (move_dir == ImGuiDir_Up && day < 0.0f) || (move_dir == ImGuiDir_Down && day > 0.0f))
8874             {
8875                 result->DistAxial = dist_axial;
8876                 new_best = true;
8877             }
8878 
8879     return new_best;
8880 }
8881 
NavApplyItemToResult(ImGuiNavItemData * result,ImGuiWindow * window,ImGuiID id,const ImRect & nav_bb_rel)8882 static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel)
8883 {
8884     result->Window = window;
8885     result->ID = id;
8886     result->FocusScopeId = window->DC.NavFocusScopeIdCurrent;
8887     result->RectRel = nav_bb_rel;
8888 }
8889 
8890 // 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)8891 static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
8892 {
8893     ImGuiContext& g = *GImGui;
8894     const ImGuiItemFlags item_flags = g.LastItemData.InFlags;
8895     const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
8896 
8897     // Process Init Request
8898     if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
8899     {
8900         // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
8901         const bool candidate_for_nav_default_focus = (item_flags & (ImGuiItemFlags_NoNavDefaultFocus | ImGuiItemFlags_Disabled)) == 0;
8902         if (candidate_for_nav_default_focus || g.NavInitResultId == 0)
8903         {
8904             g.NavInitResultId = id;
8905             g.NavInitResultRectRel = nav_bb_rel;
8906         }
8907         if (candidate_for_nav_default_focus)
8908         {
8909             g.NavInitRequest = false; // Found a match, clear request
8910             NavUpdateAnyRequestFlag();
8911         }
8912     }
8913 
8914     // Process Move Request (scoring for navigation)
8915     // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy)
8916     if (g.NavMoveScoringItems)
8917     {
8918         if ((g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav)))
8919         {
8920             ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
8921             bool new_best = NavScoreItem(result, nav_bb);
8922 
8923 #if IMGUI_DEBUG_NAV_SCORING
8924             // [DEBUG] Scoring all items in NavWindow at all times
8925             if (g.NavMoveFlags & ImGuiNavMoveFlags_DebugNoResult)
8926                 new_best = false;
8927 #endif
8928             if (new_best)
8929                 NavApplyItemToResult(result, window, id, nav_bb_rel);
8930 
8931             // Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
8932             const float VISIBLE_RATIO = 0.70f;
8933             if ((g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
8934                 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)
8935                     if (NavScoreItem(&g.NavMoveResultLocalVisible, nav_bb))
8936                         NavApplyItemToResult(&g.NavMoveResultLocalVisible, window, id, nav_bb_rel);
8937         }
8938     }
8939 
8940     // Update window-relative bounding box of navigated item
8941     if (g.NavId == id)
8942     {
8943         g.NavWindow = window;                                           // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
8944         g.NavLayer = window->DC.NavLayerCurrent;
8945         g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
8946         g.NavIdIsAlive = true;
8947         window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel;    // Store item bounding box (relative to window position)
8948     }
8949 }
8950 
NavMoveRequestButNoResultYet()8951 bool ImGui::NavMoveRequestButNoResultYet()
8952 {
8953     ImGuiContext& g = *GImGui;
8954     return g.NavMoveScoringItems && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
8955 }
8956 
NavMoveRequestCancel()8957 void ImGui::NavMoveRequestCancel()
8958 {
8959     ImGuiContext& g = *GImGui;
8960     g.NavMoveSubmitted = g.NavMoveScoringItems = false;
8961     NavUpdateAnyRequestFlag();
8962 }
8963 
8964 // Forward will reuse the move request again on the next frame (generally with modifications done to it)
NavMoveRequestForward(ImGuiDir move_dir,ImGuiDir clip_dir,ImGuiNavMoveFlags move_flags)8965 void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags)
8966 {
8967     ImGuiContext& g = *GImGui;
8968     IM_ASSERT(g.NavMoveForwardToNextFrame == false);
8969     NavMoveRequestCancel();
8970     g.NavMoveForwardToNextFrame = true;
8971     g.NavMoveDir = move_dir;
8972     g.NavMoveClipDir = clip_dir;
8973     g.NavMoveFlags = move_flags | ImGuiNavMoveFlags_Forwarded;
8974 }
8975 
8976 // Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
8977 // popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.
NavMoveRequestTryWrapping(ImGuiWindow * window,ImGuiNavMoveFlags wrap_flags)8978 void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags wrap_flags)
8979 {
8980     ImGuiContext& g = *GImGui;
8981     IM_ASSERT(wrap_flags != 0); // Call with _WrapX, _WrapY, _LoopX, _LoopY
8982     // In theory we should test for NavMoveRequestButNoResultYet() but there's no point doing it, NavEndFrame() will do the same test
8983     if (g.NavWindow == window && g.NavMoveScoringItems && g.NavLayer == ImGuiNavLayer_Main)
8984         g.NavMoveFlags |= wrap_flags;
8985 }
8986 
8987 // FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
8988 // This way we could find the last focused window among our children. It would be much less confusing this way?
NavSaveLastChildNavWindowIntoParent(ImGuiWindow * nav_window)8989 static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window)
8990 {
8991     ImGuiWindow* parent = nav_window;
8992     while (parent && parent->RootWindow != parent && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
8993         parent = parent->ParentWindow;
8994     if (parent && parent != nav_window)
8995         parent->NavLastChildNavWindow = nav_window;
8996 }
8997 
8998 // Restore the last focused child.
8999 // Call when we are expected to land on the Main Layer (0) after FocusWindow()
NavRestoreLastChildNavWindow(ImGuiWindow * window)9000 static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
9001 {
9002     if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive)
9003         return window->NavLastChildNavWindow;
9004     return window;
9005 }
9006 
NavRestoreLayer(ImGuiNavLayer layer)9007 void ImGui::NavRestoreLayer(ImGuiNavLayer layer)
9008 {
9009     ImGuiContext& g = *GImGui;
9010     if (layer == ImGuiNavLayer_Main)
9011         g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow);
9012     ImGuiWindow* window = g.NavWindow;
9013     if (window->NavLastIds[layer] != 0)
9014     {
9015         SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]);
9016         g.NavDisableHighlight = false;
9017         g.NavDisableMouseHover = g.NavMousePosDirty = true;
9018     }
9019     else
9020     {
9021         g.NavLayer = layer;
9022         NavInitWindow(window, true);
9023     }
9024 }
9025 
NavUpdateAnyRequestFlag()9026 static inline void ImGui::NavUpdateAnyRequestFlag()
9027 {
9028     ImGuiContext& g = *GImGui;
9029     g.NavAnyRequest = g.NavMoveScoringItems || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
9030     if (g.NavAnyRequest)
9031         IM_ASSERT(g.NavWindow != NULL);
9032 }
9033 
9034 // This needs to be called before we submit any widget (aka in or before Begin)
NavInitWindow(ImGuiWindow * window,bool force_reinit)9035 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
9036 {
9037     ImGuiContext& g = *GImGui;
9038     IM_ASSERT(window == g.NavWindow);
9039 
9040     if (window->Flags & ImGuiWindowFlags_NoNavInputs)
9041     {
9042         g.NavId = g.NavFocusScopeId = 0;
9043         return;
9044     }
9045 
9046     bool init_for_nav = false;
9047     if (window == window->RootWindow || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
9048         init_for_nav = true;
9049     IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer);
9050     if (init_for_nav)
9051     {
9052         SetNavID(0, g.NavLayer, 0, ImRect());
9053         g.NavInitRequest = true;
9054         g.NavInitRequestFromMove = false;
9055         g.NavInitResultId = 0;
9056         g.NavInitResultRectRel = ImRect();
9057         NavUpdateAnyRequestFlag();
9058     }
9059     else
9060     {
9061         g.NavId = window->NavLastIds[0];
9062         g.NavFocusScopeId = 0;
9063     }
9064 }
9065 
NavCalcPreferredRefPos()9066 static ImVec2 ImGui::NavCalcPreferredRefPos()
9067 {
9068     ImGuiContext& g = *GImGui;
9069     if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow)
9070     {
9071         // Mouse (we need a fallback in case the mouse becomes invalid after being used)
9072         if (IsMousePosValid(&g.IO.MousePos))
9073             return g.IO.MousePos;
9074         return g.LastValidMousePos;
9075     }
9076     else
9077     {
9078         // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item.
9079         const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
9080         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()));
9081         ImGuiViewport* viewport = GetMainViewport();
9082         return ImFloor(ImClamp(pos, viewport->Pos, viewport->Pos + viewport->Size)); // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta.
9083     }
9084 }
9085 
GetNavInputAmount(ImGuiNavInput n,ImGuiInputReadMode mode)9086 float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode)
9087 {
9088     ImGuiContext& g = *GImGui;
9089     if (mode == ImGuiInputReadMode_Down)
9090         return g.IO.NavInputs[n];                         // Instant, read analog input (0.0f..1.0f, as provided by user)
9091 
9092     const float t = g.IO.NavInputsDownDuration[n];
9093     if (t < 0.0f && mode == ImGuiInputReadMode_Released)  // Return 1.0f when just released, no repeat, ignore analog input.
9094         return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
9095     if (t < 0.0f)
9096         return 0.0f;
9097     if (mode == ImGuiInputReadMode_Pressed)               // Return 1.0f when just pressed, no repeat, ignore analog input.
9098         return (t == 0.0f) ? 1.0f : 0.0f;
9099     if (mode == ImGuiInputReadMode_Repeat)
9100         return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.80f);
9101     if (mode == ImGuiInputReadMode_RepeatSlow)
9102         return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 1.25f, g.IO.KeyRepeatRate * 2.00f);
9103     if (mode == ImGuiInputReadMode_RepeatFast)
9104         return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.30f);
9105     return 0.0f;
9106 }
9107 
GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources,ImGuiInputReadMode mode,float slow_factor,float fast_factor)9108 ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor)
9109 {
9110     ImVec2 delta(0.0f, 0.0f);
9111     if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
9112         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode)   - GetNavInputAmount(ImGuiNavInput_KeyLeft_,   mode), GetNavInputAmount(ImGuiNavInput_KeyDown_,   mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_,   mode));
9113     if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
9114         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode)   - GetNavInputAmount(ImGuiNavInput_DpadLeft,   mode), GetNavInputAmount(ImGuiNavInput_DpadDown,   mode) - GetNavInputAmount(ImGuiNavInput_DpadUp,   mode));
9115     if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
9116         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode));
9117     if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow))
9118         delta *= slow_factor;
9119     if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast))
9120         delta *= fast_factor;
9121     return delta;
9122 }
9123 
NavUpdate()9124 static void ImGui::NavUpdate()
9125 {
9126     ImGuiContext& g = *GImGui;
9127     ImGuiIO& io = g.IO;
9128 
9129     io.WantSetMousePos = false;
9130 #if 0
9131     if (g.NavScoringCount > 0) IMGUI_DEBUG_LOG("NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest);
9132 #endif
9133 
9134     // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard)
9135     // (do it before we map Keyboard input!)
9136     const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
9137     const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
9138     if (nav_gamepad_active && g.NavInputSource != ImGuiInputSource_Gamepad)
9139     {
9140         if (io.NavInputs[ImGuiNavInput_Activate] > 0.0f || io.NavInputs[ImGuiNavInput_Input] > 0.0f || io.NavInputs[ImGuiNavInput_Cancel] > 0.0f || io.NavInputs[ImGuiNavInput_Menu] > 0.0f
9141             || io.NavInputs[ImGuiNavInput_DpadLeft] > 0.0f || io.NavInputs[ImGuiNavInput_DpadRight] > 0.0f || io.NavInputs[ImGuiNavInput_DpadUp] > 0.0f || io.NavInputs[ImGuiNavInput_DpadDown] > 0.0f)
9142             g.NavInputSource = ImGuiInputSource_Gamepad;
9143     }
9144 
9145     // Update Keyboard->Nav inputs mapping
9146     if (nav_keyboard_active)
9147     {
9148         #define NAV_MAP_KEY(_KEY, _NAV_INPUT)  do { if (IsKeyDown(io.KeyMap[_KEY])) { io.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_Keyboard; } } while (0)
9149         NAV_MAP_KEY(ImGuiKey_Space,     ImGuiNavInput_Activate );
9150         NAV_MAP_KEY(ImGuiKey_Enter,     ImGuiNavInput_Input    );
9151         NAV_MAP_KEY(ImGuiKey_Escape,    ImGuiNavInput_Cancel   );
9152         NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ );
9153         NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
9154         NAV_MAP_KEY(ImGuiKey_UpArrow,   ImGuiNavInput_KeyUp_   );
9155         NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
9156         if (io.KeyCtrl)
9157             io.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
9158         if (io.KeyShift)
9159             io.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
9160         #undef NAV_MAP_KEY
9161     }
9162     memcpy(io.NavInputsDownDurationPrev, io.NavInputsDownDuration, sizeof(io.NavInputsDownDuration));
9163     for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++)
9164         io.NavInputsDownDuration[i] = (io.NavInputs[i] > 0.0f) ? (io.NavInputsDownDuration[i] < 0.0f ? 0.0f : io.NavInputsDownDuration[i] + io.DeltaTime) : -1.0f;
9165 
9166     // Process navigation init request (select first/default focus)
9167     if (g.NavInitResultId != 0)
9168         NavUpdateInitResult();
9169     g.NavInitRequest = false;
9170     g.NavInitRequestFromMove = false;
9171     g.NavInitResultId = 0;
9172     g.NavJustMovedToId = 0;
9173 
9174     // Process navigation move request
9175     if (g.NavMoveSubmitted)
9176         NavMoveRequestApplyResult();
9177     g.NavMoveSubmitted = g.NavMoveScoringItems = false;
9178 
9179     // Apply application mouse position movement, after we had a chance to process move request result.
9180     if (g.NavMousePosDirty && g.NavIdIsAlive)
9181     {
9182         // Set mouse position given our knowledge of the navigated item position from last frame
9183         if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
9184             if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
9185             {
9186                 io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos();
9187                 io.WantSetMousePos = true;
9188             }
9189         g.NavMousePosDirty = false;
9190     }
9191     g.NavIdIsAlive = false;
9192     g.NavJustTabbedId = 0;
9193     IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
9194 
9195     // Store our return window (for returning from Menu Layer to Main Layer) and clear it as soon as we step back in our own Layer 0
9196     if (g.NavWindow)
9197         NavSaveLastChildNavWindowIntoParent(g.NavWindow);
9198     if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main)
9199         g.NavWindow->NavLastChildNavWindow = NULL;
9200 
9201     // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
9202     NavUpdateWindowing();
9203 
9204     // Set output flags for user application
9205     io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
9206     io.NavVisible = (io.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL);
9207 
9208     // Process NavCancel input (to close a popup, get back to parent, clear focus)
9209     NavUpdateCancelRequest();
9210 
9211     // Process manual activation request
9212     g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
9213     if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
9214     {
9215         bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
9216         bool activate_pressed = activate_down && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
9217         if (g.ActiveId == 0 && activate_pressed)
9218             g.NavActivateId = g.NavId;
9219         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
9220             g.NavActivateDownId = g.NavId;
9221         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
9222             g.NavActivatePressedId = g.NavId;
9223         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
9224             g.NavInputId = g.NavId;
9225     }
9226     if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
9227         g.NavDisableHighlight = true;
9228     if (g.NavActivateId != 0)
9229         IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
9230 
9231     // Process programmatic activation request
9232     if (g.NavNextActivateId != 0)
9233         g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
9234     g.NavNextActivateId = 0;
9235 
9236     // Process move requests
9237     NavUpdateCreateMoveRequest();
9238     NavUpdateAnyRequestFlag();
9239 
9240     // Scrolling
9241     if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
9242     {
9243         // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
9244         ImGuiWindow* window = g.NavWindow;
9245         const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.
9246         const ImGuiDir move_dir = g.NavMoveDir;
9247         if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll && move_dir != ImGuiDir_None)
9248         {
9249             if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
9250                 SetScrollX(window, ImFloor(window->Scroll.x + ((move_dir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
9251             if (move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down)
9252                 SetScrollY(window, ImFloor(window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
9253         }
9254 
9255         // *Normal* Manual scroll with NavScrollXXX keys
9256         // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
9257         ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f / 10.0f, 10.0f);
9258         if (scroll_dir.x != 0.0f && window->ScrollbarX)
9259             SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
9260         if (scroll_dir.y != 0.0f)
9261             SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
9262     }
9263 
9264     // [DEBUG]
9265     g.NavScoringCount = 0;
9266 #if IMGUI_DEBUG_NAV_RECTS
9267     if (g.NavWindow)
9268     {
9269         ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow);
9270         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]
9271         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); }
9272     }
9273 #endif
9274 }
9275 
NavUpdateInitResult()9276 static void ImGui::NavUpdateInitResult()
9277 {
9278     // 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)
9279     ImGuiContext& g = *GImGui;
9280     if (!g.NavWindow)
9281         return;
9282 
9283     // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
9284     // FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently.
9285     IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name);
9286     SetNavID(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel);
9287     if (g.NavInitRequestFromMove)
9288     {
9289         g.NavDisableHighlight = false;
9290         g.NavDisableMouseHover = g.NavMousePosDirty = true;
9291     }
9292 }
9293 
NavUpdateCreateMoveRequest()9294 void ImGui::NavUpdateCreateMoveRequest()
9295 {
9296     ImGuiContext& g = *GImGui;
9297     ImGuiIO& io = g.IO;
9298     ImGuiWindow* window = g.NavWindow;
9299 
9300     if (g.NavMoveForwardToNextFrame && window != NULL)
9301     {
9302         // 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)
9303         // (preserve most state, which were already set by the NavMoveRequestForward() function)
9304         IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
9305         IM_ASSERT(g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded);
9306         IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir);
9307         g.NavMoveForwardToNextFrame = false;
9308     }
9309     else
9310     {
9311         // Initiate directional inputs request
9312         g.NavMoveDir = ImGuiDir_None;
9313         g.NavMoveFlags = ImGuiNavMoveFlags_None;
9314         if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs))
9315         {
9316             const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat;
9317             if (!IsActiveIdUsingNavDir(ImGuiDir_Left)  && (IsNavInputTest(ImGuiNavInput_DpadLeft,  read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_,  read_mode))) { g.NavMoveDir = ImGuiDir_Left; }
9318             if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; }
9319             if (!IsActiveIdUsingNavDir(ImGuiDir_Up)    && (IsNavInputTest(ImGuiNavInput_DpadUp,    read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_,    read_mode))) { g.NavMoveDir = ImGuiDir_Up; }
9320             if (!IsActiveIdUsingNavDir(ImGuiDir_Down)  && (IsNavInputTest(ImGuiNavInput_DpadDown,  read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_,  read_mode))) { g.NavMoveDir = ImGuiDir_Down; }
9321         }
9322         g.NavMoveClipDir = g.NavMoveDir;
9323     }
9324 
9325     // Update PageUp/PageDown/Home/End scroll
9326     // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
9327     const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
9328     float scoring_rect_offset_y = 0.0f;
9329     if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active)
9330         scoring_rect_offset_y = NavUpdatePageUpPageDown();
9331 
9332     // [DEBUG] Always send a request
9333 #if IMGUI_DEBUG_NAV_SCORING
9334     if (io.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
9335         g.NavMoveDirForDebug = (ImGuiDir)((g.NavMoveDirForDebug + 1) & 3);
9336     if (io.KeyCtrl && g.NavMoveDir == ImGuiDir_None)
9337     {
9338         g.NavMoveDir = g.NavMoveDirForDebug;
9339         g.NavMoveFlags |= ImGuiNavMoveFlags_DebugNoResult;
9340     }
9341 #endif
9342 
9343     // If we initiate a movement request and have no current NavId, we initiate a InitDefaultRequest that will be used as a fallback if the direction fails to find a match
9344     // FIXME: Would be nice to call a single function to initiate a new request
9345     if (g.NavMoveDir != ImGuiDir_None)
9346     {
9347         IM_ASSERT(window != NULL);
9348         g.NavMoveSubmitted = g.NavMoveScoringItems = true;
9349         g.NavMoveKeyMods = io.KeyMods;
9350         g.NavMoveDirForDebug = g.NavMoveDir;
9351         g.NavMoveResultLocal.Clear();
9352         g.NavMoveResultLocalVisible.Clear();
9353         g.NavMoveResultOther.Clear();
9354     }
9355 
9356     // Moving with no reference triggers a init request
9357     if (g.NavMoveSubmitted && g.NavId == 0)
9358     {
9359         IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer);
9360         g.NavInitRequest = g.NavInitRequestFromMove = true;
9361         g.NavInitResultId = 0;
9362         g.NavDisableHighlight = false;
9363     }
9364 
9365     // When using gamepad, we project the reference nav bounding box into window visible area.
9366     // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad every movements are relative
9367     // (can't focus a visible object like we can with the mouse).
9368     if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL)
9369     {
9370         ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1));
9371         if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
9372         {
9373             IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel\n");
9374             float pad = window->CalcFontSize() * 0.5f;
9375             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
9376             window->NavRectRel[g.NavLayer].ClipWithFull(window_rect_rel);
9377             g.NavId = g.NavFocusScopeId = 0;
9378         }
9379     }
9380 
9381     // 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)
9382     ImRect scoring_rect;
9383     if (window != NULL)
9384     {
9385         ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);
9386         scoring_rect = ImRect(window->Pos + nav_rect_rel.Min, window->Pos + nav_rect_rel.Max);
9387         scoring_rect.TranslateY(scoring_rect_offset_y);
9388         scoring_rect.Min.x = ImMin(scoring_rect.Min.x + 1.0f, scoring_rect.Max.x);
9389         scoring_rect.Max.x = scoring_rect.Min.x;
9390         IM_ASSERT(!scoring_rect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem().
9391         //GetForegroundDrawList()->AddRect(scoring_rect.Min, scoring_rect.Max, IM_COL32(255,200,0,255)); // [DEBUG]
9392     }
9393     g.NavScoringRect = scoring_rect;
9394 }
9395 
9396 // Apply result from previous frame navigation directional move request. Always called from NavUpdate()
NavMoveRequestApplyResult()9397 void ImGui::NavMoveRequestApplyResult()
9398 {
9399     ImGuiContext& g = *GImGui;
9400 
9401     if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
9402     {
9403         // 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)
9404         if (g.NavId != 0)
9405         {
9406             g.NavDisableHighlight = false;
9407             g.NavDisableMouseHover = true;
9408         }
9409         return;
9410     }
9411 
9412     // Select which result to use
9413     ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
9414 
9415     // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
9416     if (g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
9417         if (g.NavMoveResultLocalVisible.ID != 0 && g.NavMoveResultLocalVisible.ID != g.NavId)
9418             result = &g.NavMoveResultLocalVisible;
9419 
9420     // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
9421     if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
9422         if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
9423             result = &g.NavMoveResultOther;
9424     IM_ASSERT(g.NavWindow && result->Window);
9425 
9426     // Scroll to keep newly navigated item fully into view.
9427     if (g.NavLayer == ImGuiNavLayer_Main)
9428     {
9429         ImVec2 delta_scroll;
9430         if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdge)
9431         {
9432             float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
9433             delta_scroll.y = result->Window->Scroll.y - scroll_target;
9434             SetScrollY(result->Window, scroll_target);
9435         }
9436         else
9437         {
9438             ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos);
9439             delta_scroll = ScrollToBringRectIntoView(result->Window, rect_abs);
9440         }
9441 
9442         // Offset our result position so mouse position can be applied immediately after in NavUpdate()
9443         result->RectRel.TranslateX(-delta_scroll.x);
9444         result->RectRel.TranslateY(-delta_scroll.y);
9445     }
9446 
9447     ClearActiveID();
9448     g.NavWindow = result->Window;
9449     if (g.NavId != result->ID)
9450     {
9451         // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
9452         g.NavJustMovedToId = result->ID;
9453         g.NavJustMovedToFocusScopeId = result->FocusScopeId;
9454         g.NavJustMovedToKeyMods = g.NavMoveKeyMods;
9455     }
9456     IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
9457     SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
9458     g.NavDisableHighlight = false;
9459     g.NavDisableMouseHover = g.NavMousePosDirty = true;
9460 }
9461 
9462 // Process NavCancel input (to close a popup, get back to parent, clear focus)
9463 // FIXME: In order to support e.g. Escape to clear a selection we'll need:
9464 // - either to store the equivalent of ActiveIdUsingKeyInputMask for a FocusScope and test for it.
9465 // - either to move most/all of those tests to the epilogue/end functions of the scope they are dealing with (e.g. exit child window in EndChild()) or in EndFrame(), to allow an earlier intercept
NavUpdateCancelRequest()9466 static void ImGui::NavUpdateCancelRequest()
9467 {
9468     ImGuiContext& g = *GImGui;
9469     if (!IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
9470         return;
9471 
9472     IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n");
9473     if (g.ActiveId != 0)
9474     {
9475         if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel))
9476             ClearActiveID();
9477     }
9478     else if (g.NavLayer != ImGuiNavLayer_Main)
9479     {
9480         // Leave the "menu" layer
9481         NavRestoreLayer(ImGuiNavLayer_Main);
9482     }
9483     else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
9484     {
9485         // Exit child window
9486         ImGuiWindow* child_window = g.NavWindow;
9487         ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
9488         IM_ASSERT(child_window->ChildId != 0);
9489         ImRect child_rect = child_window->Rect();
9490         FocusWindow(parent_window);
9491         SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, ImRect(child_rect.Min - parent_window->Pos, child_rect.Max - parent_window->Pos));
9492     }
9493     else if (g.OpenPopupStack.Size > 0)
9494     {
9495         // Close open popup/menu
9496         if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
9497             ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
9498     }
9499     else
9500     {
9501         // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
9502         if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
9503             g.NavWindow->NavLastIds[0] = 0;
9504         g.NavId = g.NavFocusScopeId = 0;
9505     }
9506 }
9507 
9508 // Handle PageUp/PageDown/Home/End keys
9509 // Called from NavUpdateCreateMoveRequest() which will use our output to create a move request
9510 // FIXME-NAV: how to get Home/End to aim at the beginning/end of a 2D grid?
NavUpdatePageUpPageDown()9511 static float ImGui::NavUpdatePageUpPageDown()
9512 {
9513     ImGuiContext& g = *GImGui;
9514     ImGuiIO& io = g.IO;
9515 
9516     ImGuiWindow* window = g.NavWindow;
9517     if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != ImGuiNavLayer_Main)
9518         return 0.0f;
9519 
9520     const bool page_up_held = IsKeyDown(io.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp);
9521     const bool page_down_held = IsKeyDown(io.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown);
9522     const bool home_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home);
9523     const bool end_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End);
9524     if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out
9525         return 0.0f;
9526 
9527     if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll)
9528     {
9529         // Fallback manual-scroll when window has no navigable item
9530         if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
9531             SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
9532         else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
9533             SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
9534         else if (home_pressed)
9535             SetScrollY(window, 0.0f);
9536         else if (end_pressed)
9537             SetScrollY(window, window->ScrollMax.y);
9538     }
9539     else
9540     {
9541         ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
9542         const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
9543         float nav_scoring_rect_offset_y = 0.0f;
9544         if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
9545         {
9546             nav_scoring_rect_offset_y = -page_offset_y;
9547             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)
9548             g.NavMoveClipDir = ImGuiDir_Up;
9549             g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
9550         }
9551         else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
9552         {
9553             nav_scoring_rect_offset_y = +page_offset_y;
9554             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)
9555             g.NavMoveClipDir = ImGuiDir_Down;
9556             g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
9557         }
9558         else if (home_pressed)
9559         {
9560             // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
9561             // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result.
9562             // Preserve current horizontal position if we have any.
9563             nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y;
9564             if (nav_rect_rel.IsInverted())
9565                 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
9566             g.NavMoveDir = ImGuiDir_Down;
9567             g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
9568             // FIXME-NAV: MoveClipDir left to _None, intentional?
9569         }
9570         else if (end_pressed)
9571         {
9572             nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y;
9573             if (nav_rect_rel.IsInverted())
9574                 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
9575             g.NavMoveDir = ImGuiDir_Up;
9576             g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
9577             // FIXME-NAV: MoveClipDir left to _None, intentional?
9578         }
9579         return nav_scoring_rect_offset_y;
9580     }
9581     return 0.0f;
9582 }
9583 
NavEndFrame()9584 static void ImGui::NavEndFrame()
9585 {
9586     ImGuiContext& g = *GImGui;
9587 
9588     // Show CTRL+TAB list window
9589     if (g.NavWindowingTarget != NULL)
9590         NavUpdateWindowingOverlay();
9591 
9592     // Perform wrap-around in menus
9593     // FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame.
9594     ImGuiWindow* window = g.NavWindow;
9595     const ImGuiNavMoveFlags move_flags = g.NavMoveFlags;
9596     const ImGuiNavMoveFlags wanted_flags = ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY;
9597     if (window && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & wanted_flags) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0)
9598     {
9599         bool do_forward = false;
9600         ImRect bb_rel = window->NavRectRel[g.NavLayer];
9601         ImGuiDir clip_dir = g.NavMoveDir;
9602         if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
9603         {
9604             bb_rel.Min.x = bb_rel.Max.x =
9605                 ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x;
9606             if (move_flags & ImGuiNavMoveFlags_WrapX)
9607             {
9608                 bb_rel.TranslateY(-bb_rel.GetHeight());
9609                 clip_dir = ImGuiDir_Up;
9610             }
9611             do_forward = true;
9612         }
9613         if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
9614         {
9615             bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
9616             if (move_flags & ImGuiNavMoveFlags_WrapX)
9617             {
9618                 bb_rel.TranslateY(+bb_rel.GetHeight());
9619                 clip_dir = ImGuiDir_Down;
9620             }
9621             do_forward = true;
9622         }
9623         if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
9624         {
9625             bb_rel.Min.y = bb_rel.Max.y =
9626                 ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y;
9627             if (move_flags & ImGuiNavMoveFlags_WrapY)
9628             {
9629                 bb_rel.TranslateX(-bb_rel.GetWidth());
9630                 clip_dir = ImGuiDir_Left;
9631             }
9632             do_forward = true;
9633         }
9634         if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
9635         {
9636             bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
9637             if (move_flags & ImGuiNavMoveFlags_WrapY)
9638             {
9639                 bb_rel.TranslateX(+bb_rel.GetWidth());
9640                 clip_dir = ImGuiDir_Right;
9641             }
9642             do_forward = true;
9643         }
9644         if (do_forward)
9645         {
9646             window->NavRectRel[g.NavLayer] = bb_rel;
9647             NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags);
9648         }
9649     }
9650 }
9651 
FindWindowFocusIndex(ImGuiWindow * window)9652 static int ImGui::FindWindowFocusIndex(ImGuiWindow* window)
9653 {
9654     ImGuiContext& g = *GImGui;
9655     IM_UNUSED(g);
9656     int order = window->FocusOrder;
9657     IM_ASSERT(g.WindowsFocusOrder[order] == window);
9658     return order;
9659 }
9660 
FindWindowNavFocusable(int i_start,int i_stop,int dir)9661 static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
9662 {
9663     ImGuiContext& g = *GImGui;
9664     for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
9665         if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
9666             return g.WindowsFocusOrder[i];
9667     return NULL;
9668 }
9669 
NavUpdateWindowingHighlightWindow(int focus_change_dir)9670 static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
9671 {
9672     ImGuiContext& g = *GImGui;
9673     IM_ASSERT(g.NavWindowingTarget);
9674     if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
9675         return;
9676 
9677     const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget);
9678     ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
9679     if (!window_target)
9680         window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
9681     if (window_target) // Don't reset windowing target if there's a single window in the list
9682         g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
9683     g.NavWindowingToggleLayer = false;
9684 }
9685 
9686 // Windowing management mode
9687 // Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer)
9688 // Gamepad:  Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)
NavUpdateWindowing()9689 static void ImGui::NavUpdateWindowing()
9690 {
9691     ImGuiContext& g = *GImGui;
9692     ImGuiIO& io = g.IO;
9693 
9694     ImGuiWindow* apply_focus_window = NULL;
9695     bool apply_toggle_layer = false;
9696 
9697     ImGuiWindow* modal_window = GetTopMostPopupModal();
9698     bool allow_windowing = (modal_window == NULL);
9699     if (!allow_windowing)
9700         g.NavWindowingTarget = NULL;
9701 
9702     // Fade out
9703     if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
9704     {
9705         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - io.DeltaTime * 10.0f, 0.0f);
9706         if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
9707             g.NavWindowingTargetAnim = NULL;
9708     }
9709 
9710     // Start CTRL-TAB or Square+L/R window selection
9711     bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
9712     bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && io.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
9713     if (start_windowing_with_gamepad || start_windowing_with_keyboard)
9714         if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
9715         {
9716             g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow;
9717             g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
9718             g.NavWindowingToggleLayer = start_windowing_with_gamepad ? true : false; // Gamepad starts toggling layer
9719             g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad;
9720         }
9721 
9722     // Gamepad update
9723     g.NavWindowingTimer += io.DeltaTime;
9724     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Gamepad)
9725     {
9726         // 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
9727         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
9728 
9729         // Select window to focus
9730         const int focus_change_dir = (int)IsNavInputTest(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputTest(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow);
9731         if (focus_change_dir != 0)
9732         {
9733             NavUpdateWindowingHighlightWindow(focus_change_dir);
9734             g.NavWindowingHighlightAlpha = 1.0f;
9735         }
9736 
9737         // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)
9738         if (!IsNavInputDown(ImGuiNavInput_Menu))
9739         {
9740             g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
9741             if (g.NavWindowingToggleLayer && g.NavWindow)
9742                 apply_toggle_layer = true;
9743             else if (!g.NavWindowingToggleLayer)
9744                 apply_focus_window = g.NavWindowingTarget;
9745             g.NavWindowingTarget = NULL;
9746         }
9747     }
9748 
9749     // Keyboard: Focus
9750     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Keyboard)
9751     {
9752         // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
9753         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
9754         if (IsKeyPressedMap(ImGuiKey_Tab, true))
9755             NavUpdateWindowingHighlightWindow(io.KeyShift ? +1 : -1);
9756         if (!io.KeyCtrl)
9757             apply_focus_window = g.NavWindowingTarget;
9758     }
9759 
9760     // Keyboard: Press and Release ALT to toggle menu layer
9761     // - Testing that only Alt is tested prevents Alt+Shift or AltGR from toggling menu layer.
9762     // - AltGR is normally Alt+Ctrl but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl). But even on keyboards without AltGR we don't want Alt+Ctrl to open menu anyway.
9763     if (io.KeyMods == ImGuiKeyModFlags_Alt && (io.KeyModsPrev & ImGuiKeyModFlags_Alt) == 0)
9764     {
9765         g.NavWindowingToggleLayer = true;
9766         g.NavInputSource = ImGuiInputSource_Keyboard;
9767     }
9768     if (g.NavWindowingToggleLayer && g.NavInputSource == ImGuiInputSource_Keyboard)
9769     {
9770         // We cancel toggling nav layer when any text has been typed (generally while holding Alt). (See #370)
9771         // We cancel toggling nav layer when other modifiers are pressed. (See #4439)
9772         if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper)
9773             g.NavWindowingToggleLayer = false;
9774 
9775         // Apply layer toggle on release
9776         // Important: we don't assume that Alt was previously held in order to handle loss of focus when backend calls io.AddFocusEvent(false)
9777         // Important: as before version <18314 we lacked an explicit IO event for focus gain/loss, we also compare mouse validity to detect old backends clearing mouse pos on focus loss.
9778         if (!(io.KeyMods & ImGuiKeyModFlags_Alt) && (io.KeyModsPrev & ImGuiKeyModFlags_Alt) && g.NavWindowingToggleLayer)
9779             if (g.ActiveId == 0 || g.ActiveIdAllowOverlap)
9780                 if (IsMousePosValid(&io.MousePos) == IsMousePosValid(&io.MousePosPrev))
9781                     apply_toggle_layer = true;
9782         if (!io.KeyAlt)
9783             g.NavWindowingToggleLayer = false;
9784     }
9785 
9786     // Move window
9787     if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
9788     {
9789         ImVec2 move_delta;
9790         if (g.NavInputSource == ImGuiInputSource_Keyboard && !io.KeyShift)
9791             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
9792         if (g.NavInputSource == ImGuiInputSource_Gamepad)
9793             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down);
9794         if (move_delta.x != 0.0f || move_delta.y != 0.0f)
9795         {
9796             const float NAV_MOVE_SPEED = 800.0f;
9797             const float move_speed = ImFloor(NAV_MOVE_SPEED * io.DeltaTime * ImMin(io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y)); // FIXME: Doesn't handle variable framerate very well
9798             ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow;
9799             SetWindowPos(moving_window, moving_window->Pos + move_delta * move_speed, ImGuiCond_Always);
9800             MarkIniSettingsDirty(moving_window);
9801             g.NavDisableMouseHover = true;
9802         }
9803     }
9804 
9805     // Apply final focus
9806     if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
9807     {
9808         ClearActiveID();
9809         g.NavDisableHighlight = false;
9810         g.NavDisableMouseHover = true;
9811         apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
9812         ClosePopupsOverWindow(apply_focus_window, false);
9813         FocusWindow(apply_focus_window);
9814         if (apply_focus_window->NavLastIds[0] == 0)
9815             NavInitWindow(apply_focus_window, false);
9816 
9817         // If the window has ONLY a menu layer (no main layer), select it directly
9818         // Use NavLayersActiveMaskNext since windows didn't have a chance to be Begin()-ed on this frame,
9819         // so CTRL+Tab where the keys are only held for 1 frame will be able to use correct layers mask since
9820         // the target window as already been previewed once.
9821         // FIXME-NAV: This should be done in NavInit.. or in FocusWindow... However in both of those cases,
9822         // we won't have a guarantee that windows has been visible before and therefore NavLayersActiveMask*
9823         // won't be valid.
9824         if (apply_focus_window->DC.NavLayersActiveMaskNext == (1 << ImGuiNavLayer_Menu))
9825             g.NavLayer = ImGuiNavLayer_Menu;
9826     }
9827     if (apply_focus_window)
9828         g.NavWindowingTarget = NULL;
9829 
9830     // Apply menu/layer toggle
9831     if (apply_toggle_layer && g.NavWindow)
9832     {
9833         ClearActiveID();
9834 
9835         // Move to parent menu if necessary
9836         ImGuiWindow* new_nav_window = g.NavWindow;
9837         while (new_nav_window->ParentWindow
9838             && (new_nav_window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0
9839             && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
9840             && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
9841             new_nav_window = new_nav_window->ParentWindow;
9842         if (new_nav_window != g.NavWindow)
9843         {
9844             ImGuiWindow* old_nav_window = g.NavWindow;
9845             FocusWindow(new_nav_window);
9846             new_nav_window->NavLastChildNavWindow = old_nav_window;
9847         }
9848         g.NavDisableHighlight = false;
9849         g.NavDisableMouseHover = true;
9850 
9851         // Reinitialize navigation when entering menu bar with the Alt key.
9852         const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;
9853         if (new_nav_layer == ImGuiNavLayer_Menu)
9854             g.NavWindow->NavLastIds[new_nav_layer] = 0;
9855         NavRestoreLayer(new_nav_layer);
9856     }
9857 }
9858 
9859 // Window has already passed the IsWindowNavFocusable()
GetFallbackWindowNameForWindowingList(ImGuiWindow * window)9860 static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
9861 {
9862     if (window->Flags & ImGuiWindowFlags_Popup)
9863         return "(Popup)";
9864     if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
9865         return "(Main menu bar)";
9866     return "(Untitled)";
9867 }
9868 
9869 // Overlay displayed when using CTRL+TAB. Called by EndFrame().
NavUpdateWindowingOverlay()9870 void ImGui::NavUpdateWindowingOverlay()
9871 {
9872     ImGuiContext& g = *GImGui;
9873     IM_ASSERT(g.NavWindowingTarget != NULL);
9874 
9875     if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
9876         return;
9877 
9878     if (g.NavWindowingListWindow == NULL)
9879         g.NavWindowingListWindow = FindWindowByName("###NavWindowingList");
9880     const ImGuiViewport* viewport = GetMainViewport();
9881     SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
9882     SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
9883     PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
9884     Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
9885     for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
9886     {
9887         ImGuiWindow* window = g.WindowsFocusOrder[n];
9888         IM_ASSERT(window != NULL); // Fix static analyzers
9889         if (!IsWindowNavFocusable(window))
9890             continue;
9891         const char* label = window->Name;
9892         if (label == FindRenderedTextEnd(label))
9893             label = GetFallbackWindowNameForWindowingList(window);
9894         Selectable(label, g.NavWindowingTarget == window);
9895     }
9896     End();
9897     PopStyleVar();
9898 }
9899 
9900 
9901 //-----------------------------------------------------------------------------
9902 // [SECTION] DRAG AND DROP
9903 //-----------------------------------------------------------------------------
9904 
ClearDragDrop()9905 void ImGui::ClearDragDrop()
9906 {
9907     ImGuiContext& g = *GImGui;
9908     g.DragDropActive = false;
9909     g.DragDropPayload.Clear();
9910     g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
9911     g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
9912     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
9913     g.DragDropAcceptFrameCount = -1;
9914 
9915     g.DragDropPayloadBufHeap.clear();
9916     memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
9917 }
9918 
9919 // When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
9920 // If the item has an identifier:
9921 // - This assume/require the item to be activated (typically via ButtonBehavior).
9922 // - Therefore if you want to use this with a mouse button other than left mouse button, it is up to the item itself to activate with another button.
9923 // - We then pull and use the mouse button that was used to activate the item and use it to carry on the drag.
9924 // If the item has no identifier:
9925 // - Currently always assume left mouse button.
BeginDragDropSource(ImGuiDragDropFlags flags)9926 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
9927 {
9928     ImGuiContext& g = *GImGui;
9929     ImGuiWindow* window = g.CurrentWindow;
9930 
9931     // FIXME-DRAGDROP: While in the common-most "drag from non-zero active id" case we can tell the mouse button,
9932     // in both SourceExtern and id==0 cases we may requires something else (explicit flags or some heuristic).
9933     ImGuiMouseButton mouse_button = ImGuiMouseButton_Left;
9934 
9935     bool source_drag_active = false;
9936     ImGuiID source_id = 0;
9937     ImGuiID source_parent_id = 0;
9938     if (!(flags & ImGuiDragDropFlags_SourceExtern))
9939     {
9940         source_id = g.LastItemData.ID;
9941         if (source_id != 0)
9942         {
9943             // Common path: items with ID
9944             if (g.ActiveId != source_id)
9945                 return false;
9946             if (g.ActiveIdMouseButton != -1)
9947                 mouse_button = g.ActiveIdMouseButton;
9948             if (g.IO.MouseDown[mouse_button] == false)
9949                 return false;
9950             g.ActiveIdAllowOverlap = false;
9951         }
9952         else
9953         {
9954             // Uncommon path: items without ID
9955             if (g.IO.MouseDown[mouse_button] == false)
9956                 return false;
9957 
9958             // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
9959             // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
9960             if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
9961             {
9962                 IM_ASSERT(0);
9963                 return false;
9964             }
9965 
9966             // Early out
9967             if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
9968                 return false;
9969 
9970             // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
9971             // We build a throwaway ID based on current ID stack + relative AABB of items in window.
9972             // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
9973             // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
9974             // Rely on keeping other window->LastItemXXX fields intact.
9975             source_id = g.LastItemData.ID = window->GetIDFromRectangle(g.LastItemData.Rect);
9976             bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id);
9977             if (is_hovered && g.IO.MouseClicked[mouse_button])
9978             {
9979                 SetActiveID(source_id, window);
9980                 FocusWindow(window);
9981             }
9982             if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
9983                 g.ActiveIdAllowOverlap = is_hovered;
9984         }
9985         if (g.ActiveId != source_id)
9986             return false;
9987         source_parent_id = window->IDStack.back();
9988         source_drag_active = IsMouseDragging(mouse_button);
9989 
9990         // Disable navigation and key inputs while dragging + cancel existing request if any
9991         SetActiveIdUsingNavAndKeys();
9992     }
9993     else
9994     {
9995         window = NULL;
9996         source_id = ImHashStr("#SourceExtern");
9997         source_drag_active = true;
9998     }
9999 
10000     if (source_drag_active)
10001     {
10002         if (!g.DragDropActive)
10003         {
10004             IM_ASSERT(source_id != 0);
10005             ClearDragDrop();
10006             ImGuiPayload& payload = g.DragDropPayload;
10007             payload.SourceId = source_id;
10008             payload.SourceParentId = source_parent_id;
10009             g.DragDropActive = true;
10010             g.DragDropSourceFlags = flags;
10011             g.DragDropMouseButton = mouse_button;
10012             if (payload.SourceId == g.ActiveId)
10013                 g.ActiveIdNoClearOnFocusLoss = true;
10014         }
10015         g.DragDropSourceFrameCount = g.FrameCount;
10016         g.DragDropWithinSource = true;
10017 
10018         if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
10019         {
10020             // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
10021             // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
10022             BeginTooltip();
10023             if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
10024             {
10025                 ImGuiWindow* tooltip_window = g.CurrentWindow;
10026                 tooltip_window->Hidden = tooltip_window->SkipItems = true;
10027                 tooltip_window->HiddenFramesCanSkipItems = 1;
10028             }
10029         }
10030 
10031         if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
10032             g.LastItemData.StatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
10033 
10034         return true;
10035     }
10036     return false;
10037 }
10038 
EndDragDropSource()10039 void ImGui::EndDragDropSource()
10040 {
10041     ImGuiContext& g = *GImGui;
10042     IM_ASSERT(g.DragDropActive);
10043     IM_ASSERT(g.DragDropWithinSource && "Not after a BeginDragDropSource()?");
10044 
10045     if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
10046         EndTooltip();
10047 
10048     // Discard the drag if have not called SetDragDropPayload()
10049     if (g.DragDropPayload.DataFrameCount == -1)
10050         ClearDragDrop();
10051     g.DragDropWithinSource = false;
10052 }
10053 
10054 // 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)10055 bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
10056 {
10057     ImGuiContext& g = *GImGui;
10058     ImGuiPayload& payload = g.DragDropPayload;
10059     if (cond == 0)
10060         cond = ImGuiCond_Always;
10061 
10062     IM_ASSERT(type != NULL);
10063     IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
10064     IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
10065     IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
10066     IM_ASSERT(payload.SourceId != 0);                               // Not called between BeginDragDropSource() and EndDragDropSource()
10067 
10068     if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
10069     {
10070         // Copy payload
10071         ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
10072         g.DragDropPayloadBufHeap.resize(0);
10073         if (data_size > sizeof(g.DragDropPayloadBufLocal))
10074         {
10075             // Store in heap
10076             g.DragDropPayloadBufHeap.resize((int)data_size);
10077             payload.Data = g.DragDropPayloadBufHeap.Data;
10078             memcpy(payload.Data, data, data_size);
10079         }
10080         else if (data_size > 0)
10081         {
10082             // Store locally
10083             memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
10084             payload.Data = g.DragDropPayloadBufLocal;
10085             memcpy(payload.Data, data, data_size);
10086         }
10087         else
10088         {
10089             payload.Data = NULL;
10090         }
10091         payload.DataSize = (int)data_size;
10092     }
10093     payload.DataFrameCount = g.FrameCount;
10094 
10095     return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
10096 }
10097 
BeginDragDropTargetCustom(const ImRect & bb,ImGuiID id)10098 bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
10099 {
10100     ImGuiContext& g = *GImGui;
10101     if (!g.DragDropActive)
10102         return false;
10103 
10104     ImGuiWindow* window = g.CurrentWindow;
10105     ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
10106     if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow)
10107         return false;
10108     IM_ASSERT(id != 0);
10109     if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
10110         return false;
10111     if (window->SkipItems)
10112         return false;
10113 
10114     IM_ASSERT(g.DragDropWithinTarget == false);
10115     g.DragDropTargetRect = bb;
10116     g.DragDropTargetId = id;
10117     g.DragDropWithinTarget = true;
10118     return true;
10119 }
10120 
10121 // We don't use BeginDragDropTargetCustom() and duplicate its code because:
10122 // 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
10123 // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
10124 // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
BeginDragDropTarget()10125 bool ImGui::BeginDragDropTarget()
10126 {
10127     ImGuiContext& g = *GImGui;
10128     if (!g.DragDropActive)
10129         return false;
10130 
10131     ImGuiWindow* window = g.CurrentWindow;
10132     if (!(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect))
10133         return false;
10134     ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
10135     if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow)
10136         return false;
10137 
10138     const ImRect& display_rect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? g.LastItemData.DisplayRect : g.LastItemData.Rect;
10139     ImGuiID id = g.LastItemData.ID;
10140     if (id == 0)
10141         id = window->GetIDFromRectangle(display_rect);
10142     if (g.DragDropPayload.SourceId == id)
10143         return false;
10144 
10145     IM_ASSERT(g.DragDropWithinTarget == false);
10146     g.DragDropTargetRect = display_rect;
10147     g.DragDropTargetId = id;
10148     g.DragDropWithinTarget = true;
10149     return true;
10150 }
10151 
IsDragDropPayloadBeingAccepted()10152 bool ImGui::IsDragDropPayloadBeingAccepted()
10153 {
10154     ImGuiContext& g = *GImGui;
10155     return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
10156 }
10157 
AcceptDragDropPayload(const char * type,ImGuiDragDropFlags flags)10158 const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
10159 {
10160     ImGuiContext& g = *GImGui;
10161     ImGuiWindow* window = g.CurrentWindow;
10162     ImGuiPayload& payload = g.DragDropPayload;
10163     IM_ASSERT(g.DragDropActive);                        // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
10164     IM_ASSERT(payload.DataFrameCount != -1);            // Forgot to call EndDragDropTarget() ?
10165     if (type != NULL && !payload.IsDataType(type))
10166         return NULL;
10167 
10168     // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
10169     // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
10170     const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
10171     ImRect r = g.DragDropTargetRect;
10172     float r_surface = r.GetWidth() * r.GetHeight();
10173     if (r_surface <= g.DragDropAcceptIdCurrRectSurface)
10174     {
10175         g.DragDropAcceptFlags = flags;
10176         g.DragDropAcceptIdCurr = g.DragDropTargetId;
10177         g.DragDropAcceptIdCurrRectSurface = r_surface;
10178     }
10179 
10180     // Render default drop visuals
10181     // FIXME-DRAGDROP: Settle on a proper default visuals for drop target.
10182     payload.Preview = was_accepted_previously;
10183     flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
10184     if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
10185         window->DrawList->AddRect(r.Min - ImVec2(3.5f,3.5f), r.Max + ImVec2(3.5f, 3.5f), GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f);
10186 
10187     g.DragDropAcceptFrameCount = g.FrameCount;
10188     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()
10189     if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
10190         return NULL;
10191 
10192     return &payload;
10193 }
10194 
GetDragDropPayload()10195 const ImGuiPayload* ImGui::GetDragDropPayload()
10196 {
10197     ImGuiContext& g = *GImGui;
10198     return g.DragDropActive ? &g.DragDropPayload : NULL;
10199 }
10200 
10201 // We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
EndDragDropTarget()10202 void ImGui::EndDragDropTarget()
10203 {
10204     ImGuiContext& g = *GImGui;
10205     IM_ASSERT(g.DragDropActive);
10206     IM_ASSERT(g.DragDropWithinTarget);
10207     g.DragDropWithinTarget = false;
10208 }
10209 
10210 //-----------------------------------------------------------------------------
10211 // [SECTION] LOGGING/CAPTURING
10212 //-----------------------------------------------------------------------------
10213 // All text output from the interface can be captured into tty/file/clipboard.
10214 // By default, tree nodes are automatically opened during logging.
10215 //-----------------------------------------------------------------------------
10216 
10217 // Pass text data straight to log (without being displayed)
LogTextV(ImGuiContext & g,const char * fmt,va_list args)10218 static inline void LogTextV(ImGuiContext& g, const char* fmt, va_list args)
10219 {
10220     if (g.LogFile)
10221     {
10222         g.LogBuffer.Buf.resize(0);
10223         g.LogBuffer.appendfv(fmt, args);
10224         ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile);
10225     }
10226     else
10227     {
10228         g.LogBuffer.appendfv(fmt, args);
10229     }
10230 }
10231 
LogText(const char * fmt,...)10232 void ImGui::LogText(const char* fmt, ...)
10233 {
10234     ImGuiContext& g = *GImGui;
10235     if (!g.LogEnabled)
10236         return;
10237 
10238     va_list args;
10239     va_start(args, fmt);
10240     LogTextV(g, fmt, args);
10241     va_end(args);
10242 }
10243 
LogTextV(const char * fmt,va_list args)10244 void ImGui::LogTextV(const char* fmt, va_list args)
10245 {
10246     ImGuiContext& g = *GImGui;
10247     if (!g.LogEnabled)
10248         return;
10249 
10250     LogTextV(g, fmt, args);
10251 }
10252 
10253 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
10254 // We split text into individual lines to add current tree level padding
10255 // FIXME: This code is a little complicated perhaps, considering simplifying the whole system.
LogRenderedText(const ImVec2 * ref_pos,const char * text,const char * text_end)10256 void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
10257 {
10258     ImGuiContext& g = *GImGui;
10259     ImGuiWindow* window = g.CurrentWindow;
10260 
10261     const char* prefix = g.LogNextPrefix;
10262     const char* suffix = g.LogNextSuffix;
10263     g.LogNextPrefix = g.LogNextSuffix = NULL;
10264 
10265     if (!text_end)
10266         text_end = FindRenderedTextEnd(text, text_end);
10267 
10268     const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + g.Style.FramePadding.y + 1);
10269     if (ref_pos)
10270         g.LogLinePosY = ref_pos->y;
10271     if (log_new_line)
10272     {
10273         LogText(IM_NEWLINE);
10274         g.LogLineFirstItem = true;
10275     }
10276 
10277     if (prefix)
10278         LogRenderedText(ref_pos, prefix, prefix + strlen(prefix)); // Calculate end ourself to ensure "##" are included here.
10279 
10280     // Re-adjust padding if we have popped out of our starting depth
10281     if (g.LogDepthRef > window->DC.TreeDepth)
10282         g.LogDepthRef = window->DC.TreeDepth;
10283     const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);
10284 
10285     const char* text_remaining = text;
10286     for (;;)
10287     {
10288         // Split the string. Each new line (after a '\n') is followed by indentation corresponding to the current depth of our log entry.
10289         // We don't add a trailing \n yet to allow a subsequent item on the same line to be captured.
10290         const char* line_start = text_remaining;
10291         const char* line_end = ImStreolRange(line_start, text_end);
10292         const bool is_last_line = (line_end == text_end);
10293         if (line_start != line_end || !is_last_line)
10294         {
10295             const int line_length = (int)(line_end - line_start);
10296             const int indentation = g.LogLineFirstItem ? tree_depth * 4 : 1;
10297             LogText("%*s%.*s", indentation, "", line_length, line_start);
10298             g.LogLineFirstItem = false;
10299             if (*line_end == '\n')
10300             {
10301                 LogText(IM_NEWLINE);
10302                 g.LogLineFirstItem = true;
10303             }
10304         }
10305         if (is_last_line)
10306             break;
10307         text_remaining = line_end + 1;
10308     }
10309 
10310     if (suffix)
10311         LogRenderedText(ref_pos, suffix, suffix + strlen(suffix));
10312 }
10313 
10314 // Start logging/capturing text output
LogBegin(ImGuiLogType type,int auto_open_depth)10315 void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth)
10316 {
10317     ImGuiContext& g = *GImGui;
10318     ImGuiWindow* window = g.CurrentWindow;
10319     IM_ASSERT(g.LogEnabled == false);
10320     IM_ASSERT(g.LogFile == NULL);
10321     IM_ASSERT(g.LogBuffer.empty());
10322     g.LogEnabled = true;
10323     g.LogType = type;
10324     g.LogNextPrefix = g.LogNextSuffix = NULL;
10325     g.LogDepthRef = window->DC.TreeDepth;
10326     g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);
10327     g.LogLinePosY = FLT_MAX;
10328     g.LogLineFirstItem = true;
10329 }
10330 
10331 // Important: doesn't copy underlying data, use carefully (prefix/suffix must be in scope at the time of the next LogRenderedText)
LogSetNextTextDecoration(const char * prefix,const char * suffix)10332 void ImGui::LogSetNextTextDecoration(const char* prefix, const char* suffix)
10333 {
10334     ImGuiContext& g = *GImGui;
10335     g.LogNextPrefix = prefix;
10336     g.LogNextSuffix = suffix;
10337 }
10338 
LogToTTY(int auto_open_depth)10339 void ImGui::LogToTTY(int auto_open_depth)
10340 {
10341     ImGuiContext& g = *GImGui;
10342     if (g.LogEnabled)
10343         return;
10344     IM_UNUSED(auto_open_depth);
10345 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
10346     LogBegin(ImGuiLogType_TTY, auto_open_depth);
10347     g.LogFile = stdout;
10348 #endif
10349 }
10350 
10351 // Start logging/capturing text output to given file
LogToFile(int auto_open_depth,const char * filename)10352 void ImGui::LogToFile(int auto_open_depth, const char* filename)
10353 {
10354     ImGuiContext& g = *GImGui;
10355     if (g.LogEnabled)
10356         return;
10357 
10358     // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still
10359     // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE.
10360     // By opening the file in binary mode "ab" we have consistent output everywhere.
10361     if (!filename)
10362         filename = g.IO.LogFilename;
10363     if (!filename || !filename[0])
10364         return;
10365     ImFileHandle f = ImFileOpen(filename, "ab");
10366     if (!f)
10367     {
10368         IM_ASSERT(0);
10369         return;
10370     }
10371 
10372     LogBegin(ImGuiLogType_File, auto_open_depth);
10373     g.LogFile = f;
10374 }
10375 
10376 // Start logging/capturing text output to clipboard
LogToClipboard(int auto_open_depth)10377 void ImGui::LogToClipboard(int auto_open_depth)
10378 {
10379     ImGuiContext& g = *GImGui;
10380     if (g.LogEnabled)
10381         return;
10382     LogBegin(ImGuiLogType_Clipboard, auto_open_depth);
10383 }
10384 
LogToBuffer(int auto_open_depth)10385 void ImGui::LogToBuffer(int auto_open_depth)
10386 {
10387     ImGuiContext& g = *GImGui;
10388     if (g.LogEnabled)
10389         return;
10390     LogBegin(ImGuiLogType_Buffer, auto_open_depth);
10391 }
10392 
LogFinish()10393 void ImGui::LogFinish()
10394 {
10395     ImGuiContext& g = *GImGui;
10396     if (!g.LogEnabled)
10397         return;
10398 
10399     LogText(IM_NEWLINE);
10400     switch (g.LogType)
10401     {
10402     case ImGuiLogType_TTY:
10403 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
10404         fflush(g.LogFile);
10405 #endif
10406         break;
10407     case ImGuiLogType_File:
10408         ImFileClose(g.LogFile);
10409         break;
10410     case ImGuiLogType_Buffer:
10411         break;
10412     case ImGuiLogType_Clipboard:
10413         if (!g.LogBuffer.empty())
10414             SetClipboardText(g.LogBuffer.begin());
10415         break;
10416     case ImGuiLogType_None:
10417         IM_ASSERT(0);
10418         break;
10419     }
10420 
10421     g.LogEnabled = false;
10422     g.LogType = ImGuiLogType_None;
10423     g.LogFile = NULL;
10424     g.LogBuffer.clear();
10425 }
10426 
10427 // Helper to display logging buttons
10428 // FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!)
LogButtons()10429 void ImGui::LogButtons()
10430 {
10431     ImGuiContext& g = *GImGui;
10432 
10433     PushID("LogButtons");
10434 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
10435     const bool log_to_tty = Button("Log To TTY"); SameLine();
10436 #else
10437     const bool log_to_tty = false;
10438 #endif
10439     const bool log_to_file = Button("Log To File"); SameLine();
10440     const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
10441     PushAllowKeyboardFocus(false);
10442     SetNextItemWidth(80.0f);
10443     SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL);
10444     PopAllowKeyboardFocus();
10445     PopID();
10446 
10447     // Start logging at the end of the function so that the buttons don't appear in the log
10448     if (log_to_tty)
10449         LogToTTY();
10450     if (log_to_file)
10451         LogToFile();
10452     if (log_to_clipboard)
10453         LogToClipboard();
10454 }
10455 
10456 
10457 //-----------------------------------------------------------------------------
10458 // [SECTION] SETTINGS
10459 //-----------------------------------------------------------------------------
10460 // - UpdateSettings() [Internal]
10461 // - MarkIniSettingsDirty() [Internal]
10462 // - CreateNewWindowSettings() [Internal]
10463 // - FindWindowSettings() [Internal]
10464 // - FindOrCreateWindowSettings() [Internal]
10465 // - FindSettingsHandler() [Internal]
10466 // - ClearIniSettings() [Internal]
10467 // - LoadIniSettingsFromDisk()
10468 // - LoadIniSettingsFromMemory()
10469 // - SaveIniSettingsToDisk()
10470 // - SaveIniSettingsToMemory()
10471 // - WindowSettingsHandler_***() [Internal]
10472 //-----------------------------------------------------------------------------
10473 
10474 // Called by NewFrame()
UpdateSettings()10475 void ImGui::UpdateSettings()
10476 {
10477     // Load settings on first frame (if not explicitly loaded manually before)
10478     ImGuiContext& g = *GImGui;
10479     if (!g.SettingsLoaded)
10480     {
10481         IM_ASSERT(g.SettingsWindows.empty());
10482         if (g.IO.IniFilename)
10483             LoadIniSettingsFromDisk(g.IO.IniFilename);
10484         g.SettingsLoaded = true;
10485     }
10486 
10487     // Save settings (with a delay after the last modification, so we don't spam disk too much)
10488     if (g.SettingsDirtyTimer > 0.0f)
10489     {
10490         g.SettingsDirtyTimer -= g.IO.DeltaTime;
10491         if (g.SettingsDirtyTimer <= 0.0f)
10492         {
10493             if (g.IO.IniFilename != NULL)
10494                 SaveIniSettingsToDisk(g.IO.IniFilename);
10495             else
10496                 g.IO.WantSaveIniSettings = true;  // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
10497             g.SettingsDirtyTimer = 0.0f;
10498         }
10499     }
10500 }
10501 
MarkIniSettingsDirty()10502 void ImGui::MarkIniSettingsDirty()
10503 {
10504     ImGuiContext& g = *GImGui;
10505     if (g.SettingsDirtyTimer <= 0.0f)
10506         g.SettingsDirtyTimer = g.IO.IniSavingRate;
10507 }
10508 
MarkIniSettingsDirty(ImGuiWindow * window)10509 void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
10510 {
10511     ImGuiContext& g = *GImGui;
10512     if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
10513         if (g.SettingsDirtyTimer <= 0.0f)
10514             g.SettingsDirtyTimer = g.IO.IniSavingRate;
10515 }
10516 
CreateNewWindowSettings(const char * name)10517 ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
10518 {
10519     ImGuiContext& g = *GImGui;
10520 
10521 #if !IMGUI_DEBUG_INI_SETTINGS
10522     // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
10523     // Preserve the full string when IMGUI_DEBUG_INI_SETTINGS is set to make .ini inspection easier.
10524     if (const char* p = strstr(name, "###"))
10525         name = p;
10526 #endif
10527     const size_t name_len = strlen(name);
10528 
10529     // Allocate chunk
10530     const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1;
10531     ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size);
10532     IM_PLACEMENT_NEW(settings) ImGuiWindowSettings();
10533     settings->ID = ImHashStr(name, name_len);
10534     memcpy(settings->GetName(), name, name_len + 1);   // Store with zero terminator
10535 
10536     return settings;
10537 }
10538 
FindWindowSettings(ImGuiID id)10539 ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
10540 {
10541     ImGuiContext& g = *GImGui;
10542     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
10543         if (settings->ID == id)
10544             return settings;
10545     return NULL;
10546 }
10547 
FindOrCreateWindowSettings(const char * name)10548 ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name)
10549 {
10550     if (ImGuiWindowSettings* settings = FindWindowSettings(ImHashStr(name)))
10551         return settings;
10552     return CreateNewWindowSettings(name);
10553 }
10554 
FindSettingsHandler(const char * type_name)10555 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
10556 {
10557     ImGuiContext& g = *GImGui;
10558     const ImGuiID type_hash = ImHashStr(type_name);
10559     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10560         if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
10561             return &g.SettingsHandlers[handler_n];
10562     return NULL;
10563 }
10564 
ClearIniSettings()10565 void ImGui::ClearIniSettings()
10566 {
10567     ImGuiContext& g = *GImGui;
10568     g.SettingsIniData.clear();
10569     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10570         if (g.SettingsHandlers[handler_n].ClearAllFn)
10571             g.SettingsHandlers[handler_n].ClearAllFn(&g, &g.SettingsHandlers[handler_n]);
10572 }
10573 
LoadIniSettingsFromDisk(const char * ini_filename)10574 void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
10575 {
10576     size_t file_data_size = 0;
10577     char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
10578     if (!file_data)
10579         return;
10580     LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
10581     IM_FREE(file_data);
10582 }
10583 
10584 // Zero-tolerance, no error reporting, cheap .ini parsing
LoadIniSettingsFromMemory(const char * ini_data,size_t ini_size)10585 void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
10586 {
10587     ImGuiContext& g = *GImGui;
10588     IM_ASSERT(g.Initialized);
10589     //IM_ASSERT(!g.WithinFrameScope && "Cannot be called between NewFrame() and EndFrame()");
10590     //IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
10591 
10592     // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
10593     // 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..
10594     if (ini_size == 0)
10595         ini_size = strlen(ini_data);
10596     g.SettingsIniData.Buf.resize((int)ini_size + 1);
10597     char* const buf = g.SettingsIniData.Buf.Data;
10598     char* const buf_end = buf + ini_size;
10599     memcpy(buf, ini_data, ini_size);
10600     buf_end[0] = 0;
10601 
10602     // Call pre-read handlers
10603     // Some types will clear their data (e.g. dock information) some types will allow merge/override (window)
10604     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10605         if (g.SettingsHandlers[handler_n].ReadInitFn)
10606             g.SettingsHandlers[handler_n].ReadInitFn(&g, &g.SettingsHandlers[handler_n]);
10607 
10608     void* entry_data = NULL;
10609     ImGuiSettingsHandler* entry_handler = NULL;
10610 
10611     char* line_end = NULL;
10612     for (char* line = buf; line < buf_end; line = line_end + 1)
10613     {
10614         // Skip new lines markers, then find end of the line
10615         while (*line == '\n' || *line == '\r')
10616             line++;
10617         line_end = line;
10618         while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
10619             line_end++;
10620         line_end[0] = 0;
10621         if (line[0] == ';')
10622             continue;
10623         if (line[0] == '[' && line_end > line && line_end[-1] == ']')
10624         {
10625             // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
10626             line_end[-1] = 0;
10627             const char* name_end = line_end - 1;
10628             const char* type_start = line + 1;
10629             char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']');
10630             const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
10631             if (!type_end || !name_start)
10632                 continue;
10633             *type_end = 0; // Overwrite first ']'
10634             name_start++;  // Skip second '['
10635             entry_handler = FindSettingsHandler(type_start);
10636             entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
10637         }
10638         else if (entry_handler != NULL && entry_data != NULL)
10639         {
10640             // Let type handler parse the line
10641             entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
10642         }
10643     }
10644     g.SettingsLoaded = true;
10645 
10646     // [DEBUG] Restore untouched copy so it can be browsed in Metrics (not strictly necessary)
10647     memcpy(buf, ini_data, ini_size);
10648 
10649     // Call post-read handlers
10650     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10651         if (g.SettingsHandlers[handler_n].ApplyAllFn)
10652             g.SettingsHandlers[handler_n].ApplyAllFn(&g, &g.SettingsHandlers[handler_n]);
10653 }
10654 
SaveIniSettingsToDisk(const char * ini_filename)10655 void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
10656 {
10657     ImGuiContext& g = *GImGui;
10658     g.SettingsDirtyTimer = 0.0f;
10659     if (!ini_filename)
10660         return;
10661 
10662     size_t ini_data_size = 0;
10663     const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
10664     ImFileHandle f = ImFileOpen(ini_filename, "wt");
10665     if (!f)
10666         return;
10667     ImFileWrite(ini_data, sizeof(char), ini_data_size, f);
10668     ImFileClose(f);
10669 }
10670 
10671 // Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
SaveIniSettingsToMemory(size_t * out_size)10672 const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
10673 {
10674     ImGuiContext& g = *GImGui;
10675     g.SettingsDirtyTimer = 0.0f;
10676     g.SettingsIniData.Buf.resize(0);
10677     g.SettingsIniData.Buf.push_back(0);
10678     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10679     {
10680         ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
10681         handler->WriteAllFn(&g, handler, &g.SettingsIniData);
10682     }
10683     if (out_size)
10684         *out_size = (size_t)g.SettingsIniData.size();
10685     return g.SettingsIniData.c_str();
10686 }
10687 
WindowSettingsHandler_ClearAll(ImGuiContext * ctx,ImGuiSettingsHandler *)10688 static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
10689 {
10690     ImGuiContext& g = *ctx;
10691     for (int i = 0; i != g.Windows.Size; i++)
10692         g.Windows[i]->SettingsOffset = -1;
10693     g.SettingsWindows.clear();
10694 }
10695 
WindowSettingsHandler_ReadOpen(ImGuiContext *,ImGuiSettingsHandler *,const char * name)10696 static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
10697 {
10698     ImGuiWindowSettings* settings = ImGui::FindOrCreateWindowSettings(name);
10699     ImGuiID id = settings->ID;
10700     *settings = ImGuiWindowSettings(); // Clear existing if recycling previous entry
10701     settings->ID = id;
10702     settings->WantApply = true;
10703     return (void*)settings;
10704 }
10705 
WindowSettingsHandler_ReadLine(ImGuiContext *,ImGuiSettingsHandler *,void * entry,const char * line)10706 static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
10707 {
10708     ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
10709     int x, y;
10710     int i;
10711     if (sscanf(line, "Pos=%i,%i", &x, &y) == 2)         { settings->Pos = ImVec2ih((short)x, (short)y); }
10712     else if (sscanf(line, "Size=%i,%i", &x, &y) == 2)   { settings->Size = ImVec2ih((short)x, (short)y); }
10713     else if (sscanf(line, "Collapsed=%d", &i) == 1)     { settings->Collapsed = (i != 0); }
10714 }
10715 
10716 // Apply to existing windows (if any)
WindowSettingsHandler_ApplyAll(ImGuiContext * ctx,ImGuiSettingsHandler *)10717 static void WindowSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
10718 {
10719     ImGuiContext& g = *ctx;
10720     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
10721         if (settings->WantApply)
10722         {
10723             if (ImGuiWindow* window = ImGui::FindWindowByID(settings->ID))
10724                 ApplyWindowSettings(window, settings);
10725             settings->WantApply = false;
10726         }
10727 }
10728 
WindowSettingsHandler_WriteAll(ImGuiContext * ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)10729 static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
10730 {
10731     // Gather data from windows that were active during this session
10732     // (if a window wasn't opened in this session we preserve its settings)
10733     ImGuiContext& g = *ctx;
10734     for (int i = 0; i != g.Windows.Size; i++)
10735     {
10736         ImGuiWindow* window = g.Windows[i];
10737         if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
10738             continue;
10739 
10740         ImGuiWindowSettings* settings = (window->SettingsOffset != -1) ? g.SettingsWindows.ptr_from_offset(window->SettingsOffset) : ImGui::FindWindowSettings(window->ID);
10741         if (!settings)
10742         {
10743             settings = ImGui::CreateNewWindowSettings(window->Name);
10744             window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
10745         }
10746         IM_ASSERT(settings->ID == window->ID);
10747         settings->Pos = ImVec2ih(window->Pos);
10748         settings->Size = ImVec2ih(window->SizeFull);
10749 
10750         settings->Collapsed = window->Collapsed;
10751     }
10752 
10753     // Write to text buffer
10754     buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve
10755     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
10756     {
10757         const char* settings_name = settings->GetName();
10758         buf->appendf("[%s][%s]\n", handler->TypeName, settings_name);
10759         buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
10760         buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
10761         buf->appendf("Collapsed=%d\n", settings->Collapsed);
10762         buf->append("\n");
10763     }
10764 }
10765 
10766 
10767 //-----------------------------------------------------------------------------
10768 // [SECTION] VIEWPORTS, PLATFORM WINDOWS
10769 //-----------------------------------------------------------------------------
10770 // - GetMainViewport()
10771 // - UpdateViewportsNewFrame() [Internal]
10772 // (this section is more complete in the 'docking' branch)
10773 //-----------------------------------------------------------------------------
10774 
GetMainViewport()10775 ImGuiViewport* ImGui::GetMainViewport()
10776 {
10777     ImGuiContext& g = *GImGui;
10778     return g.Viewports[0];
10779 }
10780 
10781 // Update viewports and monitor infos
UpdateViewportsNewFrame()10782 static void ImGui::UpdateViewportsNewFrame()
10783 {
10784     ImGuiContext& g = *GImGui;
10785     IM_ASSERT(g.Viewports.Size == 1);
10786 
10787     // Update main viewport with current platform position.
10788     // FIXME-VIEWPORT: Size is driven by backend/user code for backward-compatibility but we should aim to make this more consistent.
10789     ImGuiViewportP* main_viewport = g.Viewports[0];
10790     main_viewport->Flags = ImGuiViewportFlags_IsPlatformWindow | ImGuiViewportFlags_OwnedByApp;
10791     main_viewport->Pos = ImVec2(0.0f, 0.0f);
10792     main_viewport->Size = g.IO.DisplaySize;
10793 
10794     for (int n = 0; n < g.Viewports.Size; n++)
10795     {
10796         ImGuiViewportP* viewport = g.Viewports[n];
10797 
10798         // Lock down space taken by menu bars and status bars, reset the offset for fucntions like BeginMainMenuBar() to alter them again.
10799         viewport->WorkOffsetMin = viewport->BuildWorkOffsetMin;
10800         viewport->WorkOffsetMax = viewport->BuildWorkOffsetMax;
10801         viewport->BuildWorkOffsetMin = viewport->BuildWorkOffsetMax = ImVec2(0.0f, 0.0f);
10802         viewport->UpdateWorkRect();
10803     }
10804 }
10805 
10806 //-----------------------------------------------------------------------------
10807 // [SECTION] DOCKING
10808 //-----------------------------------------------------------------------------
10809 
10810 // (this section is filled in the 'docking' branch)
10811 
10812 
10813 //-----------------------------------------------------------------------------
10814 // [SECTION] PLATFORM DEPENDENT HELPERS
10815 //-----------------------------------------------------------------------------
10816 
10817 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
10818 
10819 #ifdef _MSC_VER
10820 #pragma comment(lib, "user32")
10821 #pragma comment(lib, "kernel32")
10822 #endif
10823 
10824 // Win32 clipboard implementation
10825 // We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown()
GetClipboardTextFn_DefaultImpl(void *)10826 static const char* GetClipboardTextFn_DefaultImpl(void*)
10827 {
10828     ImGuiContext& g = *GImGui;
10829     g.ClipboardHandlerData.clear();
10830     if (!::OpenClipboard(NULL))
10831         return NULL;
10832     HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
10833     if (wbuf_handle == NULL)
10834     {
10835         ::CloseClipboard();
10836         return NULL;
10837     }
10838     if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle))
10839     {
10840         int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL);
10841         g.ClipboardHandlerData.resize(buf_len);
10842         ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL);
10843     }
10844     ::GlobalUnlock(wbuf_handle);
10845     ::CloseClipboard();
10846     return g.ClipboardHandlerData.Data;
10847 }
10848 
SetClipboardTextFn_DefaultImpl(void *,const char * text)10849 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
10850 {
10851     if (!::OpenClipboard(NULL))
10852         return;
10853     const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);
10854     HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR));
10855     if (wbuf_handle == NULL)
10856     {
10857         ::CloseClipboard();
10858         return;
10859     }
10860     WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle);
10861     ::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length);
10862     ::GlobalUnlock(wbuf_handle);
10863     ::EmptyClipboard();
10864     if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
10865         ::GlobalFree(wbuf_handle);
10866     ::CloseClipboard();
10867 }
10868 
10869 #elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS)
10870 
10871 #include <Carbon/Carbon.h>  // Use old API to avoid need for separate .mm file
10872 static PasteboardRef main_clipboard = 0;
10873 
10874 // OSX clipboard implementation
10875 // If you enable this you will need to add '-framework ApplicationServices' to your linker command-line!
SetClipboardTextFn_DefaultImpl(void *,const char * text)10876 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
10877 {
10878     if (!main_clipboard)
10879         PasteboardCreate(kPasteboardClipboard, &main_clipboard);
10880     PasteboardClear(main_clipboard);
10881     CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text));
10882     if (cf_data)
10883     {
10884         PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0);
10885         CFRelease(cf_data);
10886     }
10887 }
10888 
GetClipboardTextFn_DefaultImpl(void *)10889 static const char* GetClipboardTextFn_DefaultImpl(void*)
10890 {
10891     if (!main_clipboard)
10892         PasteboardCreate(kPasteboardClipboard, &main_clipboard);
10893     PasteboardSynchronize(main_clipboard);
10894 
10895     ItemCount item_count = 0;
10896     PasteboardGetItemCount(main_clipboard, &item_count);
10897     for (ItemCount i = 0; i < item_count; i++)
10898     {
10899         PasteboardItemID item_id = 0;
10900         PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id);
10901         CFArrayRef flavor_type_array = 0;
10902         PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array);
10903         for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++)
10904         {
10905             CFDataRef cf_data;
10906             if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr)
10907             {
10908                 ImGuiContext& g = *GImGui;
10909                 g.ClipboardHandlerData.clear();
10910                 int length = (int)CFDataGetLength(cf_data);
10911                 g.ClipboardHandlerData.resize(length + 1);
10912                 CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data);
10913                 g.ClipboardHandlerData[length] = 0;
10914                 CFRelease(cf_data);
10915                 return g.ClipboardHandlerData.Data;
10916             }
10917         }
10918     }
10919     return NULL;
10920 }
10921 
10922 #else
10923 
10924 // Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers.
GetClipboardTextFn_DefaultImpl(void *)10925 static const char* GetClipboardTextFn_DefaultImpl(void*)
10926 {
10927     ImGuiContext& g = *GImGui;
10928     return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin();
10929 }
10930 
SetClipboardTextFn_DefaultImpl(void *,const char * text)10931 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
10932 {
10933     ImGuiContext& g = *GImGui;
10934     g.ClipboardHandlerData.clear();
10935     const char* text_end = text + strlen(text);
10936     g.ClipboardHandlerData.resize((int)(text_end - text) + 1);
10937     memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text));
10938     g.ClipboardHandlerData[(int)(text_end - text)] = 0;
10939 }
10940 
10941 #endif
10942 
10943 // Win32 API IME support (for Asian languages, etc.)
10944 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
10945 
10946 #include <imm.h>
10947 #ifdef _MSC_VER
10948 #pragma comment(lib, "imm32")
10949 #endif
10950 
ImeSetInputScreenPosFn_DefaultImpl(int x,int y)10951 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
10952 {
10953     // Notify OS Input Method Editor of text input position
10954     ImGuiIO& io = ImGui::GetIO();
10955     if (HWND hwnd = (HWND)io.ImeWindowHandle)
10956         if (HIMC himc = ::ImmGetContext(hwnd))
10957         {
10958             COMPOSITIONFORM cf;
10959             cf.ptCurrentPos.x = x;
10960             cf.ptCurrentPos.y = y;
10961             cf.dwStyle = CFS_FORCE_POSITION;
10962             ::ImmSetCompositionWindow(himc, &cf);
10963             ::ImmReleaseContext(hwnd, himc);
10964         }
10965 }
10966 
10967 #else
10968 
ImeSetInputScreenPosFn_DefaultImpl(int,int)10969 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
10970 
10971 #endif
10972 
10973 //-----------------------------------------------------------------------------
10974 // [SECTION] METRICS/DEBUGGER WINDOW
10975 //-----------------------------------------------------------------------------
10976 // - RenderViewportThumbnail() [Internal]
10977 // - RenderViewportsThumbnails() [Internal]
10978 // - MetricsHelpMarker() [Internal]
10979 // - ShowMetricsWindow()
10980 // - DebugNodeColumns() [Internal]
10981 // - DebugNodeDrawList() [Internal]
10982 // - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal]
10983 // - DebugNodeStorage() [Internal]
10984 // - DebugNodeTabBar() [Internal]
10985 // - DebugNodeViewport() [Internal]
10986 // - DebugNodeWindow() [Internal]
10987 // - DebugNodeWindowSettings() [Internal]
10988 // - DebugNodeWindowsList() [Internal]
10989 //-----------------------------------------------------------------------------
10990 
10991 #ifndef IMGUI_DISABLE_METRICS_WINDOW
10992 
DebugRenderViewportThumbnail(ImDrawList * draw_list,ImGuiViewportP * viewport,const ImRect & bb)10993 void ImGui::DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb)
10994 {
10995     ImGuiContext& g = *GImGui;
10996     ImGuiWindow* window = g.CurrentWindow;
10997 
10998     ImVec2 scale = bb.GetSize() / viewport->Size;
10999     ImVec2 off = bb.Min - viewport->Pos * scale;
11000     float alpha_mul = 1.0f;
11001     window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f));
11002     for (int i = 0; i != g.Windows.Size; i++)
11003     {
11004         ImGuiWindow* thumb_window = g.Windows[i];
11005         if (!thumb_window->WasActive || (thumb_window->Flags & ImGuiWindowFlags_ChildWindow))
11006             continue;
11007 
11008         ImRect thumb_r = thumb_window->Rect();
11009         ImRect title_r = thumb_window->TitleBarRect();
11010         thumb_r = ImRect(ImFloor(off + thumb_r.Min * scale), ImFloor(off +  thumb_r.Max * scale));
11011         title_r = ImRect(ImFloor(off + title_r.Min * scale), ImFloor(off +  ImVec2(title_r.Max.x, title_r.Min.y) * scale) + ImVec2(0,5)); // Exaggerate title bar height
11012         thumb_r.ClipWithFull(bb);
11013         title_r.ClipWithFull(bb);
11014         const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight);
11015         window->DrawList->AddRectFilled(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_WindowBg, alpha_mul));
11016         window->DrawList->AddRectFilled(title_r.Min, title_r.Max, GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg, alpha_mul));
11017         window->DrawList->AddRect(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_Border, alpha_mul));
11018         window->DrawList->AddText(g.Font, g.FontSize * 1.0f, title_r.Min, GetColorU32(ImGuiCol_Text, alpha_mul), thumb_window->Name, FindRenderedTextEnd(thumb_window->Name));
11019     }
11020     draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul));
11021 }
11022 
RenderViewportsThumbnails()11023 static void RenderViewportsThumbnails()
11024 {
11025     ImGuiContext& g = *GImGui;
11026     ImGuiWindow* window = g.CurrentWindow;
11027 
11028     // We don't display full monitor bounds (we could, but it often looks awkward), instead we display just enough to cover all of our viewports.
11029     float SCALE = 1.0f / 8.0f;
11030     ImRect bb_full(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
11031     for (int n = 0; n < g.Viewports.Size; n++)
11032         bb_full.Add(g.Viewports[n]->GetMainRect());
11033     ImVec2 p = window->DC.CursorPos;
11034     ImVec2 off = p - bb_full.Min * SCALE;
11035     for (int n = 0; n < g.Viewports.Size; n++)
11036     {
11037         ImGuiViewportP* viewport = g.Viewports[n];
11038         ImRect viewport_draw_bb(off + (viewport->Pos) * SCALE, off + (viewport->Pos + viewport->Size) * SCALE);
11039         ImGui::DebugRenderViewportThumbnail(window->DrawList, viewport, viewport_draw_bb);
11040     }
11041     ImGui::Dummy(bb_full.GetSize() * SCALE);
11042 }
11043 
11044 // Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds.
MetricsHelpMarker(const char * desc)11045 static void MetricsHelpMarker(const char* desc)
11046 {
11047     ImGui::TextDisabled("(?)");
11048     if (ImGui::IsItemHovered())
11049     {
11050         ImGui::BeginTooltip();
11051         ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
11052         ImGui::TextUnformatted(desc);
11053         ImGui::PopTextWrapPos();
11054         ImGui::EndTooltip();
11055     }
11056 }
11057 
11058 #ifndef IMGUI_DISABLE_DEMO_WINDOWS
11059 namespace ImGui { void ShowFontAtlas(ImFontAtlas* atlas); }
11060 #endif
11061 
ShowMetricsWindow(bool * p_open)11062 void ImGui::ShowMetricsWindow(bool* p_open)
11063 {
11064     if (!Begin("Dear ImGui Metrics/Debugger", p_open))
11065     {
11066         End();
11067         return;
11068     }
11069 
11070     ImGuiContext& g = *GImGui;
11071     ImGuiIO& io = g.IO;
11072     ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
11073 
11074     // Basic info
11075     Text("Dear ImGui %s", GetVersion());
11076     Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
11077     Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
11078     Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
11079     Text("%d active allocations", io.MetricsActiveAllocations);
11080     //SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; }
11081 
11082     Separator();
11083 
11084     // Debugging enums
11085     enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentIdeal, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type
11086     const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentIdeal", "ContentRegionRect" };
11087     enum { TRT_OuterRect, TRT_InnerRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsWorkRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersIdeal, TRT_ColumnsContentFrozen, TRT_ColumnsContentUnfrozen, TRT_Count }; // Tables Rect Type
11088     const char* trt_rects_names[TRT_Count] = { "OuterRect", "InnerRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsWorkRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentFrozen", "ColumnsContentUnfrozen" };
11089     if (cfg->ShowWindowsRectsType < 0)
11090         cfg->ShowWindowsRectsType = WRT_WorkRect;
11091     if (cfg->ShowTablesRectsType < 0)
11092         cfg->ShowTablesRectsType = TRT_WorkRect;
11093 
11094     struct Funcs
11095     {
11096         static ImRect GetTableRect(ImGuiTable* table, int rect_type, int n)
11097         {
11098             if (rect_type == TRT_OuterRect)                     { return table->OuterRect; }
11099             else if (rect_type == TRT_InnerRect)                { return table->InnerRect; }
11100             else if (rect_type == TRT_WorkRect)                 { return table->WorkRect; }
11101             else if (rect_type == TRT_HostClipRect)             { return table->HostClipRect; }
11102             else if (rect_type == TRT_InnerClipRect)            { return table->InnerClipRect; }
11103             else if (rect_type == TRT_BackgroundClipRect)       { return table->BgClipRect; }
11104             else if (rect_type == TRT_ColumnsRect)              { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->MinX, table->InnerClipRect.Min.y, c->MaxX, table->InnerClipRect.Min.y + table->LastOuterHeight); }
11105             else if (rect_type == TRT_ColumnsWorkRect)          { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->WorkRect.Min.y, c->WorkMaxX, table->WorkRect.Max.y); }
11106             else if (rect_type == TRT_ColumnsClipRect)          { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; }
11107             else if (rect_type == TRT_ColumnsContentHeadersUsed){ ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersUsed, table->InnerClipRect.Min.y + table->LastFirstRowHeight); } // Note: y1/y2 not always accurate
11108             else if (rect_type == TRT_ColumnsContentHeadersIdeal){ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersIdeal, table->InnerClipRect.Min.y + table->LastFirstRowHeight); }
11109             else if (rect_type == TRT_ColumnsContentFrozen)     { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXFrozen, table->InnerClipRect.Min.y + table->LastFirstRowHeight); }
11110             else if (rect_type == TRT_ColumnsContentUnfrozen)   { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y + table->LastFirstRowHeight, c->ContentMaxXUnfrozen, table->InnerClipRect.Max.y); }
11111             IM_ASSERT(0);
11112             return ImRect();
11113         }
11114 
11115         static ImRect GetWindowRect(ImGuiWindow* window, int rect_type)
11116         {
11117             if (rect_type == WRT_OuterRect)                 { return window->Rect(); }
11118             else if (rect_type == WRT_OuterRectClipped)     { return window->OuterRectClipped; }
11119             else if (rect_type == WRT_InnerRect)            { return window->InnerRect; }
11120             else if (rect_type == WRT_InnerClipRect)        { return window->InnerClipRect; }
11121             else if (rect_type == WRT_WorkRect)             { return window->WorkRect; }
11122             else if (rect_type == WRT_Content)       { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
11123             else if (rect_type == WRT_ContentIdeal)         { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSizeIdeal); }
11124             else if (rect_type == WRT_ContentRegionRect)    { return window->ContentRegionRect; }
11125             IM_ASSERT(0);
11126             return ImRect();
11127         }
11128     };
11129 
11130     // Tools
11131     if (TreeNode("Tools"))
11132     {
11133         // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
11134         if (Button("Item Picker.."))
11135             DebugStartItemPicker();
11136         SameLine();
11137         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.");
11138 
11139         Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder);
11140         Checkbox("Show windows rectangles", &cfg->ShowWindowsRects);
11141         SameLine();
11142         SetNextItemWidth(GetFontSize() * 12);
11143         cfg->ShowWindowsRects |= Combo("##show_windows_rect_type", &cfg->ShowWindowsRectsType, wrt_rects_names, WRT_Count, WRT_Count);
11144         if (cfg->ShowWindowsRects && g.NavWindow != NULL)
11145         {
11146             BulletText("'%s':", g.NavWindow->Name);
11147             Indent();
11148             for (int rect_n = 0; rect_n < WRT_Count; rect_n++)
11149             {
11150                 ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n);
11151                 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]);
11152             }
11153             Unindent();
11154         }
11155         Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh);
11156         Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes);
11157 
11158         Checkbox("Show tables rectangles", &cfg->ShowTablesRects);
11159         SameLine();
11160         SetNextItemWidth(GetFontSize() * 12);
11161         cfg->ShowTablesRects |= Combo("##show_table_rects_type", &cfg->ShowTablesRectsType, trt_rects_names, TRT_Count, TRT_Count);
11162         if (cfg->ShowTablesRects && g.NavWindow != NULL)
11163         {
11164             for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)
11165             {
11166                 ImGuiTable* table = g.Tables.TryGetMapData(table_n);
11167                 if (table == NULL || table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow))
11168                     continue;
11169 
11170                 BulletText("Table 0x%08X (%d columns, in '%s')", table->ID, table->ColumnsCount, table->OuterWindow->Name);
11171                 if (IsItemHovered())
11172                     GetForegroundDrawList()->AddRect(table->OuterRect.Min - ImVec2(1, 1), table->OuterRect.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
11173                 Indent();
11174                 char buf[128];
11175                 for (int rect_n = 0; rect_n < TRT_Count; rect_n++)
11176                 {
11177                     if (rect_n >= TRT_ColumnsRect)
11178                     {
11179                         if (rect_n != TRT_ColumnsRect && rect_n != TRT_ColumnsClipRect)
11180                             continue;
11181                         for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
11182                         {
11183                             ImRect r = Funcs::GetTableRect(table, rect_n, column_n);
11184                             ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) Col %d %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), column_n, trt_rects_names[rect_n]);
11185                             Selectable(buf);
11186                             if (IsItemHovered())
11187                                 GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
11188                         }
11189                     }
11190                     else
11191                     {
11192                         ImRect r = Funcs::GetTableRect(table, rect_n, -1);
11193                         ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), trt_rects_names[rect_n]);
11194                         Selectable(buf);
11195                         if (IsItemHovered())
11196                             GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
11197                     }
11198                 }
11199                 Unindent();
11200             }
11201         }
11202 
11203         TreePop();
11204     }
11205 
11206     // Windows
11207     DebugNodeWindowsList(&g.Windows, "Windows");
11208     //DebugNodeWindowsList(&g.WindowsFocusOrder, "WindowsFocusOrder");
11209 
11210     // DrawLists
11211     int drawlist_count = 0;
11212     for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++)
11213         drawlist_count += g.Viewports[viewport_i]->DrawDataBuilder.GetDrawListCount();
11214     if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count))
11215     {
11216         for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++)
11217         {
11218             ImGuiViewportP* viewport = g.Viewports[viewport_i];
11219             for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++)
11220                 for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++)
11221                     DebugNodeDrawList(NULL, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList");
11222         }
11223         TreePop();
11224     }
11225 
11226     // Viewports
11227     if (TreeNode("Viewports", "Viewports (%d)", g.Viewports.Size))
11228     {
11229         Indent(GetTreeNodeToLabelSpacing());
11230         RenderViewportsThumbnails();
11231         Unindent(GetTreeNodeToLabelSpacing());
11232         for (int i = 0; i < g.Viewports.Size; i++)
11233             DebugNodeViewport(g.Viewports[i]);
11234         TreePop();
11235     }
11236 
11237     // Details for Popups
11238     if (TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
11239     {
11240         for (int i = 0; i < g.OpenPopupStack.Size; i++)
11241         {
11242             ImGuiWindow* window = g.OpenPopupStack[i].Window;
11243             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" : "");
11244         }
11245         TreePop();
11246     }
11247 
11248     // Details for TabBars
11249     if (TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetAliveCount()))
11250     {
11251         for (int n = 0; n < g.TabBars.GetMapSize(); n++)
11252             if (ImGuiTabBar* tab_bar = g.TabBars.TryGetMapData(n))
11253             {
11254                 PushID(tab_bar);
11255                 DebugNodeTabBar(tab_bar, "TabBar");
11256                 PopID();
11257             }
11258         TreePop();
11259     }
11260 
11261     // Details for Tables
11262     if (TreeNode("Tables", "Tables (%d)", g.Tables.GetAliveCount()))
11263     {
11264         for (int n = 0; n < g.Tables.GetMapSize(); n++)
11265             if (ImGuiTable* table = g.Tables.TryGetMapData(n))
11266                 DebugNodeTable(table);
11267         TreePop();
11268     }
11269 
11270     // Details for Fonts
11271 #ifndef IMGUI_DISABLE_DEMO_WINDOWS
11272     ImFontAtlas* atlas = g.IO.Fonts;
11273     if (TreeNode("Fonts", "Fonts (%d)", atlas->Fonts.Size))
11274     {
11275         ShowFontAtlas(atlas);
11276         TreePop();
11277     }
11278 #endif
11279 
11280     // Details for Docking
11281 #ifdef IMGUI_HAS_DOCK
11282     if (TreeNode("Docking"))
11283     {
11284         TreePop();
11285     }
11286 #endif // #ifdef IMGUI_HAS_DOCK
11287 
11288     // Settings
11289     if (TreeNode("Settings"))
11290     {
11291         if (SmallButton("Clear"))
11292             ClearIniSettings();
11293         SameLine();
11294         if (SmallButton("Save to memory"))
11295             SaveIniSettingsToMemory();
11296         SameLine();
11297         if (SmallButton("Save to disk"))
11298             SaveIniSettingsToDisk(g.IO.IniFilename);
11299         SameLine();
11300         if (g.IO.IniFilename)
11301             Text("\"%s\"", g.IO.IniFilename);
11302         else
11303             TextUnformatted("<NULL>");
11304         Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer);
11305         if (TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size))
11306         {
11307             for (int n = 0; n < g.SettingsHandlers.Size; n++)
11308                 BulletText("%s", g.SettingsHandlers[n].TypeName);
11309             TreePop();
11310         }
11311         if (TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size()))
11312         {
11313             for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
11314                 DebugNodeWindowSettings(settings);
11315             TreePop();
11316         }
11317 
11318         if (TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size()))
11319         {
11320             for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))
11321                 DebugNodeTableSettings(settings);
11322             TreePop();
11323         }
11324 
11325 #ifdef IMGUI_HAS_DOCK
11326 #endif // #ifdef IMGUI_HAS_DOCK
11327 
11328         if (TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size()))
11329         {
11330             InputTextMultiline("##Ini", (char*)(void*)g.SettingsIniData.c_str(), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, GetTextLineHeight() * 20), ImGuiInputTextFlags_ReadOnly);
11331             TreePop();
11332         }
11333         TreePop();
11334     }
11335 
11336     // Misc Details
11337     if (TreeNode("Internal state"))
11338     {
11339         const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad", "Nav", "Clipboard" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
11340 
11341         Text("WINDOWING");
11342         Indent();
11343         Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
11344         Text("HoveredWindow->Root: '%s'", g.HoveredWindow ? g.HoveredWindow->RootWindow->Name : "NULL");
11345         Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL");
11346         Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
11347         Unindent();
11348 
11349         Text("ITEMS");
11350         Indent();
11351         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]);
11352         Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
11353         Text("ActiveIdUsing: Wheel: %d, NavDirMask: %X, NavInputMask: %X, KeyInputMask: %llX", g.ActiveIdUsingMouseWheel, g.ActiveIdUsingNavDirMask, g.ActiveIdUsingNavInputMask, g.ActiveIdUsingKeyInputMask);
11354         Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame
11355         Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
11356         Unindent();
11357 
11358         Text("NAV,FOCUS");
11359         Indent();
11360         Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
11361         Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
11362         Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
11363         Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
11364         Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
11365         Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
11366         Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId);
11367         Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
11368         Unindent();
11369 
11370         TreePop();
11371     }
11372 
11373     // Overlay: Display windows Rectangles and Begin Order
11374     if (cfg->ShowWindowsRects || cfg->ShowWindowsBeginOrder)
11375     {
11376         for (int n = 0; n < g.Windows.Size; n++)
11377         {
11378             ImGuiWindow* window = g.Windows[n];
11379             if (!window->WasActive)
11380                 continue;
11381             ImDrawList* draw_list = GetForegroundDrawList(window);
11382             if (cfg->ShowWindowsRects)
11383             {
11384                 ImRect r = Funcs::GetWindowRect(window, cfg->ShowWindowsRectsType);
11385                 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
11386             }
11387             if (cfg->ShowWindowsBeginOrder && !(window->Flags & ImGuiWindowFlags_ChildWindow))
11388             {
11389                 char buf[32];
11390                 ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
11391                 float font_size = GetFontSize();
11392                 draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
11393                 draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf);
11394             }
11395         }
11396     }
11397 
11398     // Overlay: Display Tables Rectangles
11399     if (cfg->ShowTablesRects)
11400     {
11401         for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)
11402         {
11403             ImGuiTable* table = g.Tables.TryGetMapData(table_n);
11404             if (table == NULL || table->LastFrameActive < g.FrameCount - 1)
11405                 continue;
11406             ImDrawList* draw_list = GetForegroundDrawList(table->OuterWindow);
11407             if (cfg->ShowTablesRectsType >= TRT_ColumnsRect)
11408             {
11409                 for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
11410                 {
11411                     ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, column_n);
11412                     ImU32 col = (table->HoveredColumnBody == column_n) ? IM_COL32(255, 255, 128, 255) : IM_COL32(255, 0, 128, 255);
11413                     float thickness = (table->HoveredColumnBody == column_n) ? 3.0f : 1.0f;
11414                     draw_list->AddRect(r.Min, r.Max, col, 0.0f, 0, thickness);
11415                 }
11416             }
11417             else
11418             {
11419                 ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, -1);
11420                 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
11421             }
11422         }
11423     }
11424 
11425 #ifdef IMGUI_HAS_DOCK
11426     // Overlay: Display Docking info
11427     if (show_docking_nodes && g.IO.KeyCtrl)
11428     {
11429     }
11430 #endif // #ifdef IMGUI_HAS_DOCK
11431 
11432     End();
11433 }
11434 
11435 // [DEBUG] List fonts in a font atlas and display its texture
ShowFontAtlas(ImFontAtlas * atlas)11436 void ImGui::ShowFontAtlas(ImFontAtlas* atlas)
11437 {
11438     for (int i = 0; i < atlas->Fonts.Size; i++)
11439     {
11440         ImFont* font = atlas->Fonts[i];
11441         PushID(font);
11442         DebugNodeFont(font);
11443         PopID();
11444     }
11445     if (TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight))
11446     {
11447         ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
11448         ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f);
11449         Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), tint_col, border_col);
11450         TreePop();
11451     }
11452 }
11453 
11454 // [DEBUG] Display contents of Columns
DebugNodeColumns(ImGuiOldColumns * columns)11455 void ImGui::DebugNodeColumns(ImGuiOldColumns* columns)
11456 {
11457     if (!TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
11458         return;
11459     BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX);
11460     for (int column_n = 0; column_n < columns->Columns.Size; column_n++)
11461         BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, GetColumnOffsetFromNorm(columns, columns->Columns[column_n].OffsetNorm));
11462     TreePop();
11463 }
11464 
11465 // [DEBUG] Display contents of ImDrawList
DebugNodeDrawList(ImGuiWindow * window,const ImDrawList * draw_list,const char * label)11466 void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, const char* label)
11467 {
11468     ImGuiContext& g = *GImGui;
11469     ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
11470     int cmd_count = draw_list->CmdBuffer.Size;
11471     if (cmd_count > 0 && draw_list->CmdBuffer.back().ElemCount == 0 && draw_list->CmdBuffer.back().UserCallback == NULL)
11472         cmd_count--;
11473     bool node_open = TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, cmd_count);
11474     if (draw_list == GetWindowDrawList())
11475     {
11476         SameLine();
11477         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)
11478         if (node_open)
11479             TreePop();
11480         return;
11481     }
11482 
11483     ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list
11484     if (window && IsItemHovered())
11485         fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
11486     if (!node_open)
11487         return;
11488 
11489     if (window && !window->WasActive)
11490         TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!");
11491 
11492     for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.Data; pcmd < draw_list->CmdBuffer.Data + cmd_count; pcmd++)
11493     {
11494         if (pcmd->UserCallback)
11495         {
11496             BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
11497             continue;
11498         }
11499 
11500         char buf[300];
11501         ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d tris, Tex 0x%p, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
11502             pcmd->ElemCount / 3, (void*)(intptr_t)pcmd->TextureId,
11503             pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
11504         bool pcmd_node_open = TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf);
11505         if (IsItemHovered() && (cfg->ShowDrawCmdMesh || cfg->ShowDrawCmdBoundingBoxes) && fg_draw_list)
11506             DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, cfg->ShowDrawCmdMesh, cfg->ShowDrawCmdBoundingBoxes);
11507         if (!pcmd_node_open)
11508             continue;
11509 
11510         // Calculate approximate coverage area (touched pixel count)
11511         // This will be in pixels squared as long there's no post-scaling happening to the renderer output.
11512         const ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
11513         const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + pcmd->VtxOffset;
11514         float total_area = 0.0f;
11515         for (unsigned int idx_n = pcmd->IdxOffset; idx_n < pcmd->IdxOffset + pcmd->ElemCount; )
11516         {
11517             ImVec2 triangle[3];
11518             for (int n = 0; n < 3; n++, idx_n++)
11519                 triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos;
11520             total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]);
11521         }
11522 
11523         // Display vertex information summary. Hover to get all triangles drawn in wire-frame
11524         ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area);
11525         Selectable(buf);
11526         if (IsItemHovered() && fg_draw_list)
11527             DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, true, false);
11528 
11529         // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
11530         ImGuiListClipper clipper;
11531         clipper.Begin(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
11532         while (clipper.Step())
11533             for (int prim = clipper.DisplayStart, idx_i = pcmd->IdxOffset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++)
11534             {
11535                 char* buf_p = buf, * buf_end = buf + IM_ARRAYSIZE(buf);
11536                 ImVec2 triangle[3];
11537                 for (int n = 0; n < 3; n++, idx_i++)
11538                 {
11539                     const ImDrawVert& v = vtx_buffer[idx_buffer ? idx_buffer[idx_i] : idx_i];
11540                     triangle[n] = v.pos;
11541                     buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n",
11542                         (n == 0) ? "Vert:" : "     ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
11543                 }
11544 
11545                 Selectable(buf, false);
11546                 if (fg_draw_list && IsItemHovered())
11547                 {
11548                     ImDrawListFlags backup_flags = fg_draw_list->Flags;
11549                     fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
11550                     fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f);
11551                     fg_draw_list->Flags = backup_flags;
11552                 }
11553             }
11554         TreePop();
11555     }
11556     TreePop();
11557 }
11558 
11559 // [DEBUG] Display mesh/aabb of a ImDrawCmd
DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList * out_draw_list,const ImDrawList * draw_list,const ImDrawCmd * draw_cmd,bool show_mesh,bool show_aabb)11560 void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb)
11561 {
11562     IM_ASSERT(show_mesh || show_aabb);
11563 
11564     // Draw wire-frame version of all triangles
11565     ImRect clip_rect = draw_cmd->ClipRect;
11566     ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
11567     ImDrawListFlags backup_flags = out_draw_list->Flags;
11568     out_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
11569     for (unsigned int idx_n = draw_cmd->IdxOffset, idx_end = draw_cmd->IdxOffset + draw_cmd->ElemCount; idx_n < idx_end; )
11570     {
11571         ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; // We don't hold on those pointers past iterations as ->AddPolyline() may invalidate them if out_draw_list==draw_list
11572         ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset;
11573 
11574         ImVec2 triangle[3];
11575         for (int n = 0; n < 3; n++, idx_n++)
11576             vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos));
11577         if (show_mesh)
11578             out_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f); // In yellow: mesh triangles
11579     }
11580     // Draw bounding boxes
11581     if (show_aabb)
11582     {
11583         out_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU
11584         out_draw_list->AddRect(ImFloor(vtxs_rect.Min), ImFloor(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles
11585     }
11586     out_draw_list->Flags = backup_flags;
11587 }
11588 
11589 // [DEBUG] Display details for a single font, called by ShowStyleEditor().
DebugNodeFont(ImFont * font)11590 void ImGui::DebugNodeFont(ImFont* font)
11591 {
11592     bool opened = TreeNode(font, "Font: \"%s\"\n%.2f px, %d glyphs, %d file(s)",
11593         font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size, font->ConfigDataCount);
11594     SameLine();
11595     if (SmallButton("Set as default"))
11596         GetIO().FontDefault = font;
11597     if (!opened)
11598         return;
11599 
11600     // Display preview text
11601     PushFont(font);
11602     Text("The quick brown fox jumps over the lazy dog");
11603     PopFont();
11604 
11605     // Display details
11606     SetNextItemWidth(GetFontSize() * 8);
11607     DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f");
11608     SameLine(); MetricsHelpMarker(
11609         "Note than the default embedded font is NOT meant to be scaled.\n\n"
11610         "Font are currently rendered into bitmaps at a given size at the time of building the atlas. "
11611         "You may oversample them to get some flexibility with scaling. "
11612         "You can also render at multiple sizes and select which one to use at runtime.\n\n"
11613         "(Glimmer of hope: the atlas system will be rewritten in the future to make scaling more flexible.)");
11614     Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent);
11615     char c_str[5];
11616     Text("Fallback character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->FallbackChar), font->FallbackChar);
11617     Text("Ellipsis character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->EllipsisChar), font->EllipsisChar);
11618     const int surface_sqrt = (int)ImSqrt((float)font->MetricsTotalSurface);
11619     Text("Texture Area: about %d px ~%dx%d px", font->MetricsTotalSurface, surface_sqrt, surface_sqrt);
11620     for (int config_i = 0; config_i < font->ConfigDataCount; config_i++)
11621         if (font->ConfigData)
11622             if (const ImFontConfig* cfg = &font->ConfigData[config_i])
11623                 BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d, Offset: (%.1f,%.1f)",
11624                     config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH, cfg->GlyphOffset.x, cfg->GlyphOffset.y);
11625 
11626     // Display all glyphs of the fonts in separate pages of 256 characters
11627     if (TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size))
11628     {
11629         ImDrawList* draw_list = GetWindowDrawList();
11630         const ImU32 glyph_col = GetColorU32(ImGuiCol_Text);
11631         const float cell_size = font->FontSize * 1;
11632         const float cell_spacing = GetStyle().ItemSpacing.y;
11633         for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256)
11634         {
11635             // Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k)
11636             // This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT
11637             // is large // (if ImWchar==ImWchar32 we will do at least about 272 queries here)
11638             if (!(base & 4095) && font->IsGlyphRangeUnused(base, base + 4095))
11639             {
11640                 base += 4096 - 256;
11641                 continue;
11642             }
11643 
11644             int count = 0;
11645             for (unsigned int n = 0; n < 256; n++)
11646                 if (font->FindGlyphNoFallback((ImWchar)(base + n)))
11647                     count++;
11648             if (count <= 0)
11649                 continue;
11650             if (!TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph"))
11651                 continue;
11652 
11653             // Draw a 16x16 grid of glyphs
11654             ImVec2 base_pos = GetCursorScreenPos();
11655             for (unsigned int n = 0; n < 256; n++)
11656             {
11657                 // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions
11658                 // available here and thus cannot easily generate a zero-terminated UTF-8 encoded string.
11659                 ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing));
11660                 ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size);
11661                 const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n));
11662                 draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50));
11663                 if (glyph)
11664                     font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n));
11665                 if (glyph && IsMouseHoveringRect(cell_p1, cell_p2))
11666                 {
11667                     BeginTooltip();
11668                     Text("Codepoint: U+%04X", base + n);
11669                     Separator();
11670                     Text("Visible: %d", glyph->Visible);
11671                     Text("AdvanceX: %.1f", glyph->AdvanceX);
11672                     Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1);
11673                     Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1);
11674                     EndTooltip();
11675                 }
11676             }
11677             Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16));
11678             TreePop();
11679         }
11680         TreePop();
11681     }
11682     TreePop();
11683 }
11684 
11685 // [DEBUG] Display contents of ImGuiStorage
DebugNodeStorage(ImGuiStorage * storage,const char * label)11686 void ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label)
11687 {
11688     if (!TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes()))
11689         return;
11690     for (int n = 0; n < storage->Data.Size; n++)
11691     {
11692         const ImGuiStorage::ImGuiStoragePair& p = storage->Data[n];
11693         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.
11694     }
11695     TreePop();
11696 }
11697 
11698 // [DEBUG] Display contents of ImGuiTabBar
DebugNodeTabBar(ImGuiTabBar * tab_bar,const char * label)11699 void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label)
11700 {
11701     // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
11702     char buf[256];
11703     char* p = buf;
11704     const char* buf_end = buf + IM_ARRAYSIZE(buf);
11705     const bool is_active = (tab_bar->PrevFrameVisible >= GetFrameCount() - 2);
11706     p += ImFormatString(p, buf_end - p, "%s 0x%08X (%d tabs)%s", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*");
11707     p += ImFormatString(p, buf_end - p, "  { ");
11708     for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++)
11709     {
11710         ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
11711         p += ImFormatString(p, buf_end - p, "%s'%s'",
11712             tab_n > 0 ? ", " : "", (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???");
11713     }
11714     p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } ");
11715     if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
11716     bool open = TreeNode(label, "%s", buf);
11717     if (!is_active) { PopStyleColor(); }
11718     if (is_active && IsItemHovered())
11719     {
11720         ImDrawList* draw_list = GetForegroundDrawList();
11721         draw_list->AddRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, IM_COL32(255, 255, 0, 255));
11722         draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));
11723         draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));
11724     }
11725     if (open)
11726     {
11727         for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
11728         {
11729             const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
11730             PushID(tab);
11731             if (SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } SameLine(0, 2);
11732             if (SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } SameLine();
11733             Text("%02d%c Tab 0x%08X '%s' Offset: %.1f, Width: %.1f/%.1f",
11734                 tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???", tab->Offset, tab->Width, tab->ContentWidth);
11735             PopID();
11736         }
11737         TreePop();
11738     }
11739 }
11740 
DebugNodeViewport(ImGuiViewportP * viewport)11741 void ImGui::DebugNodeViewport(ImGuiViewportP* viewport)
11742 {
11743     SetNextItemOpen(true, ImGuiCond_Once);
11744     if (TreeNode("viewport0", "Viewport #%d", 0))
11745     {
11746         ImGuiWindowFlags flags = viewport->Flags;
11747         BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Offset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f",
11748             viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y,
11749             viewport->WorkOffsetMin.x, viewport->WorkOffsetMin.y, viewport->WorkOffsetMax.x, viewport->WorkOffsetMax.y);
11750         BulletText("Flags: 0x%04X =%s%s%s", viewport->Flags,
11751             (flags & ImGuiViewportFlags_IsPlatformWindow)  ? " IsPlatformWindow"  : "",
11752             (flags & ImGuiViewportFlags_IsPlatformMonitor) ? " IsPlatformMonitor" : "",
11753             (flags & ImGuiViewportFlags_OwnedByApp)        ? " OwnedByApp"        : "");
11754         for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++)
11755             for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++)
11756                 DebugNodeDrawList(NULL, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList");
11757         TreePop();
11758     }
11759 }
11760 
DebugNodeWindow(ImGuiWindow * window,const char * label)11761 void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)
11762 {
11763     if (window == NULL)
11764     {
11765         BulletText("%s: NULL", label);
11766         return;
11767     }
11768 
11769     ImGuiContext& g = *GImGui;
11770     const bool is_active = window->WasActive;
11771     ImGuiTreeNodeFlags tree_node_flags = (window == g.NavWindow) ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None;
11772     if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
11773     const bool open = TreeNodeEx(label, tree_node_flags, "%s '%s'%s", label, window->Name, is_active ? "" : " *Inactive*");
11774     if (!is_active) { PopStyleColor(); }
11775     if (IsItemHovered() && is_active)
11776         GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
11777     if (!open)
11778         return;
11779 
11780     if (window->MemoryCompacted)
11781         TextDisabled("Note: some memory buffers have been compacted/freed.");
11782 
11783     ImGuiWindowFlags flags = window->Flags;
11784     DebugNodeDrawList(window, window->DrawList, "DrawList");
11785     BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f) Ideal (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y, window->ContentSizeIdeal.x, window->ContentSizeIdeal.y);
11786     BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
11787         (flags & ImGuiWindowFlags_ChildWindow)  ? "Child " : "",      (flags & ImGuiWindowFlags_Tooltip)     ? "Tooltip "   : "",  (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
11788         (flags & ImGuiWindowFlags_Modal)        ? "Modal " : "",      (flags & ImGuiWindowFlags_ChildMenu)   ? "ChildMenu " : "",  (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
11789         (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
11790     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" : "");
11791     BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
11792     BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems);
11793     for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)
11794     {
11795         ImRect r = window->NavRectRel[layer];
11796         if (r.Min.x >= r.Max.y && r.Min.y >= r.Max.y)
11797         {
11798             BulletText("NavLastIds[%d]: 0x%08X", layer, window->NavLastIds[layer]);
11799             continue;
11800         }
11801         BulletText("NavLastIds[%d]: 0x%08X at +(%.1f,%.1f)(%.1f,%.1f)", layer, window->NavLastIds[layer], r.Min.x, r.Min.y, r.Max.x, r.Max.y);
11802         if (IsItemHovered())
11803             GetForegroundDrawList(window)->AddRect(r.Min + window->Pos, r.Max + window->Pos, IM_COL32(255, 255, 0, 255));
11804     }
11805     BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
11806     if (window->RootWindow != window)       { DebugNodeWindow(window->RootWindow, "RootWindow"); }
11807     if (window->ParentWindow != NULL)       { DebugNodeWindow(window->ParentWindow, "ParentWindow"); }
11808     if (window->DC.ChildWindows.Size > 0)   { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); }
11809     if (window->ColumnsStorage.Size > 0 && TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
11810     {
11811         for (int n = 0; n < window->ColumnsStorage.Size; n++)
11812             DebugNodeColumns(&window->ColumnsStorage[n]);
11813         TreePop();
11814     }
11815     DebugNodeStorage(&window->StateStorage, "Storage");
11816     TreePop();
11817 }
11818 
DebugNodeWindowSettings(ImGuiWindowSettings * settings)11819 void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings* settings)
11820 {
11821     Text("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d",
11822         settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed);
11823 }
11824 
DebugNodeWindowsList(ImVector<ImGuiWindow * > * windows,const char * label)11825 void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label)
11826 {
11827     if (!TreeNode(label, "%s (%d)", label, windows->Size))
11828         return;
11829     Text("(In front-to-back order:)");
11830     for (int i = windows->Size - 1; i >= 0; i--) // Iterate front to back
11831     {
11832         PushID((*windows)[i]);
11833         DebugNodeWindow((*windows)[i], "Window");
11834         PopID();
11835     }
11836     TreePop();
11837 }
11838 
11839 #else
11840 
ShowMetricsWindow(bool *)11841 void ImGui::ShowMetricsWindow(bool*) {}
ShowFontAtlas(ImFontAtlas *)11842 void ImGui::ShowFontAtlas(ImFontAtlas*) {}
DebugNodeColumns(ImGuiOldColumns *)11843 void ImGui::DebugNodeColumns(ImGuiOldColumns*) {}
DebugNodeDrawList(ImGuiWindow *,const ImDrawList *,const char *)11844 void ImGui::DebugNodeDrawList(ImGuiWindow*, const ImDrawList*, const char*) {}
DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList *,const ImDrawList *,const ImDrawCmd *,bool,bool)11845 void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList*, const ImDrawList*, const ImDrawCmd*, bool, bool) {}
DebugNodeFont(ImFont *)11846 void ImGui::DebugNodeFont(ImFont*) {}
DebugNodeStorage(ImGuiStorage *,const char *)11847 void ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {}
DebugNodeTabBar(ImGuiTabBar *,const char *)11848 void ImGui::DebugNodeTabBar(ImGuiTabBar*, const char*) {}
DebugNodeWindow(ImGuiWindow *,const char *)11849 void ImGui::DebugNodeWindow(ImGuiWindow*, const char*) {}
DebugNodeWindowSettings(ImGuiWindowSettings *)11850 void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {}
DebugNodeWindowsList(ImVector<ImGuiWindow * > *,const char *)11851 void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>*, const char*) {}
DebugNodeViewport(ImGuiViewportP *)11852 void ImGui::DebugNodeViewport(ImGuiViewportP*) {}
11853 
11854 #endif
11855 
11856 //-----------------------------------------------------------------------------
11857 
11858 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
11859 // 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.
11860 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
11861 #include "imgui_user.inl"
11862 #endif
11863 
11864 //-----------------------------------------------------------------------------
11865 
11866 #endif // #ifndef IMGUI_DISABLE
11867