1 // dear imgui, v1.80
2 // (main code and documentation)
3 
4 // Help:
5 // - Read FAQ at http://dearimgui.org/faq
6 // - Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase.
7 // - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
8 // Read imgui.cpp for details, links and comments.
9 
10 // Resources:
11 // - FAQ                   http://dearimgui.org/faq
12 // - Homepage & latest     https://github.com/ocornut/imgui
13 // - Releases & changelog  https://github.com/ocornut/imgui/releases
14 // - Gallery               https://github.com/ocornut/imgui/issues/3488 (please post your screenshots/video there!)
15 // - Glossary              https://github.com/ocornut/imgui/wiki/Glossary
16 // - Wiki                  https://github.com/ocornut/imgui/wiki
17 // - Issues & support      https://github.com/ocornut/imgui/issues
18 
19 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
20 // See LICENSE.txt for copyright and licensing details (standard MIT License).
21 // This library is free but needs your support to sustain development and maintenance.
22 // Businesses: you can support continued development via invoiced technical support, maintenance and sponsoring contracts. Please reach out to "contact AT dearimgui.org".
23 // Individuals: you can support continued development via donations. See docs/README or web page.
24 
25 // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
26 // Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
27 // modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
28 // come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
29 // to a better solution or official support for them.
30 
31 /*
32 
33 Index of this file:
34 
35 DOCUMENTATION
36 
37 - MISSION STATEMENT
38 - END-USER GUIDE
39 - PROGRAMMER GUIDE
40   - READ FIRST
41   - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
42   - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
43   - HOW A SIMPLE APPLICATION MAY LOOK LIKE
44   - HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
45   - USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
46 - API BREAKING CHANGES (read me when you update!)
47 - FREQUENTLY ASKED QUESTIONS (FAQ)
48   - Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer)
49 
50 CODE
51 (search for "[SECTION]" in the code to find them)
52 
53 // [SECTION] INCLUDES
54 // [SECTION] FORWARD DECLARATIONS
55 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
56 // [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
57 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
58 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
59 // [SECTION] MISC HELPERS/UTILITIES (File functions)
60 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
61 // [SECTION] MISC HELPERS/UTILITIES (Color functions)
62 // [SECTION] ImGuiStorage
63 // [SECTION] ImGuiTextFilter
64 // [SECTION] ImGuiTextBuffer
65 // [SECTION] ImGuiListClipper
66 // [SECTION] STYLING
67 // [SECTION] RENDER HELPERS
68 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
69 // [SECTION] ERROR CHECKING
70 // [SECTION] LAYOUT
71 // [SECTION] SCROLLING
72 // [SECTION] TOOLTIPS
73 // [SECTION] POPUPS
74 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
75 // [SECTION] DRAG AND DROP
76 // [SECTION] LOGGING/CAPTURING
77 // [SECTION] SETTINGS
78 // [SECTION] VIEWPORTS, PLATFORM WINDOWS
79 // [SECTION] DOCKING
80 // [SECTION] PLATFORM DEPENDENT HELPERS
81 // [SECTION] METRICS/DEBUGGER WINDOW
82 
83 */
84 
85 //-----------------------------------------------------------------------------
86 // DOCUMENTATION
87 //-----------------------------------------------------------------------------
88 
89 /*
90 
91  MISSION STATEMENT
92  =================
93 
94  - Easy to use to create code-driven and data-driven tools.
95  - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
96  - Easy to hack and improve.
97  - Minimize setup and maintenance.
98  - Minimize state storage on user side.
99  - Portable, minimize dependencies, run on target (consoles, phones, etc.).
100  - Efficient runtime and memory consumption.
101 
102  Designed for developers and content-creators, not the typical end-user! Some of the current weaknesses includes:
103 
104  - Doesn't look fancy, doesn't animate.
105  - Limited layout features, intricate layouts are typically crafted in code.
106 
107 
108  END-USER GUIDE
109  ==============
110 
111  - Double-click on title bar to collapse window.
112  - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin().
113  - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents).
114  - Click and drag on any empty space to move window.
115  - TAB/SHIFT+TAB to cycle through keyboard editable fields.
116  - CTRL+Click on a slider or drag box to input value as text.
117  - Use mouse wheel to scroll.
118  - Text editor:
119    - Hold SHIFT or use mouse to select text.
120    - CTRL+Left/Right to word jump.
121    - CTRL+Shift+Left/Right to select words.
122    - CTRL+A our Double-Click to select all.
123    - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
124    - CTRL+Z,CTRL+Y to undo/redo.
125    - ESCAPE to revert text to its original value.
126    - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
127    - Controls are automatically adjusted for OSX to match standard OSX text editing operations.
128  - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard.
129  - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://dearimgui.org/controls_sheets
130 
131 
132  PROGRAMMER GUIDE
133  ================
134 
135  READ FIRST
136  ----------
137  - Remember to read the FAQ (https://www.dearimgui.org/faq)
138  - 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
139    or destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, less bugs.
140  - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
141  - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
142  - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
143    You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in the FAQ.
144  - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
145    For every application frame your UI code will be called only once. This is in contrast to e.g. Unity's own implementation of an IMGUI,
146    where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
147  - Our origin are on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
148  - This codebase is also optimized to yield decent performances with typical "Debug" builds settings.
149  - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).
150    If you get an assert, read the messages and comments around the assert.
151  - 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.
152  - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
153    See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
154    However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase.
155  - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!).
156 
157 
158  HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
159  ----------------------------------------------
160  - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
161  - Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over master.
162  - You can also use '#define IMGUI_USER_CONFIG "my_config_file.h" to redirect configuration to your own file.
163  - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
164    If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
165    from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
166    likely be a comment about it. Please report any issue to the GitHub page!
167  - To find out usage of old API, you can add '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in your configuration file.
168  - Try to keep your copy of Dear ImGui reasonably up to date.
169 
170 
171  GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
172  ---------------------------------------------------------------
173  - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
174  - In the majority of cases you should be able to use unmodified backends files available in the backends/ folder.
175  - Add the Dear ImGui source files + selected backend source files to your projects or using your preferred build system.
176    It is recommended you build and statically link the .cpp files as part of your project and NOT as shared library (DLL).
177  - 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.
178  - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
179  - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
180    Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
181    phases of your own application. All rendering information are stored into command-lists that you will retrieve after calling ImGui::Render().
182  - Refer to the backends and demo applications in the examples/ folder for instruction on how to setup your code.
183  - 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.
184 
185 
186  HOW A SIMPLE APPLICATION MAY LOOK LIKE
187  --------------------------------------
188  EXHIBIT 1: USING THE EXAMPLE BACKENDS (= imgui_impl_XXX.cpp files from the backends/ folder).
189  The sub-folders in examples/ contains examples applications following this structure.
190 
191      // Application init: create a dear imgui context, setup some options, load fonts
192      ImGui::CreateContext();
193      ImGuiIO& io = ImGui::GetIO();
194      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
195      // TODO: Fill optional fields of the io structure later.
196      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
197 
198      // Initialize helper Platform and Renderer backends (here we are using imgui_impl_win32.cpp and imgui_impl_dx11.cpp)
199      ImGui_ImplWin32_Init(hwnd);
200      ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
201 
202      // Application main loop
203      while (true)
204      {
205          // Feed inputs to dear imgui, start new frame
206          ImGui_ImplDX11_NewFrame();
207          ImGui_ImplWin32_NewFrame();
208          ImGui::NewFrame();
209 
210          // Any application code here
211          ImGui::Text("Hello, world!");
212 
213          // Render dear imgui into screen
214          ImGui::Render();
215          ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
216          g_pSwapChain->Present(1, 0);
217      }
218 
219      // Shutdown
220      ImGui_ImplDX11_Shutdown();
221      ImGui_ImplWin32_Shutdown();
222      ImGui::DestroyContext();
223 
224  EXHIBIT 2: IMPLEMENTING CUSTOM BACKEND / CUSTOM ENGINE
225 
226      // Application init: create a dear imgui context, setup some options, load fonts
227      ImGui::CreateContext();
228      ImGuiIO& io = ImGui::GetIO();
229      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
230      // TODO: Fill optional fields of the io structure later.
231      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
232 
233      // Build and load the texture atlas into a texture
234      // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
235      int width, height;
236      unsigned char* pixels = NULL;
237      io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
238 
239      // At this point you've got the texture data and you need to upload that your your graphic system:
240      // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
241      // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID.
242      MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
243      io.Fonts->TexID = (void*)texture;
244 
245      // Application main loop
246      while (true)
247      {
248         // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
249         // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform Backends)
250         io.DeltaTime = 1.0f/60.0f;              // set the time elapsed since the previous frame (in seconds)
251         io.DisplaySize.x = 1920.0f;             // set the current display width
252         io.DisplaySize.y = 1280.0f;             // set the current display height here
253         io.MousePos = my_mouse_pos;             // set the mouse position
254         io.MouseDown[0] = my_mouse_buttons[0];  // set the mouse button states
255         io.MouseDown[1] = my_mouse_buttons[1];
256 
257         // Call NewFrame(), after this point you can use ImGui::* functions anytime
258         // (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use Dear ImGui everywhere)
259         ImGui::NewFrame();
260 
261         // Most of your application code here
262         ImGui::Text("Hello, world!");
263         MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
264         MyGameRender(); // may use any Dear ImGui functions as well!
265 
266         // Render dear imgui, swap buffers
267         // (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)
268         ImGui::EndFrame();
269         ImGui::Render();
270         ImDrawData* draw_data = ImGui::GetDrawData();
271         MyImGuiRenderFunction(draw_data);
272         SwapBuffers();
273      }
274 
275      // Shutdown
276      ImGui::DestroyContext();
277 
278  To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest your application,
279  you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
280  Please read the FAQ and example applications for details about this!
281 
282 
283  HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
284  ---------------------------------------------
285  The backends in impl_impl_XXX.cpp files contains many working implementations of a rendering function.
286 
287     void void MyImGuiRenderFunction(ImDrawData* draw_data)
288     {
289        // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
290        // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
291        // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
292        // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
293        for (int n = 0; n < draw_data->CmdListsCount; n++)
294        {
295           const ImDrawList* cmd_list = draw_data->CmdLists[n];
296           const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;  // vertex buffer generated by Dear ImGui
297           const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;   // index buffer generated by Dear ImGui
298           for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
299           {
300              const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
301              if (pcmd->UserCallback)
302              {
303                  pcmd->UserCallback(cmd_list, pcmd);
304              }
305              else
306              {
307                  // The texture for the draw call is specified by pcmd->TextureId.
308                  // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization.
309                  MyEngineBindTexture((MyTexture*)pcmd->TextureId);
310 
311                  // We are using scissoring to clip some objects. All low-level graphics API should supports it.
312                  // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
313                  //   (some elements visible outside their bounds) but you can fix that once everything else works!
314                  // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize)
315                  //   In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize.
316                  //   However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github),
317                  //   always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
318                  // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
319                  ImVec2 pos = draw_data->DisplayPos;
320                  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));
321 
322                  // Render 'pcmd->ElemCount/3' indexed triangles.
323                  // 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.
324                  MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
325              }
326              idx_buffer += pcmd->ElemCount;
327           }
328        }
329     }
330 
331 
332  USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
333  ------------------------------------------
334  - The gamepad/keyboard navigation is fairly functional and keeps being improved.
335  - Gamepad support is particularly useful to use Dear ImGui on a console system (e.g. PS4, Switch, XB1) without a mouse!
336  - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787
337  - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
338  - Keyboard:
339     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
340       NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
341     - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
342       will be set. For more advanced uses, you may want to read from:
343        - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
344        - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
345        - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions.
346       Please reach out if you think the game vs navigation input sharing could be improved.
347  - Gamepad:
348     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
349     - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
350       Note that io.NavInputs[] is cleared by EndFrame().
351     - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
352          0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
353     - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
354       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.).
355     - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://dearimgui.org/controls_sheets
356     - 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
357       to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
358  - Mouse:
359     - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
360     - 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.
361     - 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.
362       Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
363       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.
364       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.
365       (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, imgui will misbehave as it will see your mouse as moving back and forth!)
366       (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
367        to set a boolean to ignore your other external mouse positions until the external source is moved again.)
368 
369 
370  API BREAKING CHANGES
371  ====================
372 
373  Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
374  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.
375  When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
376  You can read releases logs https://github.com/ocornut/imgui/releases for more details.
377 
378  (Docking/Viewport Branch)
379  - 2020/XX/XX (1.XX) - when multi-viewports are enabled, all positions will be in your natural OS coordinates space. It means that:
380                         - reference to hard-coded positions such as in SetNextWindowPos(ImVec2(0,0)) are probably not what you want anymore.
381                           you may use GetMainViewport()->Pos to offset hard-coded positions, e.g. SetNextWindowPos(GetMainViewport()->Pos)
382                         - likewise io.MousePos and GetMousePos() will use OS coordinates.
383                           If you query mouse positions to interact with non-imgui coordinates you will need to offset them, e.g. subtract GetWindowViewport()->Pos.
384  - 2020/XX/XX (1.XX) - Moved IME support functions from io.ImeSetInputScreenPosFn, io.ImeWindowHandle to the PlatformIO api.
385 
386 
387  - 2020/12/21 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.63 (August 2018):
388                         - ImGui::IsItemDeactivatedAfterChange() -> use ImGui::IsItemDeactivatedAfterEdit().
389                         - ImGuiCol_ModalWindowDarkening       -> use ImGuiCol_ModalWindowDimBg
390                         - ImGuiInputTextCallback              -> use ImGuiTextEditCallback
391                         - ImGuiInputTextCallbackData          -> use ImGuiTextEditCallbackData
392  - 2020/12/21 (1.80) - renamed ImDrawList::AddBezierCurve() to AddBezierCubic(), and PathBezierCurveTo() to PathBezierCubicCurveTo(). Kept inline redirection function (will obsolete).
393  - 2020/12/04 (1.80) - added imgui_tables.cpp file! Manually constructed project files will need the new file added!
394  - 2020/11/18 (1.80) - renamed undocumented/internals ImGuiColumnsFlags_* to ImGuiOldColumnFlags_* in prevision of incoming Tables API.
395  - 2020/11/03 (1.80) - renamed io.ConfigWindowsMemoryCompactTimer to io.ConfigMemoryCompactTimer as the feature will apply to other data structures
396  - 2020/10/14 (1.80) - backends: moved all backends files (imgui_impl_XXXX.cpp, imgui_impl_XXXX.h) from examples/ to backends/.
397  - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.60 (April 2018):
398                         - io.RenderDrawListsFn pointer        -> use ImGui::GetDrawData() value and call the render function of your backend
399                         - ImGui::IsAnyWindowFocused()         -> use ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)
400                         - ImGui::IsAnyWindowHovered()         -> use ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
401                         - ImGuiStyleVar_Count_                -> use ImGuiStyleVar_COUNT
402                         - ImGuiMouseCursor_Count_             -> use ImGuiMouseCursor_COUNT
403                       - removed redirecting functions names that were marked obsolete in 1.61 (May 2018):
404                         - InputFloat (... int decimal_precision ...) -> use InputFloat (... const char* format ...) with format = "%.Xf" where X is your value for decimal_precision.
405                         - same for InputFloat2()/InputFloat3()/InputFloat4() variants taking a `int decimal_precision` parameter.
406  - 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).
407  - 2020/09/25 (1.79) - renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. Kept redirection enum (will obsolete sooner because previous name was added recently).
408  - 2020/09/25 (1.79) - renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton.
409  - 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.
410  - 2020/09/21 (1.79) - removed return value from OpenPopupOnItemClick() - returned true on mouse release on item - because it is inconsistent with other popup APIs and makes others misleading. It's also and unnecessary: you can use IsWindowAppearing() after BeginPopup() for a similar result.
411  - 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!
412  - 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().
413                        replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags).
414                        worked out a backward-compatibility scheme so hopefully most C++ codebase should not be affected. in short, when calling those functions:
415                        - if you omitted the 'power' parameter (likely!), you are not affected.
416                        - 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.
417                        - 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.
418                        see https://github.com/ocornut/imgui/issues/3361 for all details.
419                        kept inline redirection functions (will obsolete) apart for: DragFloatRange2(), VSliderFloat(), VSliderScalar(). For those three the 'float power=1.0f' version were removed directly as they were most unlikely ever used.
420                        for shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`.
421                      - 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.
422  - 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.
423  - 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete). [NOTE: THIS WAS REVERTED IN 1.79]
424  - 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017.
425  - 2020/04/23 (1.77) - removed unnecessary ID (first arg) of ImFontAtlas::AddCustomRectRegular().
426  - 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more.
427  - 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.
428  - 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.
429  - 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017):
430                        - ShowTestWindow()                    -> use ShowDemoWindow()
431                        - IsRootWindowFocused()               -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow)
432                        - IsRootWindowOrAnyChildFocused()     -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
433                        - SetNextWindowContentWidth(w)        -> use SetNextWindowContentSize(ImVec2(w, 0.0f)
434                        - GetItemsLineHeightWithSpacing()     -> use GetFrameHeightWithSpacing()
435                        - ImGuiCol_ChildWindowBg              -> use ImGuiCol_ChildBg
436                        - ImGuiStyleVar_ChildWindowRounding   -> use ImGuiStyleVar_ChildRounding
437                        - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap
438                        - IMGUI_DISABLE_TEST_WINDOWS          -> use IMGUI_DISABLE_DEMO_WINDOWS
439  - 2019/12/08 (1.75) - obsoleted calling ImDrawList::PrimReserve() with a negative count (which was the vaguely documented and rarely if ever used). Instead we added an explicit PrimUnreserve() API.
440  - 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).
441  - 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.
442  - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency.
443  - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency.
444  - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017):
445                        - Begin() [old 5 args version]        -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed
446                        - IsRootWindowOrAnyChildHovered()     -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows)
447                        - AlignFirstTextHeightToWidgets()     -> use AlignTextToFramePadding()
448                        - SetNextWindowPosCenter()            -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f)
449                        - ImFont::Glyph                       -> use ImFontGlyph
450  - 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.
451                        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.
452                        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).
453                        If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you.
454  - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete).
455  - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete).
456  - 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.
457  - 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
458                        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.
459                        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.
460                        Please reach out if you are affected.
461  - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete).
462  - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c).
463  - 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.
464  - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).
465  - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).
466  - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).
467  - 2019/02/14 (1.68) - made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). If for some reason your time step calculation gives you a zero value, replace it with an arbitrary small value!
468  - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
469  - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
470  - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).
471  - 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.
472  - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
473  - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
474  - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
475  - 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.
476                        If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
477  - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
478  - 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.
479                        NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
480                        Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
481  - 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).
482  - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
483  - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
484  - 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.
485  - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
486  - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
487  - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
488  - 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.).
489                        old backends will still work as is, however prefer using the separated backends as they will be updated to support multi-viewports.
490                        when adopting new backends follow the main.cpp code of your preferred examples/ folder to know which functions to call.
491                        in particular, note that old backends called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function.
492  - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
493  - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
494  - 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.
495                        If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
496                        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.
497                        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.
498  - 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",
499                        consistent with other functions. Kept redirection functions (will obsolete).
500  - 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.
501  - 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).
502  - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
503  - 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.
504  - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
505  - 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.
506  - 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.
507  - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
508                        - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
509                        - removed Shutdown() function, as DestroyContext() serve this purpose.
510                        - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
511                        - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
512                        - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
513  - 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.
514  - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
515  - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
516  - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
517  - 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.
518  - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
519  - 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
520  - 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.
521  - 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.
522  - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
523  - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
524                      - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
525  - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
526  - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
527  - 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.
528  - 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.
529                        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.
530  - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
531  - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
532  - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
533  - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
534  - 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.
535  - 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.
536  - 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.
537                        removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
538                          IsItemHoveredRect()        --> IsItemHovered(ImGuiHoveredFlags_RectOnly)
539                          IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
540                          IsMouseHoveringWindow()    --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior]
541  - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
542  - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
543  - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).
544  - 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).
545  - 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)".
546  - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
547                      - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
548                      - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
549  - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
550  - 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.
551  - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type.
552  - 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.
553  - 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).
554  - 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).
555  - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
556  - 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.
557                      - 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.
558                      - 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))'
559  - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
560  - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
561  - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
562  - 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().
563  - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
564  - 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.
565  - 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.
566  - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
567                        If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you, otherwise if <1.0f you need tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
568                        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:
569                        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); }
570                        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.
571  - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
572  - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
573  - 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).
574  - 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.
575  - 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).
576  - 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)
577  - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
578  - 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.
579  - 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.
580  - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
581  - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
582  - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
583                        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.
584                        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!
585  - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
586  - 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.
587  - 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
588  - 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.
589                        you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
590  - 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.
591                        this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
592                      - 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.
593                      - the signature of the io.RenderDrawListsFn handler has changed!
594                        old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
595                        new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
596                          parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
597                          ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
598                          ImDrawCmd:  'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
599                      - 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.
600                      - 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!
601                      - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
602  - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
603  - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
604  - 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.
605  - 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
606  - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely be used. Sorry!
607  - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
608  - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
609  - 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.
610  - 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.
611  - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
612  - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
613  - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
614  - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
615  - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
616  - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
617  - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
618  - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
619  - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
620  - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
621  - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
622  - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
623  - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
624  - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
625  - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
626  - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
627  - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
628  - 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
629                        - old:  const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..];
630                        - new:  unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->TexId = YourTexIdentifier;
631                        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.
632  - 2015/01/11 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
633  - 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
634  - 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
635  - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
636  - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
637  - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
638  - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
639  - 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)
640  - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
641  - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
642  - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
643  - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
644  - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
645  - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
646 
647 
648  FREQUENTLY ASKED QUESTIONS (FAQ)
649  ================================
650 
651  Read all answers online:
652    https://www.dearimgui.org/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url)
653  Read all answers locally (with a text editor or ideally a Markdown viewer):
654    docs/FAQ.md
655  Some answers are copied down here to facilitate searching in code.
656 
657  Q&A: Basics
658  ===========
659 
660  Q: Where is the documentation?
661  A: This library is poorly documented at the moment and expects of the user to be acquainted with C/C++.
662     - Run the examples/ and explore them.
663     - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function.
664     - The demo covers most features of Dear ImGui, so you can read the code and see its output.
665     - See documentation and comments at the top of imgui.cpp + effectively imgui.h.
666     - Dozens of standalone example applications using e.g. OpenGL/DirectX are provided in the
667       examples/ folder to explain how to integrate Dear ImGui with your own engine/application.
668     - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links.
669     - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful.
670     - Your programming IDE is your friend, find the type or function declaration to find comments
671       associated to it.
672 
673  Q: What is this library called?
674  Q: Which version should I get?
675  >> This library is called "Dear ImGui", please don't call it "ImGui" :)
676  >> See https://www.dearimgui.org/faq for details.
677 
678  Q&A: Integration
679  ================
680 
681  Q: How to get started?
682  A: Read 'PROGRAMMER GUIDE' above. Read examples/README.txt.
683 
684  Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or to my application?
685  A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
686  >> See https://www.dearimgui.org/faq for fully detailed answer. You really want to read this.
687 
688  Q. How can I enable keyboard controls?
689  Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display)
690  Q: I integrated Dear ImGui in my engine and little squares are showing instead of text..
691  Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
692  Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries..
693  >> See https://www.dearimgui.org/faq
694 
695  Q&A: Usage
696  ----------
697 
698  Q: Why is my widget not reacting when I click on it?
699  Q: How can I have widgets with an empty label?
700  Q: How can I have multiple widgets with the same label?
701  Q: How can I display an image? What is ImTextureID, how does it works?
702  Q: How can I use my own math types instead of ImVec2/ImVec4?
703  Q: How can I interact with standard C++ types (such as std::string and std::vector)?
704  Q: How can I display custom shapes? (using low-level ImDrawList API)
705  >> See https://www.dearimgui.org/faq
706 
707  Q&A: Fonts, Text
708  ================
709 
710  Q: How should I handle DPI in my application?
711  Q: How can I load a different font than the default?
712  Q: How can I easily use icons in my application?
713  Q: How can I load multiple fonts?
714  Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
715  >> See https://www.dearimgui.org/faq and https://github.com/ocornut/imgui/edit/master/docs/FONTS.md
716 
717  Q&A: Concerns
718  =============
719 
720  Q: Who uses Dear ImGui?
721  Q: Can you create elaborate/serious tools with Dear ImGui?
722  Q: Can you reskin the look of Dear ImGui?
723  Q: Why using C++ (as opposed to C)?
724  >> See https://www.dearimgui.org/faq
725 
726  Q&A: Community
727  ==============
728 
729  Q: How can I help?
730  A: - Businesses: please reach out to "contact AT dearimgui.org" if you work in a place using Dear ImGui!
731       We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts.
732       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.
733     - Individuals: you can support continued development via PayPal donations. See README.
734     - If you are experienced with Dear ImGui and C++, look at the github issues, look at the Wiki, read docs/TODO.txt
735       and see how you want to help and can help!
736     - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
737       You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/3488). Visuals are ideal as they inspire other programmers.
738       But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions.
739     - 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).
740 
741 */
742 
743 //-------------------------------------------------------------------------
744 // [SECTION] INCLUDES
745 //-------------------------------------------------------------------------
746 
747 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
748 #define _CRT_SECURE_NO_WARNINGS
749 #endif
750 
751 #include "imgui.h"
752 #ifndef IMGUI_DISABLE
753 
754 #ifndef IMGUI_DEFINE_MATH_OPERATORS
755 #define IMGUI_DEFINE_MATH_OPERATORS
756 #endif
757 #include "imgui_internal.h"
758 
759 // System includes
760 #include <ctype.h>      // toupper
761 #include <stdio.h>      // vsnprintf, sscanf, printf
762 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
763 #include <stddef.h>     // intptr_t
764 #else
765 #include <stdint.h>     // intptr_t
766 #endif
767 
768 // [Windows] OS specific includes (optional)
769 #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)
770 #define IMGUI_DISABLE_WIN32_FUNCTIONS
771 #endif
772 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
773 #ifndef WIN32_LEAN_AND_MEAN
774 #define WIN32_LEAN_AND_MEAN
775 #endif
776 #ifndef NOMINMAX
777 #define NOMINMAX
778 #endif
779 #ifndef __MINGW32__
780 #include <Windows.h>        // _wfopen, OpenClipboard
781 #else
782 #include <windows.h>
783 #endif
784 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) // UWP doesn't have all Win32 functions
785 #define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
786 #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
787 #endif
788 #endif
789 
790 // [Apple] OS specific includes
791 #if defined(__APPLE__)
792 #include <TargetConditionals.h>
793 #endif
794 
795 // Visual Studio warnings
796 #ifdef _MSC_VER
797 #pragma warning (disable: 4127)             // condition expression is constant
798 #pragma warning (disable: 4996)             // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
799 #if defined(_MSC_VER) && _MSC_VER >= 1922   // MSVC 2019 16.2 or later
800 #pragma warning (disable: 5054)             // operator '|': deprecated between enumerations of different types
801 #endif
802 #endif
803 
804 // Clang/GCC warnings with -Weverything
805 #if defined(__clang__)
806 #if __has_warning("-Wunknown-warning-option")
807 #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!
808 #endif
809 #pragma clang diagnostic ignored "-Wunknown-pragmas"                // warning: unknown warning group 'xxx'
810 #pragma clang diagnostic ignored "-Wold-style-cast"                 // warning: use of old-style cast                            // yes, they are more terse.
811 #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.
812 #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.
813 #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.
814 #pragma clang diagnostic ignored "-Wglobal-constructors"            // warning: declaration requires a global destructor         // similar to above, not sure what the exact difference is.
815 #pragma clang diagnostic ignored "-Wsign-conversion"                // warning: implicit conversion changes signedness
816 #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.
817 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast"       // warning: cast to 'void *' from smaller integer type 'int'
818 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"  // warning: zero as null pointer constant                    // some standard header variations use #define NULL 0
819 #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.
820 #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"  // warning: implicit conversion from 'xxx' to 'float' may lose precision
821 #elif defined(__GNUC__)
822 // We disable -Wpragmas because GCC doesn't provide an has_warning equivalent and some forks/patches may not following the warning/version association.
823 #pragma GCC diagnostic ignored "-Wpragmas"                  // warning: unknown option after '#pragma GCC diagnostic' kind
824 #pragma GCC diagnostic ignored "-Wunused-function"          // warning: 'xxxx' defined but not used
825 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"      // warning: cast to pointer from integer of different size
826 #pragma GCC diagnostic ignored "-Wformat"                   // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
827 #pragma GCC diagnostic ignored "-Wdouble-promotion"         // warning: implicit conversion from 'float' to 'double' when passing argument to function
828 #pragma GCC diagnostic ignored "-Wconversion"               // warning: conversion to 'xxxx' from 'xxxx' may alter its value
829 #pragma GCC diagnostic ignored "-Wformat-nonliteral"        // warning: format not a string literal, format string not checked
830 #pragma GCC diagnostic ignored "-Wstrict-overflow"          // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
831 #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
832 #endif
833 
834 // Debug options
835 #define IMGUI_DEBUG_NAV_SCORING     0   // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL
836 #define IMGUI_DEBUG_NAV_RECTS       0   // Display the reference navigation rectangle for each window
837 #define IMGUI_DEBUG_INI_SETTINGS    0   // Save additional comments in .ini file (particularly helps for Docking, but makes saving slower)
838 
839 // 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.
840 static const float NAV_WINDOWING_HIGHLIGHT_DELAY            = 0.20f;    // Time before the highlight and screen dimming starts fading in
841 static const float NAV_WINDOWING_LIST_APPEAR_DELAY          = 0.15f;    // Time before the window list starts to appear
842 
843 // Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by backend)
844 static const float WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS = 4.0f;     // Extend outside and inside windows. Affect FindHoveredWindow().
845 static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f;    // Reduce visual noise by only highlighting the border after a certain time.
846 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.
847 
848 // Docking
849 static const float DOCKING_TRANSPARENT_PAYLOAD_ALPHA        = 0.50f;    // For use with io.ConfigDockingTransparentPayload. Apply to Viewport _or_ WindowBg in host viewport.
850 static const float DOCKING_SPLITTER_SIZE                    = 2.0f;
851 
852 //-------------------------------------------------------------------------
853 // [SECTION] FORWARD DECLARATIONS
854 //-------------------------------------------------------------------------
855 
856 static void             SetCurrentWindow(ImGuiWindow* window);
857 static void             FindHoveredWindow();
858 static ImGuiWindow*     CreateNewWindow(const char* name, ImGuiWindowFlags flags);
859 static ImVec2           CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);
860 
861 static void             AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
862 static void             AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
863 
864 // Settings
865 static void             WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
866 static void*            WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
867 static void             WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
868 static void             WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);
869 static void             WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf);
870 
871 // Platform Dependents default implementation for IO functions
872 static const char*      GetClipboardTextFn_DefaultImpl(void* user_data);
873 static void             SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
874 
875 namespace ImGui
876 {
877 // Navigation
878 static void             NavUpdate();
879 static void             NavUpdateWindowing();
880 static void             NavUpdateWindowingOverlay();
881 static void             NavUpdateMoveResult();
882 static void             NavUpdateInitResult();
883 static float            NavUpdatePageUpPageDown();
884 static inline void      NavUpdateAnyRequestFlag();
885 static void             NavEndFrame();
886 static bool             NavScoreItem(ImGuiNavMoveResult* result, ImRect cand);
887 static void             NavApplyItemToResult(ImGuiNavMoveResult* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel);
888 static void             NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id);
889 static ImVec2           NavCalcPreferredRefPos();
890 static void             NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
891 static ImGuiWindow*     NavRestoreLastChildNavWindow(ImGuiWindow* window);
892 static int              FindWindowFocusIndex(ImGuiWindow* window);
893 
894 // Error Checking
895 static void             ErrorCheckNewFrameSanityChecks();
896 static void             ErrorCheckEndFrameSanityChecks();
897 
898 // Misc
899 static void             UpdateSettings();
900 static void             UpdateMouseInputs();
901 static void             UpdateMouseWheel();
902 static void             UpdateTabFocus();
903 static void             UpdateDebugToolItemPicker();
904 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);
905 static void             RenderWindowOuterBorders(ImGuiWindow* window);
906 static void             RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size);
907 static void             RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
908 static void             EndFrameDrawDimmedBackgrounds();
909 
910 // Viewports
911 const ImGuiID           IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHashStr("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter.
912 static ImGuiViewportP*  AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& platform_pos, const ImVec2& size, ImGuiViewportFlags flags);
913 static void             UpdateViewportsNewFrame();
914 static void             UpdateViewportsEndFrame();
915 static void             UpdateSelectWindowViewport(ImGuiWindow* window);
916 static bool             UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport);
917 static bool             UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window);
918 static void             SetCurrentViewport(ImGuiWindow* window, ImGuiViewportP* viewport);
919 static bool             GetWindowAlwaysWantOwnViewport(ImGuiWindow* window);
920 static int              FindPlatformMonitorForPos(const ImVec2& pos);
921 static int              FindPlatformMonitorForRect(const ImRect& r);
922 static void             UpdateViewportPlatformMonitor(ImGuiViewportP* viewport);
923 
924 }
925 
926 //-----------------------------------------------------------------------------
927 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
928 //-----------------------------------------------------------------------------
929 
930 // Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
931 // ImGui::CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext().
932 // 1) Important: globals are not shared across DLL boundaries! If you use DLLs or any form of hot-reloading: you will need to call
933 //    SetCurrentContext() (with the pointer you got from CreateContext) from each unique static/DLL boundary, and after each hot-reloading.
934 //    In your debugger, add GImGui to your watch window and notice how its value changes depending on which location you are currently stepping into.
935 // 2) Important: Dear ImGui functions are not thread-safe because of this pointer.
936 //    If you want thread-safety to allow N threads to access N different contexts, you can:
937 //    - Change this variable to use thread local storage so each thread can refer to a different context, in imconfig.h:
938 //          struct ImGuiContext;
939 //          extern thread_local ImGuiContext* MyImGuiTLS;
940 //          #define GImGui MyImGuiTLS
941 //      And then define MyImGuiTLS in one of your cpp file. Note that thread_local is a C++11 keyword, earlier C++ uses compiler-specific keyword.
942 //    - Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
943 //    - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from different namespace.
944 #ifndef GImGui
945 ImGuiContext*   GImGui = NULL;
946 #endif
947 
948 // Memory Allocator functions. Use SetAllocatorFunctions() to change them.
949 // If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file.
950 // Otherwise, you probably don't want to modify them mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction.
951 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
MallocWrapper(size_t size,void * user_data)952 static void*   MallocWrapper(size_t size, void* user_data)    { IM_UNUSED(user_data); return malloc(size); }
FreeWrapper(void * ptr,void * user_data)953 static void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); free(ptr); }
954 #else
MallocWrapper(size_t size,void * user_data)955 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)956 static void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }
957 #endif
958 
959 static void*  (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper;
960 static void   (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper;
961 static void*    GImAllocatorUserData = NULL;
962 
963 //-----------------------------------------------------------------------------
964 // [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
965 //-----------------------------------------------------------------------------
966 
ImGuiStyle()967 ImGuiStyle::ImGuiStyle()
968 {
969     Alpha                   = 1.0f;             // Global alpha applies to everything in ImGui
970     WindowPadding           = ImVec2(8,8);      // Padding within a window
971     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.
972     WindowBorderSize        = 1.0f;             // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
973     WindowMinSize           = ImVec2(32,32);    // Minimum window size
974     WindowTitleAlign        = ImVec2(0.0f,0.5f);// Alignment for title bar text
975     WindowMenuButtonPosition= ImGuiDir_Left;    // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.
976     ChildRounding           = 0.0f;             // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
977     ChildBorderSize         = 1.0f;             // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
978     PopupRounding           = 0.0f;             // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
979     PopupBorderSize         = 1.0f;             // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
980     FramePadding            = ImVec2(4,3);      // Padding within a framed rectangle (used by most widgets)
981     FrameRounding           = 0.0f;             // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
982     FrameBorderSize         = 0.0f;             // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
983     ItemSpacing             = ImVec2(8,4);      // Horizontal and vertical spacing between widgets/lines
984     ItemInnerSpacing        = ImVec2(4,4);      // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
985     CellPadding             = ImVec2(4,2);      // Padding within a table cell
986     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!
987     IndentSpacing           = 21.0f;            // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
988     ColumnsMinSpacing       = 6.0f;             // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
989     ScrollbarSize           = 14.0f;            // Width of the vertical scrollbar, Height of the horizontal scrollbar
990     ScrollbarRounding       = 9.0f;             // Radius of grab corners rounding for scrollbar
991     GrabMinSize             = 10.0f;            // Minimum width/height of a grab box for slider/scrollbar
992     GrabRounding            = 0.0f;             // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
993     LogSliderDeadzone       = 4.0f;             // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
994     TabRounding             = 4.0f;             // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
995     TabBorderSize           = 0.0f;             // Thickness of border around tabs.
996     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.
997     ColorButtonPosition     = ImGuiDir_Right;   // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
998     ButtonTextAlign         = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
999     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.
1000     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.
1001     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.
1002     MouseCursorScale        = 1.0f;             // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
1003     AntiAliasedLines        = true;             // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU.
1004     AntiAliasedLinesUseTex  = true;             // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering.
1005     AntiAliasedFill         = true;             // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).
1006     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.
1007     CircleSegmentMaxError   = 1.60f;            // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
1008 
1009     // Default theme
1010     ImGui::StyleColorsDark(this);
1011 }
1012 
1013 // 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.
1014 // 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)1015 void ImGuiStyle::ScaleAllSizes(float scale_factor)
1016 {
1017     WindowPadding = ImFloor(WindowPadding * scale_factor);
1018     WindowRounding = ImFloor(WindowRounding * scale_factor);
1019     WindowMinSize = ImFloor(WindowMinSize * scale_factor);
1020     ChildRounding = ImFloor(ChildRounding * scale_factor);
1021     PopupRounding = ImFloor(PopupRounding * scale_factor);
1022     FramePadding = ImFloor(FramePadding * scale_factor);
1023     FrameRounding = ImFloor(FrameRounding * scale_factor);
1024     ItemSpacing = ImFloor(ItemSpacing * scale_factor);
1025     ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
1026     CellPadding = ImFloor(CellPadding * scale_factor);
1027     TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
1028     IndentSpacing = ImFloor(IndentSpacing * scale_factor);
1029     ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
1030     ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
1031     ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
1032     GrabMinSize = ImFloor(GrabMinSize * scale_factor);
1033     GrabRounding = ImFloor(GrabRounding * scale_factor);
1034     LogSliderDeadzone = ImFloor(LogSliderDeadzone * scale_factor);
1035     TabRounding = ImFloor(TabRounding * scale_factor);
1036     TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImFloor(TabMinWidthForCloseButton * scale_factor) : FLT_MAX;
1037     DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
1038     DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
1039     MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
1040 }
1041 
ImGuiIO()1042 ImGuiIO::ImGuiIO()
1043 {
1044     // Most fields are initialized with zero
1045     memset(this, 0, sizeof(*this));
1046     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.
1047 
1048     // Settings
1049     ConfigFlags = ImGuiConfigFlags_None;
1050     BackendFlags = ImGuiBackendFlags_None;
1051     DisplaySize = ImVec2(-1.0f, -1.0f);
1052     DeltaTime = 1.0f / 60.0f;
1053     IniSavingRate = 5.0f;
1054     IniFilename = "imgui.ini";
1055     LogFilename = "imgui_log.txt";
1056     MouseDoubleClickTime = 0.30f;
1057     MouseDoubleClickMaxDist = 6.0f;
1058     for (int i = 0; i < ImGuiKey_COUNT; i++)
1059         KeyMap[i] = -1;
1060     KeyRepeatDelay = 0.275f;
1061     KeyRepeatRate = 0.050f;
1062     UserData = NULL;
1063 
1064     Fonts = NULL;
1065     FontGlobalScale = 1.0f;
1066     FontDefault = NULL;
1067     FontAllowUserScaling = false;
1068     DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1069 
1070     // Docking options (when ImGuiConfigFlags_DockingEnable is set)
1071     ConfigDockingNoSplit = false;
1072     ConfigDockingWithShift = false;
1073     ConfigDockingAlwaysTabBar = false;
1074     ConfigDockingTransparentPayload = false;
1075 
1076     // Viewport options (when ImGuiConfigFlags_ViewportsEnable is set)
1077     ConfigViewportsNoAutoMerge = false;
1078     ConfigViewportsNoTaskBarIcon = false;
1079     ConfigViewportsNoDecoration = true;
1080     ConfigViewportsNoDefaultParent = false;
1081 
1082     // Miscellaneous options
1083     MouseDrawCursor = false;
1084 #ifdef __APPLE__
1085     ConfigMacOSXBehaviors = true;  // Set Mac OS X style defaults based on __APPLE__ compile time flag
1086 #else
1087     ConfigMacOSXBehaviors = false;
1088 #endif
1089     ConfigInputTextCursorBlink = true;
1090     ConfigWindowsResizeFromEdges = true;
1091     ConfigWindowsMoveFromTitleBarOnly = false;
1092     ConfigMemoryCompactTimer = 60.0f;
1093 
1094     // Platform Functions
1095     BackendPlatformName = BackendRendererName = NULL;
1096     BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1097     GetClipboardTextFn = GetClipboardTextFn_DefaultImpl;   // Platform dependent default implementations
1098     SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
1099     ClipboardUserData = NULL;
1100 
1101     // Input (NB: we already have memset zero the entire structure!)
1102     MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1103     MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1104     MouseDragThreshold = 6.0f;
1105     for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1106     for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i]  = KeysDownDurationPrev[i] = -1.0f;
1107     for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
1108 }
1109 
1110 // Pass in translated ASCII characters for text input.
1111 // - with glfw you can get those from the callback set in glfwSetCharCallback()
1112 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(unsigned int c)1113 void ImGuiIO::AddInputCharacter(unsigned int c)
1114 {
1115     if (c != 0)
1116         InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID);
1117 }
1118 
1119 // UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so
1120 // we should save the high surrogate.
AddInputCharacterUTF16(ImWchar16 c)1121 void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c)
1122 {
1123     if (c == 0 && InputQueueSurrogate == 0)
1124         return;
1125 
1126     if ((c & 0xFC00) == 0xD800) // High surrogate, must save
1127     {
1128         if (InputQueueSurrogate != 0)
1129             InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID);
1130         InputQueueSurrogate = c;
1131         return;
1132     }
1133 
1134     ImWchar cp = c;
1135     if (InputQueueSurrogate != 0)
1136     {
1137         if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate
1138             InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID);
1139         else if (IM_UNICODE_CODEPOINT_MAX == (0xFFFF)) // Codepoint will not fit in ImWchar (extra parenthesis around 0xFFFF somehow fixes -Wunreachable-code with Clang)
1140             cp = IM_UNICODE_CODEPOINT_INVALID;
1141         else
1142             cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000);
1143         InputQueueSurrogate = 0;
1144     }
1145     InputQueueCharacters.push_back(cp);
1146 }
1147 
AddInputCharactersUTF8(const char * utf8_chars)1148 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1149 {
1150     while (*utf8_chars != 0)
1151     {
1152         unsigned int c = 0;
1153         utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
1154         if (c != 0)
1155             InputQueueCharacters.push_back((ImWchar)c);
1156     }
1157 }
1158 
ClearInputCharacters()1159 void ImGuiIO::ClearInputCharacters()
1160 {
1161     InputQueueCharacters.resize(0);
1162 }
1163 
1164 //-----------------------------------------------------------------------------
1165 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
1166 //-----------------------------------------------------------------------------
1167 
ImBezierCubicClosestPoint(const ImVec2 & p1,const ImVec2 & p2,const ImVec2 & p3,const ImVec2 & p4,const ImVec2 & p,int num_segments)1168 ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments)
1169 {
1170     IM_ASSERT(num_segments > 0); // Use ImBezierClosestPointCasteljau()
1171     ImVec2 p_last = p1;
1172     ImVec2 p_closest;
1173     float p_closest_dist2 = FLT_MAX;
1174     float t_step = 1.0f / (float)num_segments;
1175     for (int i_step = 1; i_step <= num_segments; i_step++)
1176     {
1177         ImVec2 p_current = ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step);
1178         ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1179         float dist2 = ImLengthSqr(p - p_line);
1180         if (dist2 < p_closest_dist2)
1181         {
1182             p_closest = p_line;
1183             p_closest_dist2 = dist2;
1184         }
1185         p_last = p_current;
1186     }
1187     return p_closest;
1188 }
1189 
1190 // 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)1191 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)
1192 {
1193     float dx = x4 - x1;
1194     float dy = y4 - y1;
1195     float d2 = ((x2 - x4) * dy - (y2 - y4) * dx);
1196     float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
1197     d2 = (d2 >= 0) ? d2 : -d2;
1198     d3 = (d3 >= 0) ? d3 : -d3;
1199     if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))
1200     {
1201         ImVec2 p_current(x4, y4);
1202         ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1203         float dist2 = ImLengthSqr(p - p_line);
1204         if (dist2 < p_closest_dist2)
1205         {
1206             p_closest = p_line;
1207             p_closest_dist2 = dist2;
1208         }
1209         p_last = p_current;
1210     }
1211     else if (level < 10)
1212     {
1213         float x12 = (x1 + x2)*0.5f,       y12 = (y1 + y2)*0.5f;
1214         float x23 = (x2 + x3)*0.5f,       y23 = (y2 + y3)*0.5f;
1215         float x34 = (x3 + x4)*0.5f,       y34 = (y3 + y4)*0.5f;
1216         float x123 = (x12 + x23)*0.5f,    y123 = (y12 + y23)*0.5f;
1217         float x234 = (x23 + x34)*0.5f,    y234 = (y23 + y34)*0.5f;
1218         float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f;
1219         ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);
1220         ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);
1221     }
1222 }
1223 
1224 // tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol
1225 // 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)1226 ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol)
1227 {
1228     IM_ASSERT(tess_tol > 0.0f);
1229     ImVec2 p_last = p1;
1230     ImVec2 p_closest;
1231     float p_closest_dist2 = FLT_MAX;
1232     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);
1233     return p_closest;
1234 }
1235 
ImLineClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & p)1236 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1237 {
1238     ImVec2 ap = p - a;
1239     ImVec2 ab_dir = b - a;
1240     float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1241     if (dot < 0.0f)
1242         return a;
1243     float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1244     if (dot > ab_len_sqr)
1245         return b;
1246     return a + ab_dir * dot / ab_len_sqr;
1247 }
1248 
ImTriangleContainsPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1249 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1250 {
1251     bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1252     bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1253     bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1254     return ((b1 == b2) && (b2 == b3));
1255 }
1256 
ImTriangleBarycentricCoords(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p,float & out_u,float & out_v,float & out_w)1257 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1258 {
1259     ImVec2 v0 = b - a;
1260     ImVec2 v1 = c - a;
1261     ImVec2 v2 = p - a;
1262     const float denom = v0.x * v1.y - v1.x * v0.y;
1263     out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1264     out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1265     out_u = 1.0f - out_v - out_w;
1266 }
1267 
ImTriangleClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1268 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1269 {
1270     ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1271     ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1272     ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1273     float dist2_ab = ImLengthSqr(p - proj_ab);
1274     float dist2_bc = ImLengthSqr(p - proj_bc);
1275     float dist2_ca = ImLengthSqr(p - proj_ca);
1276     float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1277     if (m == dist2_ab)
1278         return proj_ab;
1279     if (m == dist2_bc)
1280         return proj_bc;
1281     return proj_ca;
1282 }
1283 
1284 //-----------------------------------------------------------------------------
1285 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
1286 //-----------------------------------------------------------------------------
1287 
1288 // 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)1289 int ImStricmp(const char* str1, const char* str2)
1290 {
1291     int d;
1292     while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
1293     return d;
1294 }
1295 
ImStrnicmp(const char * str1,const char * str2,size_t count)1296 int ImStrnicmp(const char* str1, const char* str2, size_t count)
1297 {
1298     int d = 0;
1299     while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
1300     return d;
1301 }
1302 
ImStrncpy(char * dst,const char * src,size_t count)1303 void ImStrncpy(char* dst, const char* src, size_t count)
1304 {
1305     if (count < 1)
1306         return;
1307     if (count > 1)
1308         strncpy(dst, src, count - 1);
1309     dst[count - 1] = 0;
1310 }
1311 
ImStrdup(const char * str)1312 char* ImStrdup(const char* str)
1313 {
1314     size_t len = strlen(str);
1315     void* buf = IM_ALLOC(len + 1);
1316     return (char*)memcpy(buf, (const void*)str, len + 1);
1317 }
1318 
ImStrdupcpy(char * dst,size_t * p_dst_size,const char * src)1319 char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
1320 {
1321     size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1;
1322     size_t src_size = strlen(src) + 1;
1323     if (dst_buf_size < src_size)
1324     {
1325         IM_FREE(dst);
1326         dst = (char*)IM_ALLOC(src_size);
1327         if (p_dst_size)
1328             *p_dst_size = src_size;
1329     }
1330     return (char*)memcpy(dst, (const void*)src, src_size);
1331 }
1332 
ImStrchrRange(const char * str,const char * str_end,char c)1333 const char* ImStrchrRange(const char* str, const char* str_end, char c)
1334 {
1335     const char* p = (const char*)memchr(str, (int)c, str_end - str);
1336     return p;
1337 }
1338 
ImStrlenW(const ImWchar * str)1339 int ImStrlenW(const ImWchar* str)
1340 {
1341     //return (int)wcslen((const wchar_t*)str);  // FIXME-OPT: Could use this when wchar_t are 16-bit
1342     int n = 0;
1343     while (*str++) n++;
1344     return n;
1345 }
1346 
1347 // Find end-of-line. Return pointer will point to either first \n, either str_end.
ImStreolRange(const char * str,const char * str_end)1348 const char* ImStreolRange(const char* str, const char* str_end)
1349 {
1350     const char* p = (const char*)memchr(str, '\n', str_end - str);
1351     return p ? p : str_end;
1352 }
1353 
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)1354 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
1355 {
1356     while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1357         buf_mid_line--;
1358     return buf_mid_line;
1359 }
1360 
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)1361 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1362 {
1363     if (!needle_end)
1364         needle_end = needle + strlen(needle);
1365 
1366     const char un0 = (char)toupper(*needle);
1367     while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1368     {
1369         if (toupper(*haystack) == un0)
1370         {
1371             const char* b = needle + 1;
1372             for (const char* a = haystack + 1; b < needle_end; a++, b++)
1373                 if (toupper(*a) != toupper(*b))
1374                     break;
1375             if (b == needle_end)
1376                 return haystack;
1377         }
1378         haystack++;
1379     }
1380     return NULL;
1381 }
1382 
1383 // 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)1384 void ImStrTrimBlanks(char* buf)
1385 {
1386     char* p = buf;
1387     while (p[0] == ' ' || p[0] == '\t')     // Leading blanks
1388         p++;
1389     char* p_start = p;
1390     while (*p != 0)                         // Find end of string
1391         p++;
1392     while (p > p_start && (p[-1] == ' ' || p[-1] == '\t'))  // Trailing blanks
1393         p--;
1394     if (p_start != buf)                     // Copy memory if we had leading blanks
1395         memmove(buf, p_start, p - p_start);
1396     buf[p - p_start] = 0;                   // Zero terminate
1397 }
1398 
ImStrSkipBlank(const char * str)1399 const char* ImStrSkipBlank(const char* str)
1400 {
1401     while (str[0] == ' ' || str[0] == '\t')
1402         str++;
1403     return str;
1404 }
1405 
1406 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1407 // 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.
1408 // B) When buf==NULL vsnprintf() will return the output size.
1409 #ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1410 
1411 // We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h)
1412 // You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1413 // and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are
1414 // designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.)
1415 #ifdef IMGUI_USE_STB_SPRINTF
1416 #define STB_SPRINTF_IMPLEMENTATION
1417 #include "stb_sprintf.h"
1418 #endif
1419 
1420 #if defined(_MSC_VER) && !defined(vsnprintf)
1421 #define vsnprintf _vsnprintf
1422 #endif
1423 
ImFormatString(char * buf,size_t buf_size,const char * fmt,...)1424 int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1425 {
1426     va_list args;
1427     va_start(args, fmt);
1428 #ifdef IMGUI_USE_STB_SPRINTF
1429     int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1430 #else
1431     int w = vsnprintf(buf, buf_size, fmt, args);
1432 #endif
1433     va_end(args);
1434     if (buf == NULL)
1435         return w;
1436     if (w == -1 || w >= (int)buf_size)
1437         w = (int)buf_size - 1;
1438     buf[w] = 0;
1439     return w;
1440 }
1441 
ImFormatStringV(char * buf,size_t buf_size,const char * fmt,va_list args)1442 int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1443 {
1444 #ifdef IMGUI_USE_STB_SPRINTF
1445     int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1446 #else
1447     int w = vsnprintf(buf, buf_size, fmt, args);
1448 #endif
1449     if (buf == NULL)
1450         return w;
1451     if (w == -1 || w >= (int)buf_size)
1452         w = (int)buf_size - 1;
1453     buf[w] = 0;
1454     return w;
1455 }
1456 #endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1457 
1458 // CRC32 needs a 1KB lookup table (not cache friendly)
1459 // Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
1460 // - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
1461 static const ImU32 GCrc32LookupTable[256] =
1462 {
1463     0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
1464     0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
1465     0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
1466     0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
1467     0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
1468     0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
1469     0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
1470     0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
1471     0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
1472     0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
1473     0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
1474     0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
1475     0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
1476     0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
1477     0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
1478     0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
1479 };
1480 
1481 // Known size hash
1482 // It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
1483 // 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)1484 ImGuiID ImHashData(const void* data_p, size_t data_size, ImU32 seed)
1485 {
1486     ImU32 crc = ~seed;
1487     const unsigned char* data = (const unsigned char*)data_p;
1488     const ImU32* crc32_lut = GCrc32LookupTable;
1489     while (data_size-- != 0)
1490         crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
1491     return ~crc;
1492 }
1493 
1494 // Zero-terminated string hash, with support for ### to reset back to seed value
1495 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1496 // Because this syntax is rarely used we are optimizing for the common case.
1497 // - If we reach ### in the string we discard the hash so far and reset to the seed.
1498 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
1499 // 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)1500 ImGuiID ImHashStr(const char* data_p, size_t data_size, ImU32 seed)
1501 {
1502     seed = ~seed;
1503     ImU32 crc = seed;
1504     const unsigned char* data = (const unsigned char*)data_p;
1505     const ImU32* crc32_lut = GCrc32LookupTable;
1506     if (data_size != 0)
1507     {
1508         while (data_size-- != 0)
1509         {
1510             unsigned char c = *data++;
1511             if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')
1512                 crc = seed;
1513             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1514         }
1515     }
1516     else
1517     {
1518         while (unsigned char c = *data++)
1519         {
1520             if (c == '#' && data[0] == '#' && data[1] == '#')
1521                 crc = seed;
1522             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1523         }
1524     }
1525     return ~crc;
1526 }
1527 
1528 //-----------------------------------------------------------------------------
1529 // [SECTION] MISC HELPERS/UTILITIES (File functions)
1530 //-----------------------------------------------------------------------------
1531 
1532 // Default file functions
1533 #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
1534 
ImFileOpen(const char * filename,const char * mode)1535 ImFileHandle ImFileOpen(const char* filename, const char* mode)
1536 {
1537 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__)
1538     // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.
1539     // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32!
1540     const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
1541     const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
1542     ImVector<ImWchar> buf;
1543     buf.resize(filename_wsize + mode_wsize);
1544     ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, (wchar_t*)&buf[0], filename_wsize);
1545     ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, (wchar_t*)&buf[filename_wsize], mode_wsize);
1546     return ::_wfopen((const wchar_t*)&buf[0], (const wchar_t*)&buf[filename_wsize]);
1547 #else
1548     return fopen(filename, mode);
1549 #endif
1550 }
1551 
1552 // 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)1553 bool    ImFileClose(ImFileHandle f)     { return fclose(f) == 0; }
ImFileGetSize(ImFileHandle f)1554 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)1555 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)1556 ImU64   ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f)    { return fwrite(data, (size_t)sz, (size_t)count, f); }
1557 #endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
1558 
1559 // Helper: Load file content into memory
1560 // Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree()
1561 // 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)1562 void*   ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes)
1563 {
1564     IM_ASSERT(filename && mode);
1565     if (out_file_size)
1566         *out_file_size = 0;
1567 
1568     ImFileHandle f;
1569     if ((f = ImFileOpen(filename, mode)) == NULL)
1570         return NULL;
1571 
1572     size_t file_size = (size_t)ImFileGetSize(f);
1573     if (file_size == (size_t)-1)
1574     {
1575         ImFileClose(f);
1576         return NULL;
1577     }
1578 
1579     void* file_data = IM_ALLOC(file_size + padding_bytes);
1580     if (file_data == NULL)
1581     {
1582         ImFileClose(f);
1583         return NULL;
1584     }
1585     if (ImFileRead(file_data, 1, file_size, f) != file_size)
1586     {
1587         ImFileClose(f);
1588         IM_FREE(file_data);
1589         return NULL;
1590     }
1591     if (padding_bytes > 0)
1592         memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
1593 
1594     ImFileClose(f);
1595     if (out_file_size)
1596         *out_file_size = file_size;
1597 
1598     return file_data;
1599 }
1600 
1601 //-----------------------------------------------------------------------------
1602 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
1603 //-----------------------------------------------------------------------------
1604 
1605 // Convert UTF-8 to 32-bit character, process single character input.
1606 // A nearly-branchless UTF-8 decoder, based on work of Christopher Wellons (https://github.com/skeeto/branchless-utf8).
1607 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)1608 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1609 {
1610     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 };
1611     static const int masks[]  = { 0x00, 0x7f, 0x1f, 0x0f, 0x07 };
1612     static const uint32_t mins[] = { 0x400000, 0, 0x80, 0x800, 0x10000 };
1613     static const int shiftc[] = { 0, 18, 12, 6, 0 };
1614     static const int shifte[] = { 0, 6, 4, 2, 0 };
1615     int len = lengths[*(const unsigned char*)in_text >> 3];
1616     int wanted = len + !len;
1617 
1618     if (in_text_end == NULL)
1619         in_text_end = in_text + wanted; // Max length, nulls will be taken into account.
1620 
1621     // Copy at most 'len' bytes, stop copying at 0 or past in_text_end. Branch predictor does a good job here,
1622     // so it is fast even with excessive branching.
1623     unsigned char s[4];
1624     s[0] = in_text + 0 < in_text_end ? in_text[0] : 0;
1625     s[1] = in_text + 1 < in_text_end ? in_text[1] : 0;
1626     s[2] = in_text + 2 < in_text_end ? in_text[2] : 0;
1627     s[3] = in_text + 3 < in_text_end ? in_text[3] : 0;
1628 
1629     // Assume a four-byte character and load four bytes. Unused bits are shifted out.
1630     *out_char  = (uint32_t)(s[0] & masks[len]) << 18;
1631     *out_char |= (uint32_t)(s[1] & 0x3f) << 12;
1632     *out_char |= (uint32_t)(s[2] & 0x3f) <<  6;
1633     *out_char |= (uint32_t)(s[3] & 0x3f) <<  0;
1634     *out_char >>= shiftc[len];
1635 
1636     // Accumulate the various error conditions.
1637     int e = 0;
1638     e  = (*out_char < mins[len]) << 6; // non-canonical encoding
1639     e |= ((*out_char >> 11) == 0x1b) << 7;  // surrogate half?
1640     e |= (*out_char > IM_UNICODE_CODEPOINT_MAX) << 8;  // out of range?
1641     e |= (s[1] & 0xc0) >> 2;
1642     e |= (s[2] & 0xc0) >> 4;
1643     e |= (s[3]       ) >> 6;
1644     e ^= 0x2a; // top two bits of each tail byte correct?
1645     e >>= shifte[len];
1646 
1647     if (e)
1648     {
1649         // No bytes are consumed when *in_text == 0 || in_text == in_text_end.
1650         // One byte is consumed in case of invalid first byte of in_text.
1651         // All available bytes (at most `len` bytes) are consumed on incomplete/invalid second to last bytes.
1652         // Invalid or incomplete input may consume less bytes than wanted, therefore every byte has to be inspected in s.
1653         wanted = ImMin(wanted, !!s[0] + !!s[1] + !!s[2] + !!s[3]);
1654         *out_char = IM_UNICODE_CODEPOINT_INVALID;
1655     }
1656 
1657     return wanted;
1658 }
1659 
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1660 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1661 {
1662     ImWchar* buf_out = buf;
1663     ImWchar* buf_end = buf + buf_size;
1664     while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
1665     {
1666         unsigned int c;
1667         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1668         if (c == 0)
1669             break;
1670         *buf_out++ = (ImWchar)c;
1671     }
1672     *buf_out = 0;
1673     if (in_text_remaining)
1674         *in_text_remaining = in_text;
1675     return (int)(buf_out - buf);
1676 }
1677 
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1678 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1679 {
1680     int char_count = 0;
1681     while ((!in_text_end || in_text < in_text_end) && *in_text)
1682     {
1683         unsigned int c;
1684         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1685         if (c == 0)
1686             break;
1687         char_count++;
1688     }
1689     return char_count;
1690 }
1691 
1692 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8(char * buf,int buf_size,unsigned int c)1693 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1694 {
1695     if (c < 0x80)
1696     {
1697         buf[0] = (char)c;
1698         return 1;
1699     }
1700     if (c < 0x800)
1701     {
1702         if (buf_size < 2) return 0;
1703         buf[0] = (char)(0xc0 + (c >> 6));
1704         buf[1] = (char)(0x80 + (c & 0x3f));
1705         return 2;
1706     }
1707     if (c < 0x10000)
1708     {
1709         if (buf_size < 3) return 0;
1710         buf[0] = (char)(0xe0 + (c >> 12));
1711         buf[1] = (char)(0x80 + ((c >> 6) & 0x3f));
1712         buf[2] = (char)(0x80 + ((c ) & 0x3f));
1713         return 3;
1714     }
1715     if (c <= 0x10FFFF)
1716     {
1717         if (buf_size < 4) return 0;
1718         buf[0] = (char)(0xf0 + (c >> 18));
1719         buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1720         buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1721         buf[3] = (char)(0x80 + ((c ) & 0x3f));
1722         return 4;
1723     }
1724     // Invalid code point, the max unicode is 0x10FFFF
1725     return 0;
1726 }
1727 
1728 // Not optimal but we very rarely use this function.
ImTextCountUtf8BytesFromChar(const char * in_text,const char * in_text_end)1729 int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
1730 {
1731     unsigned int unused = 0;
1732     return ImTextCharFromUtf8(&unused, in_text, in_text_end);
1733 }
1734 
ImTextCountUtf8BytesFromChar(unsigned int c)1735 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1736 {
1737     if (c < 0x80) return 1;
1738     if (c < 0x800) return 2;
1739     if (c < 0x10000) return 3;
1740     if (c <= 0x10FFFF) return 4;
1741     return 3;
1742 }
1743 
ImTextStrToUtf8(char * buf,int buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1744 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1745 {
1746     char* buf_out = buf;
1747     const char* buf_end = buf + buf_size;
1748     while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
1749     {
1750         unsigned int c = (unsigned int)(*in_text++);
1751         if (c < 0x80)
1752             *buf_out++ = (char)c;
1753         else
1754             buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end - buf_out - 1), c);
1755     }
1756     *buf_out = 0;
1757     return (int)(buf_out - buf);
1758 }
1759 
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1760 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1761 {
1762     int bytes_count = 0;
1763     while ((!in_text_end || in_text < in_text_end) && *in_text)
1764     {
1765         unsigned int c = (unsigned int)(*in_text++);
1766         if (c < 0x80)
1767             bytes_count++;
1768         else
1769             bytes_count += ImTextCountUtf8BytesFromChar(c);
1770     }
1771     return bytes_count;
1772 }
1773 
1774 //-----------------------------------------------------------------------------
1775 // [SECTION] MISC HELPERS/UTILITIES (Color functions)
1776 // Note: The Convert functions are early design which are not consistent with other API.
1777 //-----------------------------------------------------------------------------
1778 
ImAlphaBlendColors(ImU32 col_a,ImU32 col_b)1779 IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b)
1780 {
1781     float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;
1782     int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);
1783     int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);
1784     int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);
1785     return IM_COL32(r, g, b, 0xFF);
1786 }
1787 
ColorConvertU32ToFloat4(ImU32 in)1788 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1789 {
1790     float s = 1.0f / 255.0f;
1791     return ImVec4(
1792         ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1793         ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1794         ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1795         ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1796 }
1797 
ColorConvertFloat4ToU32(const ImVec4 & in)1798 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1799 {
1800     ImU32 out;
1801     out  = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1802     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1803     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1804     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1805     return out;
1806 }
1807 
1808 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1809 // 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)1810 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1811 {
1812     float K = 0.f;
1813     if (g < b)
1814     {
1815         ImSwap(g, b);
1816         K = -1.f;
1817     }
1818     if (r < g)
1819     {
1820         ImSwap(r, g);
1821         K = -2.f / 6.f - K;
1822     }
1823 
1824     const float chroma = r - (g < b ? g : b);
1825     out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
1826     out_s = chroma / (r + 1e-20f);
1827     out_v = r;
1828 }
1829 
1830 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1831 // 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)1832 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1833 {
1834     if (s == 0.0f)
1835     {
1836         // gray
1837         out_r = out_g = out_b = v;
1838         return;
1839     }
1840 
1841     h = ImFmod(h, 1.0f) / (60.0f / 360.0f);
1842     int   i = (int)h;
1843     float f = h - (float)i;
1844     float p = v * (1.0f - s);
1845     float q = v * (1.0f - s * f);
1846     float t = v * (1.0f - s * (1.0f - f));
1847 
1848     switch (i)
1849     {
1850     case 0: out_r = v; out_g = t; out_b = p; break;
1851     case 1: out_r = q; out_g = v; out_b = p; break;
1852     case 2: out_r = p; out_g = v; out_b = t; break;
1853     case 3: out_r = p; out_g = q; out_b = v; break;
1854     case 4: out_r = t; out_g = p; out_b = v; break;
1855     case 5: default: out_r = v; out_g = p; out_b = q; break;
1856     }
1857 }
1858 
1859 //-----------------------------------------------------------------------------
1860 // [SECTION] ImGuiStorage
1861 // Helper: Key->value storage
1862 //-----------------------------------------------------------------------------
1863 
1864 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair> & data,ImGuiID key)1865 static ImGuiStorage::ImGuiStoragePair* LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair>& data, ImGuiID key)
1866 {
1867     ImGuiStorage::ImGuiStoragePair* first = data.Data;
1868     ImGuiStorage::ImGuiStoragePair* last = data.Data + data.Size;
1869     size_t count = (size_t)(last - first);
1870     while (count > 0)
1871     {
1872         size_t count2 = count >> 1;
1873         ImGuiStorage::ImGuiStoragePair* mid = first + count2;
1874         if (mid->key < key)
1875         {
1876             first = ++mid;
1877             count -= count2 + 1;
1878         }
1879         else
1880         {
1881             count = count2;
1882         }
1883     }
1884     return first;
1885 }
1886 
1887 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
BuildSortByKey()1888 void ImGuiStorage::BuildSortByKey()
1889 {
1890     struct StaticFunc
1891     {
1892         static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)
1893         {
1894             // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1895             if (((const ImGuiStoragePair*)lhs)->key > ((const ImGuiStoragePair*)rhs)->key) return +1;
1896             if (((const ImGuiStoragePair*)lhs)->key < ((const ImGuiStoragePair*)rhs)->key) return -1;
1897             return 0;
1898         }
1899     };
1900     if (Data.Size > 1)
1901         ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairCompareByID);
1902 }
1903 
GetInt(ImGuiID key,int default_val) const1904 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1905 {
1906     ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1907     if (it == Data.end() || it->key != key)
1908         return default_val;
1909     return it->val_i;
1910 }
1911 
GetBool(ImGuiID key,bool default_val) const1912 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1913 {
1914     return GetInt(key, default_val ? 1 : 0) != 0;
1915 }
1916 
GetFloat(ImGuiID key,float default_val) const1917 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1918 {
1919     ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1920     if (it == Data.end() || it->key != key)
1921         return default_val;
1922     return it->val_f;
1923 }
1924 
GetVoidPtr(ImGuiID key) const1925 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1926 {
1927     ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1928     if (it == Data.end() || it->key != key)
1929         return NULL;
1930     return it->val_p;
1931 }
1932 
1933 // 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)1934 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1935 {
1936     ImGuiStoragePair* it = LowerBound(Data, key);
1937     if (it == Data.end() || it->key != key)
1938         it = Data.insert(it, ImGuiStoragePair(key, default_val));
1939     return &it->val_i;
1940 }
1941 
GetBoolRef(ImGuiID key,bool default_val)1942 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1943 {
1944     return (bool*)GetIntRef(key, default_val ? 1 : 0);
1945 }
1946 
GetFloatRef(ImGuiID key,float default_val)1947 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1948 {
1949     ImGuiStoragePair* it = LowerBound(Data, key);
1950     if (it == Data.end() || it->key != key)
1951         it = Data.insert(it, ImGuiStoragePair(key, default_val));
1952     return &it->val_f;
1953 }
1954 
GetVoidPtrRef(ImGuiID key,void * default_val)1955 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1956 {
1957     ImGuiStoragePair* it = LowerBound(Data, key);
1958     if (it == Data.end() || it->key != key)
1959         it = Data.insert(it, ImGuiStoragePair(key, default_val));
1960     return &it->val_p;
1961 }
1962 
1963 // 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)1964 void ImGuiStorage::SetInt(ImGuiID key, int val)
1965 {
1966     ImGuiStoragePair* it = LowerBound(Data, key);
1967     if (it == Data.end() || it->key != key)
1968     {
1969         Data.insert(it, ImGuiStoragePair(key, val));
1970         return;
1971     }
1972     it->val_i = val;
1973 }
1974 
SetBool(ImGuiID key,bool val)1975 void ImGuiStorage::SetBool(ImGuiID key, bool val)
1976 {
1977     SetInt(key, val ? 1 : 0);
1978 }
1979 
SetFloat(ImGuiID key,float val)1980 void ImGuiStorage::SetFloat(ImGuiID key, float val)
1981 {
1982     ImGuiStoragePair* it = LowerBound(Data, key);
1983     if (it == Data.end() || it->key != key)
1984     {
1985         Data.insert(it, ImGuiStoragePair(key, val));
1986         return;
1987     }
1988     it->val_f = val;
1989 }
1990 
SetVoidPtr(ImGuiID key,void * val)1991 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1992 {
1993     ImGuiStoragePair* it = LowerBound(Data, key);
1994     if (it == Data.end() || it->key != key)
1995     {
1996         Data.insert(it, ImGuiStoragePair(key, val));
1997         return;
1998     }
1999     it->val_p = val;
2000 }
2001 
SetAllInt(int v)2002 void ImGuiStorage::SetAllInt(int v)
2003 {
2004     for (int i = 0; i < Data.Size; i++)
2005         Data[i].val_i = v;
2006 }
2007 
2008 //-----------------------------------------------------------------------------
2009 // [SECTION] ImGuiTextFilter
2010 //-----------------------------------------------------------------------------
2011 
2012 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)2013 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
2014 {
2015     if (default_filter)
2016     {
2017         ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
2018         Build();
2019     }
2020     else
2021     {
2022         InputBuf[0] = 0;
2023         CountGrep = 0;
2024     }
2025 }
2026 
Draw(const char * label,float width)2027 bool ImGuiTextFilter::Draw(const char* label, float width)
2028 {
2029     if (width != 0.0f)
2030         ImGui::SetNextItemWidth(width);
2031     bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
2032     if (value_changed)
2033         Build();
2034     return value_changed;
2035 }
2036 
split(char separator,ImVector<ImGuiTextRange> * out) const2037 void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const
2038 {
2039     out->resize(0);
2040     const char* wb = b;
2041     const char* we = wb;
2042     while (we < e)
2043     {
2044         if (*we == separator)
2045         {
2046             out->push_back(ImGuiTextRange(wb, we));
2047             wb = we + 1;
2048         }
2049         we++;
2050     }
2051     if (wb != we)
2052         out->push_back(ImGuiTextRange(wb, we));
2053 }
2054 
Build()2055 void ImGuiTextFilter::Build()
2056 {
2057     Filters.resize(0);
2058     ImGuiTextRange input_range(InputBuf, InputBuf + strlen(InputBuf));
2059     input_range.split(',', &Filters);
2060 
2061     CountGrep = 0;
2062     for (int i = 0; i != Filters.Size; i++)
2063     {
2064         ImGuiTextRange& f = Filters[i];
2065         while (f.b < f.e && ImCharIsBlankA(f.b[0]))
2066             f.b++;
2067         while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
2068             f.e--;
2069         if (f.empty())
2070             continue;
2071         if (Filters[i].b[0] != '-')
2072             CountGrep += 1;
2073     }
2074 }
2075 
PassFilter(const char * text,const char * text_end) const2076 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
2077 {
2078     if (Filters.empty())
2079         return true;
2080 
2081     if (text == NULL)
2082         text = "";
2083 
2084     for (int i = 0; i != Filters.Size; i++)
2085     {
2086         const ImGuiTextRange& f = Filters[i];
2087         if (f.empty())
2088             continue;
2089         if (f.b[0] == '-')
2090         {
2091             // Subtract
2092             if (ImStristr(text, text_end, f.b + 1, f.e) != NULL)
2093                 return false;
2094         }
2095         else
2096         {
2097             // Grep
2098             if (ImStristr(text, text_end, f.b, f.e) != NULL)
2099                 return true;
2100         }
2101     }
2102 
2103     // Implicit * grep
2104     if (CountGrep == 0)
2105         return true;
2106 
2107     return false;
2108 }
2109 
2110 //-----------------------------------------------------------------------------
2111 // [SECTION] ImGuiTextBuffer
2112 //-----------------------------------------------------------------------------
2113 
2114 // On some platform vsnprintf() takes va_list by reference and modifies it.
2115 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
2116 #ifndef va_copy
2117 #if defined(__GNUC__) || defined(__clang__)
2118 #define va_copy(dest, src) __builtin_va_copy(dest, src)
2119 #else
2120 #define va_copy(dest, src) (dest = src)
2121 #endif
2122 #endif
2123 
2124 char ImGuiTextBuffer::EmptyString[1] = { 0 };
2125 
append(const char * str,const char * str_end)2126 void ImGuiTextBuffer::append(const char* str, const char* str_end)
2127 {
2128     int len = str_end ? (int)(str_end - str) : (int)strlen(str);
2129 
2130     // Add zero-terminator the first time
2131     const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2132     const int needed_sz = write_off + len;
2133     if (write_off + len >= Buf.Capacity)
2134     {
2135         int new_capacity = Buf.Capacity * 2;
2136         Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2137     }
2138 
2139     Buf.resize(needed_sz);
2140     memcpy(&Buf[write_off - 1], str, (size_t)len);
2141     Buf[write_off - 1 + len] = 0;
2142 }
2143 
appendf(const char * fmt,...)2144 void ImGuiTextBuffer::appendf(const char* fmt, ...)
2145 {
2146     va_list args;
2147     va_start(args, fmt);
2148     appendfv(fmt, args);
2149     va_end(args);
2150 }
2151 
2152 // Helper: Text buffer for logging/accumulating text
appendfv(const char * fmt,va_list args)2153 void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
2154 {
2155     va_list args_copy;
2156     va_copy(args_copy, args);
2157 
2158     int len = ImFormatStringV(NULL, 0, fmt, args);         // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
2159     if (len <= 0)
2160     {
2161         va_end(args_copy);
2162         return;
2163     }
2164 
2165     // Add zero-terminator the first time
2166     const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2167     const int needed_sz = write_off + len;
2168     if (write_off + len >= Buf.Capacity)
2169     {
2170         int new_capacity = Buf.Capacity * 2;
2171         Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2172     }
2173 
2174     Buf.resize(needed_sz);
2175     ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
2176     va_end(args_copy);
2177 }
2178 
2179 //-----------------------------------------------------------------------------
2180 // [SECTION] ImGuiListClipper
2181 // This is currently not as flexible/powerful as it should be and really confusing/spaghetti, mostly because we changed
2182 // the API mid-way through development and support two ways to using the clipper, needs some rework (see TODO)
2183 //-----------------------------------------------------------------------------
2184 
2185 // FIXME-TABLE: This prevents us from using ImGuiListClipper _inside_ a table cell.
2186 // The problem we have is that without a Begin/End scheme for rows using the clipper is ambiguous.
GetSkipItemForListClipping()2187 static bool GetSkipItemForListClipping()
2188 {
2189     ImGuiContext& g = *GImGui;
2190     return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems);
2191 }
2192 
2193 // Helper to calculate coarse clipping of large list of evenly sized items.
2194 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
2195 // 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)2196 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
2197 {
2198     ImGuiContext& g = *GImGui;
2199     ImGuiWindow* window = g.CurrentWindow;
2200     if (g.LogEnabled)
2201     {
2202         // If logging is active, do not perform any clipping
2203         *out_items_display_start = 0;
2204         *out_items_display_end = items_count;
2205         return;
2206     }
2207     if (GetSkipItemForListClipping())
2208     {
2209         *out_items_display_start = *out_items_display_end = 0;
2210         return;
2211     }
2212 
2213     // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect
2214     ImRect unclipped_rect = window->ClipRect;
2215     if (g.NavMoveRequest)
2216         unclipped_rect.Add(g.NavScoringRect);
2217     if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId)
2218         unclipped_rect.Add(ImRect(window->Pos + window->NavRectRel[0].Min, window->Pos + window->NavRectRel[0].Max));
2219 
2220     const ImVec2 pos = window->DC.CursorPos;
2221     int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
2222     int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
2223 
2224     // When performing a navigation request, ensure we have one item extra in the direction we are moving to
2225     if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up)
2226         start--;
2227     if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down)
2228         end++;
2229 
2230     start = ImClamp(start, 0, items_count);
2231     end = ImClamp(end + 1, start, items_count);
2232     *out_items_display_start = start;
2233     *out_items_display_end = end;
2234 }
2235 
SetCursorPosYAndSetupForPrevLine(float pos_y,float line_height)2236 static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height)
2237 {
2238     // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
2239     // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
2240     // The clipper should probably have a 4th step to display the last item in a regular manner.
2241     ImGuiContext& g = *GImGui;
2242     ImGuiWindow* window = g.CurrentWindow;
2243     float off_y = pos_y - window->DC.CursorPos.y;
2244     window->DC.CursorPos.y = pos_y;
2245     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y);
2246     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.
2247     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.
2248     if (ImGuiOldColumns* columns = window->DC.CurrentColumns)
2249         columns->LineMinY = window->DC.CursorPos.y;                         // Setting this so that cell Y position are set properly
2250     if (ImGuiTable* table = g.CurrentTable)
2251     {
2252         if (table->IsInsideRow)
2253             ImGui::TableEndRow(table);
2254         table->RowPosY2 = window->DC.CursorPos.y;
2255         const int row_increase = (int)((off_y / line_height) + 0.5f);
2256         //table->CurrentRow += row_increase; // Can't do without fixing TableEndRow()
2257         table->RowBgColorCounter += row_increase;
2258     }
2259 }
2260 
ImGuiListClipper()2261 ImGuiListClipper::ImGuiListClipper()
2262 {
2263     memset(this, 0, sizeof(*this));
2264     ItemsCount = -1;
2265 }
2266 
~ImGuiListClipper()2267 ImGuiListClipper::~ImGuiListClipper()
2268 {
2269     IM_ASSERT(ItemsCount == -1 && "Forgot to call End(), or to Step() until false?");
2270 }
2271 
2272 // Use case A: Begin() called from constructor with items_height<0, then called again from Step() in StepNo 1
2273 // Use case B: Begin() called from constructor with items_height>0
2274 // 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)2275 void ImGuiListClipper::Begin(int items_count, float items_height)
2276 {
2277     ImGuiContext& g = *GImGui;
2278     ImGuiWindow* window = g.CurrentWindow;
2279 
2280     if (ImGuiTable* table = g.CurrentTable)
2281         if (table->IsInsideRow)
2282             ImGui::TableEndRow(table);
2283 
2284     StartPosY = window->DC.CursorPos.y;
2285     ItemsHeight = items_height;
2286     ItemsCount = items_count;
2287     ItemsFrozen = 0;
2288     StepNo = 0;
2289     DisplayStart = -1;
2290     DisplayEnd = 0;
2291 }
2292 
End()2293 void ImGuiListClipper::End()
2294 {
2295     if (ItemsCount < 0) // Already ended
2296         return;
2297 
2298     // 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.
2299     if (ItemsCount < INT_MAX && DisplayStart >= 0)
2300         SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight);
2301     ItemsCount = -1;
2302     StepNo = 3;
2303 }
2304 
Step()2305 bool ImGuiListClipper::Step()
2306 {
2307     ImGuiContext& g = *GImGui;
2308     ImGuiWindow* window = g.CurrentWindow;
2309 
2310     ImGuiTable* table = g.CurrentTable;
2311     if (table && table->IsInsideRow)
2312         ImGui::TableEndRow(table);
2313 
2314     // No items
2315     if (ItemsCount == 0 || GetSkipItemForListClipping())
2316     {
2317         End();
2318         return false;
2319     }
2320 
2321     // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height)
2322     if (StepNo == 0)
2323     {
2324         // While we are in frozen row state, keep displaying items one by one, unclipped
2325         // FIXME: Could be stored as a table-agnostic state.
2326         if (table != NULL && !table->IsUnfrozenRows)
2327         {
2328             DisplayStart = ItemsFrozen;
2329             DisplayEnd = ItemsFrozen + 1;
2330             ItemsFrozen++;
2331             return true;
2332         }
2333 
2334         StartPosY = window->DC.CursorPos.y;
2335         if (ItemsHeight <= 0.0f)
2336         {
2337             // Submit the first item so we can measure its height (generally it is 0..1)
2338             DisplayStart = ItemsFrozen;
2339             DisplayEnd = ItemsFrozen + 1;
2340             StepNo = 1;
2341             return true;
2342         }
2343 
2344         // Already has item height (given by user in Begin): skip to calculating step
2345         DisplayStart = DisplayEnd;
2346         StepNo = 2;
2347     }
2348 
2349     // Step 1: the clipper infer height from first element
2350     if (StepNo == 1)
2351     {
2352         IM_ASSERT(ItemsHeight <= 0.0f);
2353         if (table)
2354         {
2355             const float pos_y1 = table->RowPosY1;   // Using this instead of StartPosY to handle clipper straddling the frozen row
2356             const float pos_y2 = table->RowPosY2;   // Using this instead of CursorPos.y to take account of tallest cell.
2357             ItemsHeight = pos_y2 - pos_y1;
2358             window->DC.CursorPos.y = pos_y2;
2359         }
2360         else
2361         {
2362             ItemsHeight = window->DC.CursorPos.y - StartPosY;
2363         }
2364         IM_ASSERT(ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!");
2365         StepNo = 2;
2366     }
2367 
2368     // Reached end of list
2369     if (DisplayEnd >= ItemsCount)
2370     {
2371         End();
2372         return false;
2373     }
2374 
2375     // Step 2: calculate the actual range of elements to display, and position the cursor before the first element
2376     if (StepNo == 2)
2377     {
2378         IM_ASSERT(ItemsHeight > 0.0f);
2379 
2380         int already_submitted = DisplayEnd;
2381         ImGui::CalcListClipping(ItemsCount - already_submitted, ItemsHeight, &DisplayStart, &DisplayEnd);
2382         DisplayStart += already_submitted;
2383         DisplayEnd += already_submitted;
2384 
2385         // Seek cursor
2386         if (DisplayStart > already_submitted)
2387             SetCursorPosYAndSetupForPrevLine(StartPosY + (DisplayStart - ItemsFrozen) * ItemsHeight, ItemsHeight);
2388 
2389         StepNo = 3;
2390         return true;
2391     }
2392 
2393     // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd),
2394     // Advance the cursor to the end of the list and then returns 'false' to end the loop.
2395     if (StepNo == 3)
2396     {
2397         // Seek cursor
2398         if (ItemsCount < INT_MAX)
2399             SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight); // advance cursor
2400         ItemsCount = -1;
2401         return false;
2402     }
2403 
2404     IM_ASSERT(0);
2405     return false;
2406 }
2407 
2408 //-----------------------------------------------------------------------------
2409 // [SECTION] STYLING
2410 //-----------------------------------------------------------------------------
2411 
GetStyle()2412 ImGuiStyle& ImGui::GetStyle()
2413 {
2414     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
2415     return GImGui->Style;
2416 }
2417 
GetColorU32(ImGuiCol idx,float alpha_mul)2418 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
2419 {
2420     ImGuiStyle& style = GImGui->Style;
2421     ImVec4 c = style.Colors[idx];
2422     c.w *= style.Alpha * alpha_mul;
2423     return ColorConvertFloat4ToU32(c);
2424 }
2425 
GetColorU32(const ImVec4 & col)2426 ImU32 ImGui::GetColorU32(const ImVec4& col)
2427 {
2428     ImGuiStyle& style = GImGui->Style;
2429     ImVec4 c = col;
2430     c.w *= style.Alpha;
2431     return ColorConvertFloat4ToU32(c);
2432 }
2433 
GetStyleColorVec4(ImGuiCol idx)2434 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
2435 {
2436     ImGuiStyle& style = GImGui->Style;
2437     return style.Colors[idx];
2438 }
2439 
GetColorU32(ImU32 col)2440 ImU32 ImGui::GetColorU32(ImU32 col)
2441 {
2442     ImGuiStyle& style = GImGui->Style;
2443     if (style.Alpha >= 1.0f)
2444         return col;
2445     ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
2446     a = (ImU32)(a * style.Alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
2447     return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
2448 }
2449 
2450 // 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)2451 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
2452 {
2453     ImGuiContext& g = *GImGui;
2454     ImGuiColorMod backup;
2455     backup.Col = idx;
2456     backup.BackupValue = g.Style.Colors[idx];
2457     g.ColorStack.push_back(backup);
2458     g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
2459 }
2460 
PushStyleColor(ImGuiCol idx,const ImVec4 & col)2461 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
2462 {
2463     ImGuiContext& g = *GImGui;
2464     ImGuiColorMod backup;
2465     backup.Col = idx;
2466     backup.BackupValue = g.Style.Colors[idx];
2467     g.ColorStack.push_back(backup);
2468     g.Style.Colors[idx] = col;
2469 }
2470 
PopStyleColor(int count)2471 void ImGui::PopStyleColor(int count)
2472 {
2473     ImGuiContext& g = *GImGui;
2474     while (count > 0)
2475     {
2476         ImGuiColorMod& backup = g.ColorStack.back();
2477         g.Style.Colors[backup.Col] = backup.BackupValue;
2478         g.ColorStack.pop_back();
2479         count--;
2480     }
2481 }
2482 
2483 struct ImGuiStyleVarInfo
2484 {
2485     ImGuiDataType   Type;
2486     ImU32           Count;
2487     ImU32           Offset;
GetVarPtrImGuiStyleVarInfo2488     void*           GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
2489 };
2490 
2491 static const ImGuiCol GWindowDockStyleColors[ImGuiWindowDockStyleCol_COUNT] =
2492 {
2493     ImGuiCol_Text, ImGuiCol_Tab, ImGuiCol_TabHovered, ImGuiCol_TabActive, ImGuiCol_TabUnfocused, ImGuiCol_TabUnfocusedActive
2494 };
2495 
2496 static const ImGuiStyleVarInfo GStyleVarInfo[] =
2497 {
2498     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) },               // ImGuiStyleVar_Alpha
2499     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) },       // ImGuiStyleVar_WindowPadding
2500     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) },      // ImGuiStyleVar_WindowRounding
2501     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) },    // ImGuiStyleVar_WindowBorderSize
2502     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) },       // ImGuiStyleVar_WindowMinSize
2503     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) },    // ImGuiStyleVar_WindowTitleAlign
2504     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) },       // ImGuiStyleVar_ChildRounding
2505     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) },     // ImGuiStyleVar_ChildBorderSize
2506     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) },       // ImGuiStyleVar_PopupRounding
2507     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) },     // ImGuiStyleVar_PopupBorderSize
2508     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) },        // ImGuiStyleVar_FramePadding
2509     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) },       // ImGuiStyleVar_FrameRounding
2510     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) },     // ImGuiStyleVar_FrameBorderSize
2511     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) },         // ImGuiStyleVar_ItemSpacing
2512     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) },    // ImGuiStyleVar_ItemInnerSpacing
2513     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) },       // ImGuiStyleVar_IndentSpacing
2514     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, CellPadding) },         // ImGuiStyleVar_CellPadding
2515     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) },       // ImGuiStyleVar_ScrollbarSize
2516     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) },   // ImGuiStyleVar_ScrollbarRounding
2517     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) },         // ImGuiStyleVar_GrabMinSize
2518     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) },        // ImGuiStyleVar_GrabRounding
2519     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) },         // ImGuiStyleVar_TabRounding
2520     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) },     // ImGuiStyleVar_ButtonTextAlign
2521     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
2522 };
2523 
GetStyleVarInfo(ImGuiStyleVar idx)2524 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
2525 {
2526     IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
2527     IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
2528     return &GStyleVarInfo[idx];
2529 }
2530 
PushStyleVar(ImGuiStyleVar idx,float val)2531 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
2532 {
2533     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
2534     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
2535     {
2536         ImGuiContext& g = *GImGui;
2537         float* pvar = (float*)var_info->GetVarPtr(&g.Style);
2538         g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
2539         *pvar = val;
2540         return;
2541     }
2542     IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!");
2543 }
2544 
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)2545 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
2546 {
2547     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
2548     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
2549     {
2550         ImGuiContext& g = *GImGui;
2551         ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
2552         g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
2553         *pvar = val;
2554         return;
2555     }
2556     IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!");
2557 }
2558 
PopStyleVar(int count)2559 void ImGui::PopStyleVar(int count)
2560 {
2561     ImGuiContext& g = *GImGui;
2562     while (count > 0)
2563     {
2564         // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
2565         ImGuiStyleMod& backup = g.StyleVarStack.back();
2566         const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
2567         void* data = info->GetVarPtr(&g.Style);
2568         if (info->Type == ImGuiDataType_Float && info->Count == 1)      { ((float*)data)[0] = backup.BackupFloat[0]; }
2569         else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
2570         g.StyleVarStack.pop_back();
2571         count--;
2572     }
2573 }
2574 
GetStyleColorName(ImGuiCol idx)2575 const char* ImGui::GetStyleColorName(ImGuiCol idx)
2576 {
2577     // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
2578     switch (idx)
2579     {
2580     case ImGuiCol_Text: return "Text";
2581     case ImGuiCol_TextDisabled: return "TextDisabled";
2582     case ImGuiCol_WindowBg: return "WindowBg";
2583     case ImGuiCol_ChildBg: return "ChildBg";
2584     case ImGuiCol_PopupBg: return "PopupBg";
2585     case ImGuiCol_Border: return "Border";
2586     case ImGuiCol_BorderShadow: return "BorderShadow";
2587     case ImGuiCol_FrameBg: return "FrameBg";
2588     case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
2589     case ImGuiCol_FrameBgActive: return "FrameBgActive";
2590     case ImGuiCol_TitleBg: return "TitleBg";
2591     case ImGuiCol_TitleBgActive: return "TitleBgActive";
2592     case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
2593     case ImGuiCol_MenuBarBg: return "MenuBarBg";
2594     case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
2595     case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
2596     case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
2597     case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
2598     case ImGuiCol_CheckMark: return "CheckMark";
2599     case ImGuiCol_SliderGrab: return "SliderGrab";
2600     case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
2601     case ImGuiCol_Button: return "Button";
2602     case ImGuiCol_ButtonHovered: return "ButtonHovered";
2603     case ImGuiCol_ButtonActive: return "ButtonActive";
2604     case ImGuiCol_Header: return "Header";
2605     case ImGuiCol_HeaderHovered: return "HeaderHovered";
2606     case ImGuiCol_HeaderActive: return "HeaderActive";
2607     case ImGuiCol_Separator: return "Separator";
2608     case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
2609     case ImGuiCol_SeparatorActive: return "SeparatorActive";
2610     case ImGuiCol_ResizeGrip: return "ResizeGrip";
2611     case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
2612     case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
2613     case ImGuiCol_Tab: return "Tab";
2614     case ImGuiCol_TabHovered: return "TabHovered";
2615     case ImGuiCol_TabActive: return "TabActive";
2616     case ImGuiCol_TabUnfocused: return "TabUnfocused";
2617     case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive";
2618     case ImGuiCol_DockingPreview: return "DockingPreview";
2619     case ImGuiCol_DockingEmptyBg: return "DockingEmptyBg";
2620     case ImGuiCol_PlotLines: return "PlotLines";
2621     case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
2622     case ImGuiCol_PlotHistogram: return "PlotHistogram";
2623     case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
2624     case ImGuiCol_TableHeaderBg: return "TableHeaderBg";
2625     case ImGuiCol_TableBorderStrong: return "TableBorderStrong";
2626     case ImGuiCol_TableBorderLight: return "TableBorderLight";
2627     case ImGuiCol_TableRowBg: return "TableRowBg";
2628     case ImGuiCol_TableRowBgAlt: return "TableRowBgAlt";
2629     case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
2630     case ImGuiCol_DragDropTarget: return "DragDropTarget";
2631     case ImGuiCol_NavHighlight: return "NavHighlight";
2632     case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
2633     case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
2634     case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
2635     }
2636     IM_ASSERT(0);
2637     return "Unknown";
2638 }
2639 
2640 
2641 //-----------------------------------------------------------------------------
2642 // [SECTION] RENDER HELPERS
2643 // Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change,
2644 // we need a nicer separation between low-level functions and high-level functions relying on the ImGui context.
2645 // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context.
2646 //-----------------------------------------------------------------------------
2647 
FindRenderedTextEnd(const char * text,const char * text_end)2648 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2649 {
2650     const char* text_display_end = text;
2651     if (!text_end)
2652         text_end = (const char*)-1;
2653 
2654     while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2655         text_display_end++;
2656     return text_display_end;
2657 }
2658 
2659 // Internal ImGui functions to render text
2660 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)2661 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2662 {
2663     ImGuiContext& g = *GImGui;
2664     ImGuiWindow* window = g.CurrentWindow;
2665 
2666     // Hide anything after a '##' string
2667     const char* text_display_end;
2668     if (hide_text_after_hash)
2669     {
2670         text_display_end = FindRenderedTextEnd(text, text_end);
2671     }
2672     else
2673     {
2674         if (!text_end)
2675             text_end = text + strlen(text); // FIXME-OPT
2676         text_display_end = text_end;
2677     }
2678 
2679     if (text != text_display_end)
2680     {
2681         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2682         if (g.LogEnabled)
2683             LogRenderedText(&pos, text, text_display_end);
2684     }
2685 }
2686 
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)2687 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2688 {
2689     ImGuiContext& g = *GImGui;
2690     ImGuiWindow* window = g.CurrentWindow;
2691 
2692     if (!text_end)
2693         text_end = text + strlen(text); // FIXME-OPT
2694 
2695     if (text != text_end)
2696     {
2697         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2698         if (g.LogEnabled)
2699             LogRenderedText(&pos, text, text_end);
2700     }
2701 }
2702 
2703 // Default clip_rect uses (pos_min,pos_max)
2704 // 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)2705 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)
2706 {
2707     // Perform CPU side clipping for single clipped element to avoid using scissor state
2708     ImVec2 pos = pos_min;
2709     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
2710 
2711     const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
2712     const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
2713     bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2714     if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
2715         need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2716 
2717     // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
2718     if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
2719     if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
2720 
2721     // Render
2722     if (need_clipping)
2723     {
2724         ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2725         draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2726     }
2727     else
2728     {
2729         draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2730     }
2731 }
2732 
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)2733 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)
2734 {
2735     // Hide anything after a '##' string
2736     const char* text_display_end = FindRenderedTextEnd(text, text_end);
2737     const int text_len = (int)(text_display_end - text);
2738     if (text_len == 0)
2739         return;
2740 
2741     ImGuiContext& g = *GImGui;
2742     ImGuiWindow* window = g.CurrentWindow;
2743     RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
2744     if (g.LogEnabled)
2745         LogRenderedText(&pos_min, text, text_display_end);
2746 }
2747 
2748 
2749 // Another overly complex function until we reorganize everything into a nice all-in-one helper.
2750 // 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.
2751 // 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)2752 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)
2753 {
2754     ImGuiContext& g = *GImGui;
2755     if (text_end_full == NULL)
2756         text_end_full = FindRenderedTextEnd(text);
2757     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);
2758 
2759     //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));
2760     //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));
2761     //draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255));
2762     // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
2763     if (text_size.x > pos_max.x - pos_min.x)
2764     {
2765         // Hello wo...
2766         // |       |   |
2767         // min   max   ellipsis_max
2768         //          <-> this is generally some padding value
2769 
2770         const ImFont* font = draw_list->_Data->Font;
2771         const float font_size = draw_list->_Data->FontSize;
2772         const char* text_end_ellipsis = NULL;
2773 
2774         ImWchar ellipsis_char = font->EllipsisChar;
2775         int ellipsis_char_count = 1;
2776         if (ellipsis_char == (ImWchar)-1)
2777         {
2778             ellipsis_char = (ImWchar)'.';
2779             ellipsis_char_count = 3;
2780         }
2781         const ImFontGlyph* glyph = font->FindGlyph(ellipsis_char);
2782 
2783         float ellipsis_glyph_width = glyph->X1;                 // Width of the glyph with no padding on either side
2784         float ellipsis_total_width = ellipsis_glyph_width;      // Full width of entire ellipsis
2785 
2786         if (ellipsis_char_count > 1)
2787         {
2788             // Full ellipsis size without free spacing after it.
2789             const float spacing_between_dots = 1.0f * (draw_list->_Data->FontSize / font->FontSize);
2790             ellipsis_glyph_width = glyph->X1 - glyph->X0 + spacing_between_dots;
2791             ellipsis_total_width = ellipsis_glyph_width * (float)ellipsis_char_count - spacing_between_dots;
2792         }
2793 
2794         // We can now claim the space between pos_max.x and ellipsis_max.x
2795         const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_total_width) - pos_min.x, 1.0f);
2796         float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
2797         if (text == text_end_ellipsis && text_end_ellipsis < text_end_full)
2798         {
2799             // Always display at least 1 character if there's no room for character + ellipsis
2800             text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full);
2801             text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x;
2802         }
2803         while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1]))
2804         {
2805             // 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)
2806             text_end_ellipsis--;
2807             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
2808         }
2809 
2810         // Render text, render ellipsis
2811         RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
2812         float ellipsis_x = pos_min.x + text_size_clipped_x;
2813         if (ellipsis_x + ellipsis_total_width <= ellipsis_max_x)
2814             for (int i = 0; i < ellipsis_char_count; i++)
2815             {
2816                 font->RenderChar(draw_list, font_size, ImVec2(ellipsis_x, pos_min.y), GetColorU32(ImGuiCol_Text), ellipsis_char);
2817                 ellipsis_x += ellipsis_glyph_width;
2818             }
2819     }
2820     else
2821     {
2822         RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));
2823     }
2824 
2825     if (g.LogEnabled)
2826         LogRenderedText(&pos_min, text, text_end_full);
2827 }
2828 
2829 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)2830 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2831 {
2832     ImGuiContext& g = *GImGui;
2833     ImGuiWindow* window = g.CurrentWindow;
2834     window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2835     const float border_size = g.Style.FrameBorderSize;
2836     if (border && border_size > 0.0f)
2837     {
2838         window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2839         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2840     }
2841 }
2842 
RenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding)2843 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
2844 {
2845     ImGuiContext& g = *GImGui;
2846     ImGuiWindow* window = g.CurrentWindow;
2847     const float border_size = g.Style.FrameBorderSize;
2848     if (border_size > 0.0f)
2849     {
2850         window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2851         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2852     }
2853 }
2854 
RenderNavHighlight(const ImRect & bb,ImGuiID id,ImGuiNavHighlightFlags flags)2855 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
2856 {
2857     ImGuiContext& g = *GImGui;
2858     if (id != g.NavId)
2859         return;
2860     if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
2861         return;
2862     ImGuiWindow* window = g.CurrentWindow;
2863     if (window->DC.NavHideHighlightOneFrame)
2864         return;
2865 
2866     float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
2867     ImRect display_rect = bb;
2868     display_rect.ClipWith(window->ClipRect);
2869     if (flags & ImGuiNavHighlightFlags_TypeDefault)
2870     {
2871         const float THICKNESS = 2.0f;
2872         const float DISTANCE = 3.0f + THICKNESS * 0.5f;
2873         display_rect.Expand(ImVec2(DISTANCE, DISTANCE));
2874         bool fully_visible = window->ClipRect.Contains(display_rect);
2875         if (!fully_visible)
2876             window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
2877         window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f), display_rect.Max - ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f), GetColorU32(ImGuiCol_NavHighlight), rounding, ImDrawCornerFlags_All, THICKNESS);
2878         if (!fully_visible)
2879             window->DrawList->PopClipRect();
2880     }
2881     if (flags & ImGuiNavHighlightFlags_TypeThin)
2882     {
2883         window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f);
2884     }
2885 }
2886 
2887 //-----------------------------------------------------------------------------
2888 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
2889 //-----------------------------------------------------------------------------
2890 
2891 // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
ImGuiWindow(ImGuiContext * context,const char * name)2892 ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) : DrawListInst(NULL)
2893 {
2894     memset(this, 0, sizeof(*this));
2895     Name = ImStrdup(name);
2896     NameBufLen = (int)strlen(name) + 1;
2897     ID = ImHashStr(name);
2898     IDStack.push_back(ID);
2899     ViewportAllowPlatformMonitorExtend = -1;
2900     ViewportPos = ImVec2(FLT_MAX, FLT_MAX);
2901     MoveId = GetID("#MOVE");
2902     ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
2903     ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
2904     AutoFitFramesX = AutoFitFramesY = -1;
2905     AutoPosLastDirection = ImGuiDir_None;
2906     SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = SetWindowDockAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
2907     SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
2908     LastFrameActive = -1;
2909     LastFrameJustFocused = -1;
2910     LastTimeActive = -1.0f;
2911     FontWindowScale = FontDpiScale = 1.0f;
2912     SettingsOffset = -1;
2913     DockOrder = -1;
2914     DrawList = &DrawListInst;
2915     DrawList->_Data = &context->DrawListSharedData;
2916     DrawList->_OwnerName = Name;
2917 }
2918 
~ImGuiWindow()2919 ImGuiWindow::~ImGuiWindow()
2920 {
2921     IM_ASSERT(DrawList == &DrawListInst);
2922     IM_DELETE(Name);
2923     for (int i = 0; i != ColumnsStorage.Size; i++)
2924         ColumnsStorage[i].~ImGuiOldColumns();
2925 }
2926 
GetID(const char * str,const char * str_end)2927 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
2928 {
2929     ImGuiID seed = IDStack.back();
2930     ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2931     ImGui::KeepAliveID(id);
2932 #ifdef IMGUI_ENABLE_TEST_ENGINE
2933     ImGuiContext& g = *GImGui;
2934     IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
2935 #endif
2936     return id;
2937 }
2938 
GetID(const void * ptr)2939 ImGuiID ImGuiWindow::GetID(const void* ptr)
2940 {
2941     ImGuiID seed = IDStack.back();
2942     ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
2943     ImGui::KeepAliveID(id);
2944 #ifdef IMGUI_ENABLE_TEST_ENGINE
2945     ImGuiContext& g = *GImGui;
2946     IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr);
2947 #endif
2948     return id;
2949 }
2950 
GetID(int n)2951 ImGuiID ImGuiWindow::GetID(int n)
2952 {
2953     ImGuiID seed = IDStack.back();
2954     ImGuiID id = ImHashData(&n, sizeof(n), seed);
2955     ImGui::KeepAliveID(id);
2956 #ifdef IMGUI_ENABLE_TEST_ENGINE
2957     ImGuiContext& g = *GImGui;
2958     IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n);
2959 #endif
2960     return id;
2961 }
2962 
GetIDNoKeepAlive(const char * str,const char * str_end)2963 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
2964 {
2965     ImGuiID seed = IDStack.back();
2966     ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2967 #ifdef IMGUI_ENABLE_TEST_ENGINE
2968     ImGuiContext& g = *GImGui;
2969     IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
2970 #endif
2971     return id;
2972 }
2973 
GetIDNoKeepAlive(const void * ptr)2974 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
2975 {
2976     ImGuiID seed = IDStack.back();
2977     ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
2978 #ifdef IMGUI_ENABLE_TEST_ENGINE
2979     ImGuiContext& g = *GImGui;
2980     IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr);
2981 #endif
2982     return id;
2983 }
2984 
GetIDNoKeepAlive(int n)2985 ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n)
2986 {
2987     ImGuiID seed = IDStack.back();
2988     ImGuiID id = ImHashData(&n, sizeof(n), seed);
2989 #ifdef IMGUI_ENABLE_TEST_ENGINE
2990     ImGuiContext& g = *GImGui;
2991     IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n);
2992 #endif
2993     return id;
2994 }
2995 
2996 // This is only used in rare/specific situations to manufacture an ID out of nowhere.
GetIDFromRectangle(const ImRect & r_abs)2997 ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
2998 {
2999     ImGuiID seed = IDStack.back();
3000     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) };
3001     ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
3002     ImGui::KeepAliveID(id);
3003     return id;
3004 }
3005 
SetCurrentWindow(ImGuiWindow * window)3006 static void SetCurrentWindow(ImGuiWindow* window)
3007 {
3008     ImGuiContext& g = *GImGui;
3009     g.CurrentWindow = window;
3010     g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL;
3011     if (window)
3012         g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
3013 }
3014 
GcCompactTransientMiscBuffers()3015 void ImGui::GcCompactTransientMiscBuffers()
3016 {
3017     ImGuiContext& g = *GImGui;
3018     g.ItemFlagsStack.clear();
3019     g.GroupStack.clear();
3020     TableGcCompactSettings();
3021 }
3022 
3023 // Free up/compact internal window buffers, we can use this when a window becomes unused.
3024 // Not freed:
3025 // - ImGuiWindow, ImGuiWindowSettings, Name, StateStorage, ColumnsStorage (may hold useful data)
3026 // This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.
GcCompactTransientWindowBuffers(ImGuiWindow * window)3027 void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window)
3028 {
3029     window->MemoryCompacted = true;
3030     window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;
3031     window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;
3032     window->IDStack.clear();
3033     window->DrawList->_ClearFreeMemory();
3034     window->DC.ChildWindows.clear();
3035     window->DC.ItemWidthStack.clear();
3036     window->DC.TextWrapPosStack.clear();
3037 }
3038 
GcAwakeTransientWindowBuffers(ImGuiWindow * window)3039 void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window)
3040 {
3041     // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.
3042     // The other buffers tends to amortize much faster.
3043     window->MemoryCompacted = false;
3044     window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity);
3045     window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity);
3046     window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0;
3047 }
3048 
SetActiveID(ImGuiID id,ImGuiWindow * window)3049 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
3050 {
3051     ImGuiContext& g = *GImGui;
3052     g.ActiveIdIsJustActivated = (g.ActiveId != id);
3053     if (g.ActiveIdIsJustActivated)
3054     {
3055         g.ActiveIdTimer = 0.0f;
3056         g.ActiveIdHasBeenPressedBefore = false;
3057         g.ActiveIdHasBeenEditedBefore = false;
3058         if (id != 0)
3059         {
3060             g.LastActiveId = id;
3061             g.LastActiveIdTimer = 0.0f;
3062         }
3063     }
3064     g.ActiveId = id;
3065     g.ActiveIdAllowOverlap = false;
3066     g.ActiveIdNoClearOnFocusLoss = false;
3067     g.ActiveIdWindow = window;
3068     g.ActiveIdHasBeenEditedThisFrame = false;
3069     if (id)
3070     {
3071         g.ActiveIdIsAlive = id;
3072         g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
3073     }
3074 
3075     // Clear declaration of inputs claimed by the widget
3076     // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
3077     g.ActiveIdUsingMouseWheel = false;
3078     g.ActiveIdUsingNavDirMask = 0x00;
3079     g.ActiveIdUsingNavInputMask = 0x00;
3080     g.ActiveIdUsingKeyInputMask = 0x00;
3081 }
3082 
ClearActiveID()3083 void ImGui::ClearActiveID()
3084 {
3085     SetActiveID(0, NULL); // g.ActiveId = 0;
3086 }
3087 
SetHoveredID(ImGuiID id)3088 void ImGui::SetHoveredID(ImGuiID id)
3089 {
3090     ImGuiContext& g = *GImGui;
3091     g.HoveredId = id;
3092     g.HoveredIdAllowOverlap = false;
3093     g.HoveredIdUsingMouseWheel = false;
3094     if (id != 0 && g.HoveredIdPreviousFrame != id)
3095         g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
3096 }
3097 
GetHoveredID()3098 ImGuiID ImGui::GetHoveredID()
3099 {
3100     ImGuiContext& g = *GImGui;
3101     return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
3102 }
3103 
KeepAliveID(ImGuiID id)3104 void ImGui::KeepAliveID(ImGuiID id)
3105 {
3106     ImGuiContext& g = *GImGui;
3107     if (g.ActiveId == id)
3108         g.ActiveIdIsAlive = id;
3109     if (g.ActiveIdPreviousFrame == id)
3110         g.ActiveIdPreviousFrameIsAlive = true;
3111 }
3112 
MarkItemEdited(ImGuiID id)3113 void ImGui::MarkItemEdited(ImGuiID id)
3114 {
3115     // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
3116     // 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.
3117     ImGuiContext& g = *GImGui;
3118     IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive);
3119     IM_UNUSED(id); // Avoid unused variable warnings when asserts are compiled out.
3120     //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
3121     g.ActiveIdHasBeenEditedThisFrame = true;
3122     g.ActiveIdHasBeenEditedBefore = true;
3123     g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
3124 }
3125 
IsWindowContentHoverable(ImGuiWindow * window,ImGuiHoveredFlags flags)3126 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
3127 {
3128     // An active popup disable hovering on other windows (apart from its own children)
3129     // FIXME-OPT: This could be cached/stored within the window.
3130     ImGuiContext& g = *GImGui;
3131     if (g.NavWindow)
3132         if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
3133             if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
3134             {
3135                 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
3136                 // NB: The order of those two tests is important because Modal windows are also Popups.
3137                 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
3138                     return false;
3139                 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
3140                     return false;
3141             }
3142 
3143     // Filter by viewport
3144     if (window->Viewport != g.MouseViewport)
3145         if (g.MovingWindow == NULL || window->RootWindow != g.MovingWindow->RootWindow)
3146             return false;
3147 
3148     return true;
3149 }
3150 
3151 // This is roughly matching the behavior of internal-facing ItemHoverable()
3152 // - 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()
3153 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
IsItemHovered(ImGuiHoveredFlags flags)3154 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
3155 {
3156     ImGuiContext& g = *GImGui;
3157     ImGuiWindow* window = g.CurrentWindow;
3158     if (g.NavDisableMouseHover && !g.NavDisableHighlight)
3159         return IsItemFocused();
3160 
3161     // Test for bounding box overlap, as updated as ItemAdd()
3162     if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
3163         return false;
3164     IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0);   // Flags not supported by this function
3165 
3166     // Test if we are hovering the right window (our window could be behind another window)
3167     // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable to use IsItemHovered() after EndChild() itself.
3168     // Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was the test that has been running for a long while.
3169     //if (g.HoveredWindow != window)
3170     //    return false;
3171     if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
3172         return false;
3173 
3174     // Test if another item is active (e.g. being dragged)
3175     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
3176         if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
3177             return false;
3178 
3179     // Test if interactions on this window are blocked by an active popup or modal.
3180     // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
3181     if (!IsWindowContentHoverable(window, flags))
3182         return false;
3183 
3184     // Test if the item is disabled
3185     if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
3186         return false;
3187 
3188     // Special handling for calling after Begin() which represent the title bar or tab.
3189     // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
3190     if ((window->DC.LastItemId == window->ID || window->DC.LastItemId == window->MoveId) && window->WriteAccessed)
3191         return false;
3192     return true;
3193 }
3194 
3195 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
ItemHoverable(const ImRect & bb,ImGuiID id)3196 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
3197 {
3198     ImGuiContext& g = *GImGui;
3199     if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
3200         return false;
3201 
3202     ImGuiWindow* window = g.CurrentWindow;
3203     if (g.HoveredWindow != window)
3204         return false;
3205     if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
3206         return false;
3207     if (!IsMouseHoveringRect(bb.Min, bb.Max))
3208         return false;
3209     if (g.NavDisableMouseHover)
3210         return false;
3211     if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_None) || (window->DC.ItemFlags & ImGuiItemFlags_Disabled))
3212     {
3213         g.HoveredIdDisabled = true;
3214         return false;
3215     }
3216 
3217     // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level
3218     // hover test in widgets code. We could also decide to split this function is two.
3219     if (id != 0)
3220     {
3221         SetHoveredID(id);
3222 
3223         // [DEBUG] Item Picker tool!
3224         // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making
3225         // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered
3226         // items if we perform the test in ItemAdd(), but that would incur a small runtime cost.
3227         // #define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX in imconfig.h if you want this check to also be performed in ItemAdd().
3228         if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
3229             GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
3230         if (g.DebugItemPickerBreakId == id)
3231             IM_DEBUG_BREAK();
3232     }
3233 
3234     return true;
3235 }
3236 
IsClippedEx(const ImRect & bb,ImGuiID id,bool clip_even_when_logged)3237 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
3238 {
3239     ImGuiContext& g = *GImGui;
3240     ImGuiWindow* window = g.CurrentWindow;
3241     if (!bb.Overlaps(window->ClipRect))
3242         if (id == 0 || (id != g.ActiveId && id != g.NavId))
3243             if (clip_even_when_logged || !g.LogEnabled)
3244                 return true;
3245     return false;
3246 }
3247 
3248 // This is also inlined in ItemAdd()
3249 // Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set window->DC.LastItemDisplayRect!
SetLastItemData(ImGuiWindow * window,ImGuiID item_id,ImGuiItemStatusFlags item_flags,const ImRect & item_rect)3250 void ImGui::SetLastItemData(ImGuiWindow* window, ImGuiID item_id, ImGuiItemStatusFlags item_flags, const ImRect& item_rect)
3251 {
3252     window->DC.LastItemId = item_id;
3253     window->DC.LastItemStatusFlags = item_flags;
3254     window->DC.LastItemRect = item_rect;
3255 }
3256 
3257 // Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out.
FocusableItemRegister(ImGuiWindow * window,ImGuiID id)3258 bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id)
3259 {
3260     ImGuiContext& g = *GImGui;
3261 
3262     // Increment counters
3263     const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0;
3264     window->DC.FocusCounterRegular++;
3265     if (is_tab_stop)
3266         window->DC.FocusCounterTabStop++;
3267 
3268     // Process TAB/Shift-TAB to tab *OUT* of the currently focused item.
3269     // (Note that we can always TAB out of a widget that doesn't allow tabbing in)
3270     if (g.ActiveId == id && g.FocusTabPressed && !IsActiveIdUsingKey(ImGuiKey_Tab) && g.FocusRequestNextWindow == NULL)
3271     {
3272         g.FocusRequestNextWindow = window;
3273         g.FocusRequestNextCounterTabStop = window->DC.FocusCounterTabStop + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items.
3274     }
3275 
3276     // Handle focus requests
3277     if (g.FocusRequestCurrWindow == window)
3278     {
3279         if (window->DC.FocusCounterRegular == g.FocusRequestCurrCounterRegular)
3280             return true;
3281         if (is_tab_stop && window->DC.FocusCounterTabStop == g.FocusRequestCurrCounterTabStop)
3282         {
3283             g.NavJustTabbedId = id;
3284             return true;
3285         }
3286 
3287         // If another item is about to be focused, we clear our own active id
3288         if (g.ActiveId == id)
3289             ClearActiveID();
3290     }
3291 
3292     return false;
3293 }
3294 
FocusableItemUnregister(ImGuiWindow * window)3295 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
3296 {
3297     window->DC.FocusCounterRegular--;
3298     window->DC.FocusCounterTabStop--;
3299 }
3300 
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)3301 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
3302 {
3303     if (wrap_pos_x < 0.0f)
3304         return 0.0f;
3305 
3306     ImGuiContext& g = *GImGui;
3307     ImGuiWindow* window = g.CurrentWindow;
3308     if (wrap_pos_x == 0.0f)
3309     {
3310         // We could decide to setup a default wrapping max point for auto-resizing windows,
3311         // or have auto-wrap (with unspecified wrapping pos) behave as a ContentSize extending function?
3312         //if (window->Hidden && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize))
3313         //    wrap_pos_x = ImMax(window->WorkRect.Min.x + g.FontSize * 10.0f, window->WorkRect.Max.x);
3314         //else
3315         wrap_pos_x = window->WorkRect.Max.x;
3316     }
3317     else if (wrap_pos_x > 0.0f)
3318     {
3319         wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
3320     }
3321 
3322     return ImMax(wrap_pos_x - pos.x, 1.0f);
3323 }
3324 
3325 // IM_ALLOC() == ImGui::MemAlloc()
MemAlloc(size_t size)3326 void* ImGui::MemAlloc(size_t size)
3327 {
3328     if (ImGuiContext* ctx = GImGui)
3329         ctx->IO.MetricsActiveAllocations++;
3330     return GImAllocatorAllocFunc(size, GImAllocatorUserData);
3331 }
3332 
3333 // IM_FREE() == ImGui::MemFree()
MemFree(void * ptr)3334 void ImGui::MemFree(void* ptr)
3335 {
3336     if (ptr)
3337         if (ImGuiContext* ctx = GImGui)
3338             ctx->IO.MetricsActiveAllocations--;
3339     return GImAllocatorFreeFunc(ptr, GImAllocatorUserData);
3340 }
3341 
GetClipboardText()3342 const char* ImGui::GetClipboardText()
3343 {
3344     ImGuiContext& g = *GImGui;
3345     return g.IO.GetClipboardTextFn ? g.IO.GetClipboardTextFn(g.IO.ClipboardUserData) : "";
3346 }
3347 
SetClipboardText(const char * text)3348 void ImGui::SetClipboardText(const char* text)
3349 {
3350     ImGuiContext& g = *GImGui;
3351     if (g.IO.SetClipboardTextFn)
3352         g.IO.SetClipboardTextFn(g.IO.ClipboardUserData, text);
3353 }
3354 
GetVersion()3355 const char* ImGui::GetVersion()
3356 {
3357     return IMGUI_VERSION;
3358 }
3359 
3360 // Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself
3361 // 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()3362 ImGuiContext* ImGui::GetCurrentContext()
3363 {
3364     return GImGui;
3365 }
3366 
SetCurrentContext(ImGuiContext * ctx)3367 void ImGui::SetCurrentContext(ImGuiContext* ctx)
3368 {
3369 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
3370     IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
3371 #else
3372     GImGui = ctx;
3373 #endif
3374 }
3375 
SetAllocatorFunctions(void * (* alloc_func)(size_t sz,void * user_data),void (* free_func)(void * ptr,void * user_data),void * user_data)3376 void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data)
3377 {
3378     GImAllocatorAllocFunc = alloc_func;
3379     GImAllocatorFreeFunc = free_func;
3380     GImAllocatorUserData = user_data;
3381 }
3382 
CreateContext(ImFontAtlas * shared_font_atlas)3383 ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
3384 {
3385     ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
3386     if (GImGui == NULL)
3387         SetCurrentContext(ctx);
3388     Initialize(ctx);
3389     return ctx;
3390 }
3391 
DestroyContext(ImGuiContext * ctx)3392 void ImGui::DestroyContext(ImGuiContext* ctx)
3393 {
3394     if (ctx == NULL)
3395         ctx = GImGui;
3396     Shutdown(ctx);
3397     if (GImGui == ctx)
3398         SetCurrentContext(NULL);
3399     IM_DELETE(ctx);
3400 }
3401 
3402 // No specific ordering/dependency support, will see as needed
AddContextHook(ImGuiContext * ctx,const ImGuiContextHook * hook)3403 void ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook)
3404 {
3405     ImGuiContext& g = *ctx;
3406     IM_ASSERT(hook->Callback != NULL);
3407     g.Hooks.push_back(*hook);
3408 }
3409 
3410 // Call context hooks (used by e.g. test engine)
3411 // We assume a small number of hooks so all stored in same array
CallContextHooks(ImGuiContext * ctx,ImGuiContextHookType hook_type)3412 void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type)
3413 {
3414     ImGuiContext& g = *ctx;
3415     for (int n = 0; n < g.Hooks.Size; n++)
3416         if (g.Hooks[n].Type == hook_type)
3417             g.Hooks[n].Callback(&g, &g.Hooks[n]);
3418 }
3419 
GetIO()3420 ImGuiIO& ImGui::GetIO()
3421 {
3422     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3423     return GImGui->IO;
3424 }
3425 
GetPlatformIO()3426 ImGuiPlatformIO& ImGui::GetPlatformIO()
3427 {
3428     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
3429     return GImGui->PlatformIO;
3430 }
3431 
3432 // Pass this to your backend rendering function! Valid after Render() and until the next call to NewFrame()
GetDrawData()3433 ImDrawData* ImGui::GetDrawData()
3434 {
3435     ImGuiContext& g = *GImGui;
3436     return g.Viewports[0]->DrawDataP.Valid ? &g.Viewports[0]->DrawDataP : NULL;
3437 }
3438 
GetTime()3439 double ImGui::GetTime()
3440 {
3441     return GImGui->Time;
3442 }
3443 
GetFrameCount()3444 int ImGui::GetFrameCount()
3445 {
3446     return GImGui->FrameCount;
3447 }
3448 
GetViewportDrawList(ImGuiViewportP * viewport,size_t drawlist_no,const char * drawlist_name)3449 static ImDrawList* GetViewportDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name)
3450 {
3451     // Create the draw list on demand, because they are not frequently used for all viewports
3452     ImGuiContext& g = *GImGui;
3453     IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->DrawLists));
3454     ImDrawList* draw_list = viewport->DrawLists[drawlist_no];
3455     if (draw_list == NULL)
3456     {
3457         draw_list = IM_NEW(ImDrawList)(&g.DrawListSharedData);
3458         draw_list->_OwnerName = drawlist_name;
3459         viewport->DrawLists[drawlist_no] = draw_list;
3460     }
3461 
3462     // Our ImDrawList system requires that there is always a command
3463     if (viewport->LastFrameDrawLists[drawlist_no] != g.FrameCount)
3464     {
3465         draw_list->_ResetForNewFrame();
3466         draw_list->PushTextureID(g.IO.Fonts->TexID);
3467         draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false);
3468         viewport->LastFrameDrawLists[drawlist_no] = g.FrameCount;
3469     }
3470     return draw_list;
3471 }
3472 
GetBackgroundDrawList(ImGuiViewport * viewport)3473 ImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport)
3474 {
3475     return GetViewportDrawList((ImGuiViewportP*)viewport, 0, "##Background");
3476 }
3477 
GetBackgroundDrawList()3478 ImDrawList* ImGui::GetBackgroundDrawList()
3479 {
3480     ImGuiWindow* window = GImGui->CurrentWindow;
3481     return GetBackgroundDrawList(window->Viewport);
3482 }
3483 
GetForegroundDrawList(ImGuiViewport * viewport)3484 ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport)
3485 {
3486     return GetViewportDrawList((ImGuiViewportP*)viewport, 1, "##Foreground");
3487 }
3488 
GetForegroundDrawList()3489 ImDrawList* ImGui::GetForegroundDrawList()
3490 {
3491     ImGuiWindow* window = GImGui->CurrentWindow;
3492     return GetForegroundDrawList(window->Viewport);
3493 }
3494 
GetDrawListSharedData()3495 ImDrawListSharedData* ImGui::GetDrawListSharedData()
3496 {
3497     return &GImGui->DrawListSharedData;
3498 }
3499 
StartMouseMovingWindow(ImGuiWindow * window)3500 void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
3501 {
3502     // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
3503     // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
3504     // This is because we want ActiveId to be set even when the window is not permitted to move.
3505     ImGuiContext& g = *GImGui;
3506     FocusWindow(window);
3507     SetActiveID(window->MoveId, window);
3508     g.NavDisableHighlight = true;
3509     g.ActiveIdNoClearOnFocusLoss = true;
3510     g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos;
3511 
3512     bool can_move_window = true;
3513     if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
3514         can_move_window = false;
3515     if (ImGuiDockNode* node = window->DockNodeAsHost)
3516         if (node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove))
3517             can_move_window = false;
3518     if (can_move_window)
3519         g.MovingWindow = window;
3520 }
3521 
3522 // We use 'undock_floating_node == false' when dragging from title bar to allow moving groups of floating nodes without undocking them.
3523 // - undock_floating_node == true: when dragging from a floating node within a hierarchy, always undock the node.
3524 // - undock_floating_node == false: when dragging from a floating node within a hierarchy, move root window.
StartMouseMovingWindowOrNode(ImGuiWindow * window,ImGuiDockNode * node,bool undock_floating_node)3525 void ImGui::StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* node, bool undock_floating_node)
3526 {
3527     ImGuiContext& g = *GImGui;
3528     bool can_undock_node = false;
3529     if (node != NULL && node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove) == 0)
3530     {
3531         // Can undock if:
3532         // - part of a floating node hierarchy with more than one visible node (if only one is visible, we'll just move the whole hierarchy)
3533         // - part of a dockspace node hierarchy (trivia: undocking from a fixed/central node will create a new node and copy windows)
3534         ImGuiDockNode* root_node = DockNodeGetRootNode(node);
3535         if (root_node->OnlyNodeWithWindows != node || root_node->CentralNode != NULL)   // -V1051 PVS-Studio thinks node should be root_node and is wrong about that.
3536             if (undock_floating_node || root_node->IsDockSpace())
3537                 can_undock_node = true;
3538     }
3539 
3540     const bool clicked = IsMouseClicked(0);
3541     const bool dragging = IsMouseDragging(0, g.IO.MouseDragThreshold * 1.70f);
3542     if (can_undock_node && dragging)
3543         DockContextQueueUndockNode(&g, node); // Will lead to DockNodeStartMouseMovingWindow() -> StartMouseMovingWindow() being called next frame
3544     else if (!can_undock_node && (clicked || dragging) && g.MovingWindow != window)
3545         StartMouseMovingWindow(window);
3546 }
3547 
3548 // Handle mouse moving window
3549 // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
3550 // FIXME: We don't have strong guarantee that g.MovingWindow stay synched with g.ActiveId == g.MovingWindow->MoveId.
3551 // This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs,
3552 // but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other.
UpdateMouseMovingWindowNewFrame()3553 void ImGui::UpdateMouseMovingWindowNewFrame()
3554 {
3555     ImGuiContext& g = *GImGui;
3556     if (g.MovingWindow != NULL)
3557     {
3558         // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
3559         // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
3560         KeepAliveID(g.ActiveId);
3561         IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
3562         ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
3563         if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
3564         {
3565             ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
3566             if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
3567             {
3568                 MarkIniSettingsDirty(moving_window);
3569                 SetWindowPos(moving_window, pos, ImGuiCond_Always);
3570                 if (moving_window->ViewportOwned) // Synchronize viewport immediately because some overlays may relies on clipping rectangle before we Begin() into the window.
3571                     moving_window->Viewport->Pos = pos;
3572             }
3573             FocusWindow(g.MovingWindow);
3574         }
3575         else
3576         {
3577             // Try to merge the window back into the main viewport.
3578             // This works because MouseViewport should be != MovingWindow->Viewport on release (as per code in UpdateViewports)
3579             if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)
3580                 UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseViewport);
3581 
3582             // Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button.
3583             if (!IsDragDropPayloadBeingAccepted())
3584                 g.MouseViewport = moving_window->Viewport;
3585 
3586             // Clear the NoInput window flag set by the Viewport system
3587             moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; // FIXME-VIEWPORT: Test engine managed to crash here because Viewport was NULL.
3588 
3589             ClearActiveID();
3590             g.MovingWindow = NULL;
3591         }
3592     }
3593     else
3594     {
3595         // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
3596         if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
3597         {
3598             KeepAliveID(g.ActiveId);
3599             if (!g.IO.MouseDown[0])
3600                 ClearActiveID();
3601         }
3602     }
3603 }
3604 
3605 // Initiate moving window when clicking on empty space or title bar.
3606 // Handle left-click and right-click focus.
UpdateMouseMovingWindowEndFrame()3607 void ImGui::UpdateMouseMovingWindowEndFrame()
3608 {
3609     ImGuiContext& g = *GImGui;
3610     if (g.ActiveId != 0 || g.HoveredId != 0)
3611         return;
3612 
3613     // Unless we just made a window/popup appear
3614     if (g.NavWindow && g.NavWindow->Appearing)
3615         return;
3616 
3617     // Click on void to focus window and start moving
3618     // (after we're done with all our widgets, so e.g. clicking on docking tab-bar which have set HoveredId already and not get us here!)
3619     if (g.IO.MouseClicked[0])
3620     {
3621         // Handle the edge case of a popup being closed while clicking in its empty space.
3622         // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more.
3623         ImGuiWindow* root_window = g.HoveredWindow ? g.HoveredWindow->RootWindowDockStop : NULL;
3624         const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(root_window->PopupId, ImGuiPopupFlags_AnyPopupLevel);
3625 
3626         if (root_window != NULL && !is_closed_popup)
3627         {
3628             StartMouseMovingWindow(g.HoveredWindow); //-V595
3629 
3630             // Cancel moving if clicked outside of title bar
3631             if (g.IO.ConfigWindowsMoveFromTitleBarOnly)
3632                 if (!(root_window->Flags & ImGuiWindowFlags_NoTitleBar) || root_window->DockIsActive)
3633                     if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
3634                         g.MovingWindow = NULL;
3635 
3636             // Cancel moving if clicked over an item which was disabled or inhibited by popups (note that we know HoveredId == 0 already)
3637             if (g.HoveredIdDisabled)
3638                 g.MovingWindow = NULL;
3639         }
3640         else if (root_window == NULL && g.NavWindow != NULL && GetTopMostPopupModal() == NULL)
3641         {
3642             // Clicking on void disable focus
3643             FocusWindow(NULL);
3644         }
3645     }
3646 
3647     // With right mouse button we close popups without changing focus based on where the mouse is aimed
3648     // Instead, focus will be restored to the window under the bottom-most closed popup.
3649     // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
3650     if (g.IO.MouseClicked[1])
3651     {
3652         // Find the top-most window between HoveredWindow and the top-most Modal Window.
3653         // This is where we can trim the popup stack.
3654         ImGuiWindow* modal = GetTopMostPopupModal();
3655         bool hovered_window_above_modal = g.HoveredWindow && IsWindowAbove(g.HoveredWindow, modal);
3656         ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true);
3657     }
3658 }
3659 
TranslateWindow(ImGuiWindow * window,const ImVec2 & delta)3660 static void TranslateWindow(ImGuiWindow* window, const ImVec2& delta)
3661 {
3662     window->Pos += delta;
3663     window->ClipRect.Translate(delta);
3664     window->OuterRectClipped.Translate(delta);
3665     window->InnerRect.Translate(delta);
3666     window->DC.CursorPos += delta;
3667     window->DC.CursorStartPos += delta;
3668     window->DC.CursorMaxPos += delta;
3669     window->DC.LastItemRect.Translate(delta);
3670     window->DC.LastItemDisplayRect.Translate(delta);
3671 }
3672 
ScaleWindow(ImGuiWindow * window,float scale)3673 static void ScaleWindow(ImGuiWindow* window, float scale)
3674 {
3675     ImVec2 origin = window->Viewport->Pos;
3676     window->Pos = ImFloor((window->Pos - origin) * scale + origin);
3677     window->Size = ImFloor(window->Size * scale);
3678     window->SizeFull = ImFloor(window->SizeFull * scale);
3679     window->ContentSize = ImFloor(window->ContentSize * scale);
3680 }
3681 
IsWindowActiveAndVisible(ImGuiWindow * window)3682 static bool IsWindowActiveAndVisible(ImGuiWindow* window)
3683 {
3684     return (window->Active) && (!window->Hidden);
3685 }
3686 
UpdateMouseInputs()3687 static void ImGui::UpdateMouseInputs()
3688 {
3689     ImGuiContext& g = *GImGui;
3690 
3691     // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
3692     if (IsMousePosValid(&g.IO.MousePos))
3693         g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos);
3694 
3695     // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
3696     if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
3697         g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
3698     else
3699         g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
3700     if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
3701         g.NavDisableMouseHover = false;
3702 
3703     g.IO.MousePosPrev = g.IO.MousePos;
3704     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3705     {
3706         g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
3707         g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
3708         g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
3709         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;
3710         g.IO.MouseDoubleClicked[i] = false;
3711         if (g.IO.MouseClicked[i])
3712         {
3713             if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime)
3714             {
3715                 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3716                 if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
3717                     g.IO.MouseDoubleClicked[i] = true;
3718                 g.IO.MouseClickedTime[i] = -g.IO.MouseDoubleClickTime * 2.0f; // Mark as "old enough" so the third click isn't turned into a double-click
3719             }
3720             else
3721             {
3722                 g.IO.MouseClickedTime[i] = g.Time;
3723             }
3724             g.IO.MouseClickedPos[i] = g.IO.MousePos;
3725             g.IO.MouseDownWasDoubleClick[i] = g.IO.MouseDoubleClicked[i];
3726             g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
3727             g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
3728         }
3729         else if (g.IO.MouseDown[i])
3730         {
3731             // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
3732             ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3733             g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
3734             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);
3735             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);
3736         }
3737         if (!g.IO.MouseDown[i] && !g.IO.MouseReleased[i])
3738             g.IO.MouseDownWasDoubleClick[i] = false;
3739         if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
3740             g.NavDisableMouseHover = false;
3741     }
3742 }
3743 
StartLockWheelingWindow(ImGuiWindow * window)3744 static void StartLockWheelingWindow(ImGuiWindow* window)
3745 {
3746     ImGuiContext& g = *GImGui;
3747     if (g.WheelingWindow == window)
3748         return;
3749     g.WheelingWindow = window;
3750     g.WheelingWindowRefMousePos = g.IO.MousePos;
3751     g.WheelingWindowTimer = WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER;
3752 }
3753 
UpdateMouseWheel()3754 void ImGui::UpdateMouseWheel()
3755 {
3756     ImGuiContext& g = *GImGui;
3757 
3758     // Reset the locked window if we move the mouse or after the timer elapses
3759     if (g.WheelingWindow != NULL)
3760     {
3761         g.WheelingWindowTimer -= g.IO.DeltaTime;
3762         if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
3763             g.WheelingWindowTimer = 0.0f;
3764         if (g.WheelingWindowTimer <= 0.0f)
3765         {
3766             g.WheelingWindow = NULL;
3767             g.WheelingWindowTimer = 0.0f;
3768         }
3769     }
3770 
3771     if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f)
3772         return;
3773 
3774     if ((g.ActiveId != 0 && g.ActiveIdUsingMouseWheel) || (g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrameUsingMouseWheel))
3775         return;
3776 
3777     ImGuiWindow* window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
3778     if (!window || window->Collapsed)
3779         return;
3780 
3781     // Zoom / Scale window
3782     // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
3783     if (g.IO.MouseWheel != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
3784     {
3785         StartLockWheelingWindow(window);
3786         const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
3787         const float scale = new_font_scale / window->FontWindowScale;
3788         window->FontWindowScale = new_font_scale;
3789         if (!(window->Flags & ImGuiWindowFlags_ChildWindow))
3790         {
3791             const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
3792             SetWindowPos(window, window->Pos + offset, 0);
3793             window->Size = ImFloor(window->Size * scale);
3794             window->SizeFull = ImFloor(window->SizeFull * scale);
3795         }
3796         return;
3797     }
3798 
3799     // Mouse wheel scrolling
3800     // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent
3801 
3802     // Vertical Mouse Wheel scrolling
3803     const float wheel_y = (g.IO.MouseWheel != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f;
3804     if (wheel_y != 0.0f && !g.IO.KeyCtrl)
3805     {
3806         StartLockWheelingWindow(window);
3807         while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3808             window = window->ParentWindow;
3809         if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3810         {
3811             float max_step = window->InnerRect.GetHeight() * 0.67f;
3812             float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step));
3813             SetScrollY(window, window->Scroll.y - wheel_y * scroll_step);
3814         }
3815     }
3816 
3817     // Horizontal Mouse Wheel scrolling, or Vertical Mouse Wheel w/ Shift held
3818     const float wheel_x = (g.IO.MouseWheelH != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheelH : (g.IO.MouseWheel != 0.0f && g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f;
3819     if (wheel_x != 0.0f && !g.IO.KeyCtrl)
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.FocusTabPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab));
3839     if (g.ActiveId == 0 && g.FocusTabPressed)
3840     {
3841         // Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also
3842         // manipulate the Next fields even, even though they will be turned into Curr fields by the code below.
3843         g.FocusRequestNextWindow = g.NavWindow;
3844         g.FocusRequestNextCounterRegular = INT_MAX;
3845         if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
3846             g.FocusRequestNextCounterTabStop = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1);
3847         else
3848             g.FocusRequestNextCounterTabStop = g.IO.KeyShift ? -1 : 0;
3849     }
3850 
3851     // Turn queued focus request into current one
3852     g.FocusRequestCurrWindow = NULL;
3853     g.FocusRequestCurrCounterRegular = g.FocusRequestCurrCounterTabStop = INT_MAX;
3854     if (g.FocusRequestNextWindow != NULL)
3855     {
3856         ImGuiWindow* window = g.FocusRequestNextWindow;
3857         g.FocusRequestCurrWindow = window;
3858         if (g.FocusRequestNextCounterRegular != INT_MAX && window->DC.FocusCounterRegular != -1)
3859             g.FocusRequestCurrCounterRegular = ImModPositive(g.FocusRequestNextCounterRegular, window->DC.FocusCounterRegular + 1);
3860         if (g.FocusRequestNextCounterTabStop != INT_MAX && window->DC.FocusCounterTabStop != -1)
3861             g.FocusRequestCurrCounterTabStop = ImModPositive(g.FocusRequestNextCounterTabStop, window->DC.FocusCounterTabStop + 1);
3862         g.FocusRequestNextWindow = NULL;
3863         g.FocusRequestNextCounterRegular = g.FocusRequestNextCounterTabStop = INT_MAX;
3864     }
3865 
3866     g.NavIdTabCounter = INT_MAX;
3867 }
3868 
3869 // 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()3870 void ImGui::UpdateHoveredWindowAndCaptureFlags()
3871 {
3872     ImGuiContext& g = *GImGui;
3873 
3874     // Find the window hovered by mouse:
3875     // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3876     // - 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.
3877     // - 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.
3878     bool clear_hovered_windows = false;
3879     FindHoveredWindow();
3880     IM_ASSERT(g.HoveredWindow == NULL || g.HoveredWindow == g.MovingWindow || g.HoveredWindow->Viewport == g.MouseViewport);
3881 
3882     // Modal windows prevents mouse from hovering behind them.
3883     ImGuiWindow* modal_window = GetTopMostPopupModal();
3884     if (modal_window && g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window))
3885         clear_hovered_windows = true;
3886 
3887     // Disabled mouse?
3888     if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
3889         clear_hovered_windows = true;
3890 
3891     // We track click ownership. When clicked outside of a window the click is owned by the application and won't report hovering nor request capture even while dragging over our windows afterward.
3892     int mouse_earliest_button_down = -1;
3893     bool mouse_any_down = false;
3894     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3895     {
3896         if (g.IO.MouseClicked[i])
3897             g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (g.OpenPopupStack.Size > 0);
3898         mouse_any_down |= g.IO.MouseDown[i];
3899         if (g.IO.MouseDown[i])
3900             if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
3901                 mouse_earliest_button_down = i;
3902     }
3903     const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
3904 
3905     // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
3906     // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
3907     const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
3908     if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
3909         clear_hovered_windows = true;
3910 
3911     if (clear_hovered_windows)
3912         g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL;
3913 
3914     // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app)
3915     if (g.WantCaptureMouseNextFrame != -1)
3916         g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
3917     else
3918         g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.OpenPopupStack.Size > 0);
3919 
3920     // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app)
3921     if (g.WantCaptureKeyboardNextFrame != -1)
3922         g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
3923     else
3924         g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
3925     if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
3926         g.IO.WantCaptureKeyboard = true;
3927 
3928     // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
3929     g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
3930 }
3931 
GetMergedKeyModFlags()3932 ImGuiKeyModFlags ImGui::GetMergedKeyModFlags()
3933 {
3934     ImGuiContext& g = *GImGui;
3935     ImGuiKeyModFlags key_mod_flags = ImGuiKeyModFlags_None;
3936     if (g.IO.KeyCtrl)   { key_mod_flags |= ImGuiKeyModFlags_Ctrl; }
3937     if (g.IO.KeyShift)  { key_mod_flags |= ImGuiKeyModFlags_Shift; }
3938     if (g.IO.KeyAlt)    { key_mod_flags |= ImGuiKeyModFlags_Alt; }
3939     if (g.IO.KeySuper)  { key_mod_flags |= ImGuiKeyModFlags_Super; }
3940     return key_mod_flags;
3941 }
3942 
NewFrame()3943 void ImGui::NewFrame()
3944 {
3945     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3946     ImGuiContext& g = *GImGui;
3947 
3948     CallContextHooks(&g, ImGuiContextHookType_NewFramePre);
3949 
3950     // Check and assert for various common IO and Configuration mistakes
3951     g.ConfigFlagsLastFrame = g.ConfigFlagsCurrFrame;
3952     ErrorCheckNewFrameSanityChecks();
3953     g.ConfigFlagsCurrFrame = g.IO.ConfigFlags;
3954 
3955     // Load settings on first frame, save settings when modified (after a delay)
3956     UpdateSettings();
3957 
3958     g.Time += g.IO.DeltaTime;
3959     g.WithinFrameScope = true;
3960     g.FrameCount += 1;
3961     g.TooltipOverrideCount = 0;
3962     g.WindowsActiveCount = 0;
3963     g.MenusIdSubmittedThisFrame.resize(0);
3964 
3965     // Calculate frame-rate for the user, as a purely luxurious feature
3966     g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
3967     g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
3968     g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
3969     g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX;
3970 
3971     UpdateViewportsNewFrame();
3972 
3973     // Setup current font and draw list shared data
3974     // FIXME-VIEWPORT: the concept of a single ClipRectFullscreen is not ideal!
3975     g.IO.Fonts->Locked = true;
3976     SetCurrentFont(GetDefaultFont());
3977     IM_ASSERT(g.Font->IsLoaded());
3978     ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
3979     for (int n = 0; n < g.Viewports.Size; n++)
3980         virtual_space.Add(g.Viewports[n]->GetMainRect());
3981     g.DrawListSharedData.ClipRectFullscreen = ImVec4(virtual_space.Min.x, virtual_space.Min.y, virtual_space.Max.x, virtual_space.Max.y);
3982     g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
3983     g.DrawListSharedData.SetCircleSegmentMaxError(g.Style.CircleSegmentMaxError);
3984     g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
3985     if (g.Style.AntiAliasedLines)
3986         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
3987     if (g.Style.AntiAliasedLinesUseTex && !(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines))
3988         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex;
3989     if (g.Style.AntiAliasedFill)
3990         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
3991     if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
3992         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
3993 
3994     // Mark rendering data as invalid to prevent user who may have a handle on it to use it.
3995     for (int n = 0; n < g.Viewports.Size; n++)
3996     {
3997         ImGuiViewportP* viewport = g.Viewports[n];
3998         viewport->DrawData = NULL;
3999         viewport->DrawDataP.Clear();
4000     }
4001 
4002     // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
4003     if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
4004         KeepAliveID(g.DragDropPayload.SourceId);
4005 
4006     // Update HoveredId data
4007     if (!g.HoveredIdPreviousFrame)
4008         g.HoveredIdTimer = 0.0f;
4009     if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
4010         g.HoveredIdNotActiveTimer = 0.0f;
4011     if (g.HoveredId)
4012         g.HoveredIdTimer += g.IO.DeltaTime;
4013     if (g.HoveredId && g.ActiveId != g.HoveredId)
4014         g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
4015     g.HoveredIdPreviousFrame = g.HoveredId;
4016     g.HoveredIdPreviousFrameUsingMouseWheel = g.HoveredIdUsingMouseWheel;
4017     g.HoveredId = 0;
4018     g.HoveredIdAllowOverlap = false;
4019     g.HoveredIdUsingMouseWheel = false;
4020     g.HoveredIdDisabled = false;
4021 
4022     // Update ActiveId data (clear reference to active widget if the widget isn't alive anymore)
4023     if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
4024         ClearActiveID();
4025     if (g.ActiveId)
4026         g.ActiveIdTimer += g.IO.DeltaTime;
4027     g.LastActiveIdTimer += g.IO.DeltaTime;
4028     g.ActiveIdPreviousFrame = g.ActiveId;
4029     g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
4030     g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore;
4031     g.ActiveIdIsAlive = 0;
4032     g.ActiveIdHasBeenEditedThisFrame = false;
4033     g.ActiveIdPreviousFrameIsAlive = false;
4034     g.ActiveIdIsJustActivated = false;
4035     if (g.TempInputId != 0 && g.ActiveId != g.TempInputId)
4036         g.TempInputId = 0;
4037     if (g.ActiveId == 0)
4038     {
4039         g.ActiveIdUsingNavDirMask = 0x00;
4040         g.ActiveIdUsingNavInputMask = 0x00;
4041         g.ActiveIdUsingKeyInputMask = 0x00;
4042     }
4043 
4044     // Drag and drop
4045     g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
4046     g.DragDropAcceptIdCurr = 0;
4047     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
4048     g.DragDropWithinSource = false;
4049     g.DragDropWithinTarget = false;
4050     g.DragDropHoldJustPressedId = 0;
4051 
4052     // Update keyboard input state
4053     // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools
4054     g.IO.KeyMods = GetMergedKeyModFlags();
4055     memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
4056     for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
4057         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;
4058 
4059     // Update gamepad/keyboard navigation
4060     NavUpdate();
4061 
4062     // Update mouse input state
4063     UpdateMouseInputs();
4064 
4065     // Undocking
4066     // (needs to be before UpdateMouseMovingWindowNewFrame so the window is already offset and following the mouse on the detaching frame)
4067     DockContextNewFrameUpdateUndocking(&g);
4068 
4069     // Find hovered window
4070     // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
4071     UpdateHoveredWindowAndCaptureFlags();
4072 
4073     // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
4074     UpdateMouseMovingWindowNewFrame();
4075 
4076     // Background darkening/whitening
4077     if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
4078         g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
4079     else
4080         g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
4081 
4082     g.MouseCursor = ImGuiMouseCursor_Arrow;
4083     g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
4084     g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
4085     g.PlatformImePosViewport = NULL;
4086 
4087     // Mouse wheel scrolling, scale
4088     UpdateMouseWheel();
4089 
4090     // Update legacy TAB focus
4091     UpdateTabFocus();
4092 
4093     // Mark all windows as not visible and compact unused memory.
4094     IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size);
4095     const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer;
4096     for (int i = 0; i != g.Windows.Size; i++)
4097     {
4098         ImGuiWindow* window = g.Windows[i];
4099         window->WasActive = window->Active;
4100         window->BeginCount = 0;
4101         window->Active = false;
4102         window->WriteAccessed = false;
4103 
4104         // Garbage collect transient buffers of recently unused windows
4105         if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
4106             GcCompactTransientWindowBuffers(window);
4107     }
4108 
4109     // Garbage collect transient buffers of recently unused tables
4110     for (int i = 0; i < g.TablesLastTimeActive.Size; i++)
4111         if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time)
4112             TableGcCompactTransientBuffers(g.Tables.GetByIndex(i));
4113     if (g.GcCompactAll)
4114         GcCompactTransientMiscBuffers();
4115     g.GcCompactAll = false;
4116 
4117     // Closing the focused window restore focus to the first active root window in descending z-order
4118     if (g.NavWindow && !g.NavWindow->WasActive)
4119         FocusTopMostWindowUnderOne(NULL, NULL);
4120 
4121     // No window should be open at the beginning of the frame.
4122     // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
4123     g.CurrentWindowStack.resize(0);
4124     g.BeginPopupStack.resize(0);
4125     g.ItemFlagsStack.resize(0);
4126     g.ItemFlagsStack.push_back(ImGuiItemFlags_Default_);
4127     g.GroupStack.resize(0);
4128     ClosePopupsOverWindow(g.NavWindow, false);
4129 
4130     // Docking
4131     DockContextNewFrameUpdateDocking(&g);
4132 
4133     // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
4134     UpdateDebugToolItemPicker();
4135 
4136     // Create implicit/fallback window - which we will only render it if the user has added something to it.
4137     // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
4138     // This fallback is particularly important as it avoid ImGui:: calls from crashing.
4139     g.WithinFrameScopeWithImplicitWindow = true;
4140     SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
4141     Begin("Debug##Default");
4142     IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true);
4143 
4144     CallContextHooks(&g, ImGuiContextHookType_NewFramePost);
4145 }
4146 
4147 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
UpdateDebugToolItemPicker()4148 void ImGui::UpdateDebugToolItemPicker()
4149 {
4150     ImGuiContext& g = *GImGui;
4151     g.DebugItemPickerBreakId = 0;
4152     if (g.DebugItemPickerActive)
4153     {
4154         const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
4155         ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
4156         if (ImGui::IsKeyPressedMap(ImGuiKey_Escape))
4157             g.DebugItemPickerActive = false;
4158         if (ImGui::IsMouseClicked(0) && hovered_id)
4159         {
4160             g.DebugItemPickerBreakId = hovered_id;
4161             g.DebugItemPickerActive = false;
4162         }
4163         ImGui::SetNextWindowBgAlpha(0.60f);
4164         ImGui::BeginTooltip();
4165         ImGui::Text("HoveredId: 0x%08X", hovered_id);
4166         ImGui::Text("Press ESC to abort picking.");
4167         ImGui::TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!");
4168         ImGui::EndTooltip();
4169     }
4170 }
4171 
Initialize(ImGuiContext * context)4172 void ImGui::Initialize(ImGuiContext* context)
4173 {
4174     ImGuiContext& g = *context;
4175     IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
4176 
4177     // Add .ini handle for ImGuiWindow type
4178     {
4179         ImGuiSettingsHandler ini_handler;
4180         ini_handler.TypeName = "Window";
4181         ini_handler.TypeHash = ImHashStr("Window");
4182         ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll;
4183         ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen;
4184         ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;
4185         ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll;
4186         ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll;
4187         g.SettingsHandlers.push_back(ini_handler);
4188     }
4189 
4190 #ifdef IMGUI_HAS_TABLE
4191     // Add .ini handle for ImGuiTable type
4192     TableSettingsInstallHandler(context);
4193 #endif // #ifdef IMGUI_HAS_TABLE
4194 
4195 #ifdef IMGUI_HAS_DOCK
4196     // Create default viewport
4197     ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)();
4198     viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID;
4199     viewport->Idx = 0;
4200     viewport->PlatformWindowCreated = true;
4201     g.Viewports.push_back(viewport);
4202     g.PlatformIO.MainViewport = g.Viewports[0]; // Make it accessible in public-facing GetPlatformIO() immediately (before the first call to EndFrame)
4203     g.PlatformIO.Viewports.push_back(g.Viewports[0]);
4204 
4205     // Extensions
4206     DockContextInitialize(&g);
4207 #endif // #ifdef IMGUI_HAS_DOCK
4208 
4209     g.Initialized = true;
4210 }
4211 
4212 // This function is merely here to free heap allocations.
Shutdown(ImGuiContext * context)4213 void ImGui::Shutdown(ImGuiContext* context)
4214 {
4215     // 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)
4216     ImGuiContext& g = *context;
4217     if (g.IO.Fonts && g.FontAtlasOwnedByContext)
4218     {
4219         g.IO.Fonts->Locked = false;
4220         IM_DELETE(g.IO.Fonts);
4221     }
4222     g.IO.Fonts = NULL;
4223 
4224     // Cleanup of other data are conditional on actually having initialized Dear ImGui.
4225     if (!g.Initialized)
4226         return;
4227 
4228     // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
4229     if (g.SettingsLoaded && g.IO.IniFilename != NULL)
4230     {
4231         ImGuiContext* backup_context = GImGui;
4232         SetCurrentContext(&g);
4233         SaveIniSettingsToDisk(g.IO.IniFilename);
4234         SetCurrentContext(backup_context);
4235     }
4236 
4237     // Destroy platform windows
4238     ImGuiContext* backup_context = ImGui::GetCurrentContext();
4239     SetCurrentContext(context);
4240     DestroyPlatformWindows();
4241     SetCurrentContext(backup_context);
4242 
4243     // Shutdown extensions
4244     DockContextShutdown(&g);
4245 
4246     CallContextHooks(&g, ImGuiContextHookType_Shutdown);
4247 
4248     // Clear everything else
4249     for (int i = 0; i < g.Windows.Size; i++)
4250         IM_DELETE(g.Windows[i]);
4251     g.Windows.clear();
4252     g.WindowsFocusOrder.clear();
4253     g.WindowsTempSortBuffer.clear();
4254     g.CurrentWindow = NULL;
4255     g.CurrentWindowStack.clear();
4256     g.WindowsById.Clear();
4257     g.NavWindow = NULL;
4258     g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL;
4259     g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
4260     g.MovingWindow = NULL;
4261     g.ColorStack.clear();
4262     g.StyleVarStack.clear();
4263     g.FontStack.clear();
4264     g.OpenPopupStack.clear();
4265     g.BeginPopupStack.clear();
4266 
4267     g.CurrentViewport = g.MouseViewport = g.MouseLastHoveredViewport = NULL;
4268     for (int i = 0; i < g.Viewports.Size; i++)
4269         IM_DELETE(g.Viewports[i]);
4270     g.Viewports.clear();
4271 
4272     g.TabBars.Clear();
4273     g.CurrentTabBarStack.clear();
4274     g.ShrinkWidthBuffer.clear();
4275 
4276     g.Tables.Clear();
4277     g.CurrentTableStack.clear();
4278     g.DrawChannelsTempMergeBuffer.clear();
4279 
4280     g.ClipboardHandlerData.clear();
4281     g.MenusIdSubmittedThisFrame.clear();
4282     g.InputTextState.ClearFreeMemory();
4283 
4284     g.SettingsWindows.clear();
4285     g.SettingsHandlers.clear();
4286 
4287     if (g.LogFile)
4288     {
4289 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
4290         if (g.LogFile != stdout)
4291 #endif
4292             ImFileClose(g.LogFile);
4293         g.LogFile = NULL;
4294     }
4295     g.LogBuffer.clear();
4296 
4297     g.Initialized = false;
4298 }
4299 
4300 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)4301 static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
4302 {
4303     const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
4304     const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
4305     if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
4306         return d;
4307     if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
4308         return d;
4309     return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
4310 }
4311 
AddWindowToSortBuffer(ImVector<ImGuiWindow * > * out_sorted_windows,ImGuiWindow * window)4312 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
4313 {
4314     out_sorted_windows->push_back(window);
4315     if (window->Active)
4316     {
4317         int count = window->DC.ChildWindows.Size;
4318         if (count > 1)
4319             ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
4320         for (int i = 0; i < count; i++)
4321         {
4322             ImGuiWindow* child = window->DC.ChildWindows[i];
4323             if (child->Active)
4324                 AddWindowToSortBuffer(out_sorted_windows, child);
4325         }
4326     }
4327 }
4328 
AddDrawListToDrawData(ImVector<ImDrawList * > * out_list,ImDrawList * draw_list)4329 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
4330 {
4331     // Remove trailing command if unused.
4332     // Technically we could return directly instead of popping, but this make things looks neat in Metrics/Debugger window as well.
4333     draw_list->_PopUnusedDrawCmd();
4334     if (draw_list->CmdBuffer.Size == 0)
4335         return;
4336 
4337     // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
4338     // May trigger for you if you are using PrimXXX functions incorrectly.
4339     IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
4340     IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
4341     if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset))
4342         IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
4343 
4344     // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
4345     // If this assert triggers because you are drawing lots of stuff manually:
4346     // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds.
4347     //   Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents.
4348     // - If you want large meshes with more than 64K vertices, you can either:
4349     //   (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'.
4350     //       Most example backends already support this from 1.71. Pre-1.71 backends won't.
4351     //       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.
4352     //   (B) Or handle 32-bit indices in your renderer backend, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h.
4353     //       Most example backends already support this. For example, the OpenGL example code detect index size at compile-time:
4354     //         glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
4355     //       Your own engine or render API may use different parameters or function calls to specify index sizes.
4356     //       2 and 4 bytes indices are generally supported by most graphics API.
4357     // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching
4358     //   the 64K limit to split your draw commands in multiple draw lists.
4359     if (sizeof(ImDrawIdx) == 2)
4360         IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
4361 
4362     out_list->push_back(draw_list);
4363 }
4364 
AddWindowToDrawData(ImGuiWindow * window,int layer)4365 static void AddWindowToDrawData(ImGuiWindow* window, int layer)
4366 {
4367     ImGuiContext& g = *GImGui;
4368     g.IO.MetricsRenderWindows++;
4369     AddDrawListToDrawData(&window->Viewport->DrawDataBuilder.Layers[layer], window->DrawList);
4370     for (int i = 0; i < window->DC.ChildWindows.Size; i++)
4371     {
4372         ImGuiWindow* child = window->DC.ChildWindows[i];
4373         if (IsWindowActiveAndVisible(child)) // Clipped children may have been marked not active
4374             AddWindowToDrawData(child, layer);
4375     }
4376 }
4377 
4378 // Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)
AddRootWindowToDrawData(ImGuiWindow * window)4379 static void AddRootWindowToDrawData(ImGuiWindow* window)
4380 {
4381     int layer = (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0;
4382     AddWindowToDrawData(window, layer);
4383 }
4384 
FlattenIntoSingleLayer()4385 void ImDrawDataBuilder::FlattenIntoSingleLayer()
4386 {
4387     int n = Layers[0].Size;
4388     int size = n;
4389     for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
4390         size += Layers[i].Size;
4391     Layers[0].resize(size);
4392     for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
4393     {
4394         ImVector<ImDrawList*>& layer = Layers[layer_n];
4395         if (layer.empty())
4396             continue;
4397         memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
4398         n += layer.Size;
4399         layer.resize(0);
4400     }
4401 }
4402 
SetupViewportDrawData(ImGuiViewportP * viewport,ImVector<ImDrawList * > * draw_lists)4403 static void SetupViewportDrawData(ImGuiViewportP* viewport, ImVector<ImDrawList*>* draw_lists)
4404 {
4405     // When minimized, we report draw_data->DisplaySize as zero to be consistent with non-viewport mode,
4406     // and to allow applications/backends to easily skip rendering.
4407     // FIXME: Note that we however do NOT attempt to report "zero drawlist / vertices" into the ImDrawData structure.
4408     // This is because the work has been done already, and its wasted! We should fix that and add optimizations for
4409     // it earlier in the pipeline, rather than pretend to hide the data at the end of the pipeline.
4410     const bool is_minimized = (viewport->Flags & ImGuiViewportFlags_Minimized) != 0;
4411 
4412     ImDrawData* draw_data = &viewport->DrawDataP;
4413     viewport->DrawData = draw_data; // Make publicly accessible
4414     draw_data->Valid = true;
4415     draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
4416     draw_data->CmdListsCount = draw_lists->Size;
4417     draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
4418     draw_data->DisplayPos = viewport->Pos;
4419     draw_data->DisplaySize = is_minimized ? ImVec2(0.0f, 0.0f) : viewport->Size;
4420     draw_data->FramebufferScale = ImGui::GetIO().DisplayFramebufferScale; // FIXME-VIEWPORT: This may vary on a per-monitor/viewport basis?
4421     draw_data->OwnerViewport = viewport;
4422     for (int n = 0; n < draw_lists->Size; n++)
4423     {
4424         draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size;
4425         draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size;
4426     }
4427 }
4428 
4429 // Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering.
4430 // - When using this function it is sane to ensure that float are perfectly rounded to integer values,
4431 //   so that e.g. (int)(max.x-min.x) in user's render produce correct result.
4432 // - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect():
4433 //   some frequently called functions which to modify both channels and clipping simultaneously tend to use the
4434 //   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)4435 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
4436 {
4437     ImGuiWindow* window = GetCurrentWindow();
4438     window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
4439     window->ClipRect = window->DrawList->_ClipRectStack.back();
4440 }
4441 
PopClipRect()4442 void ImGui::PopClipRect()
4443 {
4444     ImGuiWindow* window = GetCurrentWindow();
4445     window->DrawList->PopClipRect();
4446     window->ClipRect = window->DrawList->_ClipRectStack.back();
4447 }
4448 
FindFrontMostVisibleChildWindow(ImGuiWindow * window)4449 static ImGuiWindow* FindFrontMostVisibleChildWindow(ImGuiWindow* window)
4450 {
4451     for (int n = window->DC.ChildWindows.Size - 1; n >= 0; n--)
4452         if (IsWindowActiveAndVisible(window->DC.ChildWindows[n]))
4453             return FindFrontMostVisibleChildWindow(window->DC.ChildWindows[n]);
4454     return window;
4455 }
4456 
EndFrameDrawDimmedBackgrounds()4457 static void ImGui::EndFrameDrawDimmedBackgrounds()
4458 {
4459     ImGuiContext& g = *GImGui;
4460 
4461     // Draw modal whitening background on _other_ viewports than the one the modal is one
4462     ImGuiWindow* modal_window = GetTopMostPopupModal();
4463     const bool dim_bg_for_modal = (modal_window != NULL);
4464     const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL);
4465     if (dim_bg_for_modal || dim_bg_for_window_list)
4466         for (int viewport_n = 0; viewport_n < g.Viewports.Size; viewport_n++)
4467         {
4468             ImGuiViewportP* viewport = g.Viewports[viewport_n];
4469             if (modal_window && viewport == modal_window->Viewport)
4470                 continue;
4471             if (g.NavWindowingListWindow && viewport == g.NavWindowingListWindow->Viewport)
4472                 continue;
4473             if (g.NavWindowingTargetAnim && viewport == g.NavWindowingTargetAnim->Viewport)
4474                 continue;
4475             if (viewport->Window && modal_window && IsWindowAbove(viewport->Window, modal_window))
4476                 continue;
4477             ImDrawList* draw_list = GetForegroundDrawList(viewport);
4478             const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
4479             draw_list->AddRectFilled(viewport->Pos, viewport->Pos + viewport->Size, dim_bg_col);
4480         }
4481 
4482     // Draw modal whitening background between CTRL-TAB list
4483     if (dim_bg_for_window_list && g.NavWindowingTargetAnim->Active)
4484     {
4485         // Choose a draw list that will be front-most across all our children
4486         // In the unlikely case that the window wasn't made active we can't rely on its drawlist and skip rendering all-together.
4487         ImGuiWindow* window = g.NavWindowingTargetAnim;
4488         ImDrawList* draw_list = FindFrontMostVisibleChildWindow(window->RootWindow)->DrawList;
4489         draw_list->PushClipRectFullScreen();
4490 
4491         // Docking: draw modal whitening background on other nodes of a same dock tree
4492         // For CTRL+TAB within a docking node we need to render the dimming background in 8 steps
4493         // (Because the root node renders the background in one shot, in order to avoid flickering when a child dock node is not submitted)
4494         if (window->RootWindowDockStop->DockIsActive)
4495             if (window->RootWindow != window->RootWindowDockStop)
4496                 RenderRectFilledWithHole(draw_list, window->RootWindow->Rect(), window->RootWindowDockStop->Rect(), GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio), g.Style.WindowRounding);
4497 
4498         // Draw navigation selection/windowing rectangle border
4499         float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
4500         ImRect bb = window->Rect();
4501         bb.Expand(g.FontSize);
4502         if (bb.Contains(window->Viewport->GetMainRect())) // If a window fits the entire viewport, adjust its highlight inward
4503         {
4504             bb.Expand(-g.FontSize - 1.0f);
4505             rounding = window->WindowRounding;
4506         }
4507         draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f);
4508         draw_list->PopClipRect();
4509     }
4510 }
4511 
4512 // 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()4513 void ImGui::EndFrame()
4514 {
4515     ImGuiContext& g = *GImGui;
4516     IM_ASSERT(g.Initialized);
4517 
4518     // Don't process EndFrame() multiple times.
4519     if (g.FrameCountEnded == g.FrameCount)
4520         return;
4521     IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?");
4522 
4523     CallContextHooks(&g, ImGuiContextHookType_EndFramePre);
4524 
4525     ErrorCheckEndFrameSanityChecks();
4526 
4527     // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
4528     if (g.PlatformIO.Platform_SetImeInputPos && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImePos - g.PlatformImeLastPos) > 0.0001f))
4529         if (g.PlatformImePosViewport && g.PlatformImePosViewport->PlatformWindowCreated)
4530         {
4531             g.PlatformIO.Platform_SetImeInputPos(g.PlatformImePosViewport, g.PlatformImePos);
4532             g.PlatformImeLastPos = g.PlatformImePos;
4533             g.PlatformImePosViewport = NULL;
4534         }
4535 
4536     // Hide implicit/fallback "Debug" window if it hasn't been used
4537     g.WithinFrameScopeWithImplicitWindow = false;
4538     if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
4539         g.CurrentWindow->Active = false;
4540     End();
4541 
4542     // Draw modal whitening background on _other_ viewports than the one the modal is one
4543     EndFrameDrawDimmedBackgrounds();
4544 
4545     // Update navigation: CTRL+Tab, wrap-around requests
4546     NavEndFrame();
4547 
4548     SetCurrentViewport(NULL, NULL);
4549 
4550     // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
4551     if (g.DragDropActive)
4552     {
4553         bool is_delivered = g.DragDropPayload.Delivery;
4554         bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton));
4555         if (is_delivered || is_elapsed)
4556             ClearDragDrop();
4557     }
4558 
4559     // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.
4560     if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
4561     {
4562         g.DragDropWithinSource = true;
4563         SetTooltip("...");
4564         g.DragDropWithinSource = false;
4565     }
4566 
4567     // End frame
4568     g.WithinFrameScope = false;
4569     g.FrameCountEnded = g.FrameCount;
4570 
4571     // Initiate moving window + handle left-click and right-click focus
4572     UpdateMouseMovingWindowEndFrame();
4573 
4574     // Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some)
4575     UpdateViewportsEndFrame();
4576 
4577     // Sort the window list so that all child windows are after their parent
4578     // We cannot do that on FocusWindow() because children may not exist yet
4579     g.WindowsTempSortBuffer.resize(0);
4580     g.WindowsTempSortBuffer.reserve(g.Windows.Size);
4581     for (int i = 0; i != g.Windows.Size; i++)
4582     {
4583         ImGuiWindow* window = g.Windows[i];
4584         if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow))       // if a child is active its parent will add it
4585             continue;
4586         AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window);
4587     }
4588 
4589     // 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.
4590     IM_ASSERT(g.Windows.Size == g.WindowsTempSortBuffer.Size);
4591     g.Windows.swap(g.WindowsTempSortBuffer);
4592     g.IO.MetricsActiveWindows = g.WindowsActiveCount;
4593 
4594     // Unlock font atlas
4595     g.IO.Fonts->Locked = false;
4596 
4597     // Clear Input data for next frame
4598     g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
4599     g.IO.InputQueueCharacters.resize(0);
4600     memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
4601 
4602     CallContextHooks(&g, ImGuiContextHookType_EndFramePost);
4603 }
4604 
Render()4605 void ImGui::Render()
4606 {
4607     ImGuiContext& g = *GImGui;
4608     IM_ASSERT(g.Initialized);
4609 
4610     if (g.FrameCountEnded != g.FrameCount)
4611         EndFrame();
4612     g.FrameCountRendered = g.FrameCount;
4613     g.IO.MetricsRenderWindows = 0;
4614 
4615     CallContextHooks(&g, ImGuiContextHookType_RenderPre);
4616 
4617     // Add background ImDrawList (for each active viewport)
4618     for (int n = 0; n != g.Viewports.Size; n++)
4619     {
4620         ImGuiViewportP* viewport = g.Viewports[n];
4621         viewport->DrawDataBuilder.Clear();
4622         if (viewport->DrawLists[0] != NULL)
4623             AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport));
4624     }
4625 
4626     // Add ImDrawList to render
4627     ImGuiWindow* windows_to_render_top_most[2];
4628     windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
4629     windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL);
4630     for (int n = 0; n != g.Windows.Size; n++)
4631     {
4632         ImGuiWindow* window = g.Windows[n];
4633         if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])
4634             AddRootWindowToDrawData(window);
4635     }
4636     for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++)
4637         if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window
4638             AddRootWindowToDrawData(windows_to_render_top_most[n]);
4639 
4640     ImVec2 mouse_cursor_offset, mouse_cursor_size, mouse_cursor_uv[4];
4641     if (g.IO.MouseDrawCursor && g.MouseCursor != ImGuiMouseCursor_None)
4642         g.IO.Fonts->GetMouseCursorTexData(g.MouseCursor, &mouse_cursor_offset, &mouse_cursor_size, &mouse_cursor_uv[0], &mouse_cursor_uv[2]);
4643 
4644     // Setup ImDrawData structures for end-user
4645     g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0;
4646     for (int n = 0; n < g.Viewports.Size; n++)
4647     {
4648         ImGuiViewportP* viewport = g.Viewports[n];
4649         viewport->DrawDataBuilder.FlattenIntoSingleLayer();
4650 
4651         // Draw software mouse cursor if requested by io.MouseDrawCursor flag
4652         // (note we scale cursor by current viewport/monitor, however Windows 10 for its own hardware cursor seems to be using a different scale factor)
4653         if (mouse_cursor_size.x > 0.0f && mouse_cursor_size.y > 0.0f)
4654         {
4655             float scale = g.Style.MouseCursorScale * viewport->DpiScale;
4656             if (viewport->GetMainRect().Overlaps(ImRect(g.IO.MousePos, g.IO.MousePos + ImVec2(mouse_cursor_size.x + 2, mouse_cursor_size.y + 2) * scale)))
4657                 RenderMouseCursor(GetForegroundDrawList(viewport), g.IO.MousePos, scale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
4658         }
4659 
4660         // Add foreground ImDrawList (for each active viewport)
4661         if (viewport->DrawLists[1] != NULL)
4662             AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport));
4663 
4664         SetupViewportDrawData(viewport, &viewport->DrawDataBuilder.Layers[0]);
4665         g.IO.MetricsRenderVertices += viewport->DrawData->TotalVtxCount;
4666         g.IO.MetricsRenderIndices += viewport->DrawData->TotalIdxCount;
4667     }
4668 
4669     CallContextHooks(&g, ImGuiContextHookType_RenderPost);
4670 }
4671 
4672 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
4673 // 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)4674 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
4675 {
4676     ImGuiContext& g = *GImGui;
4677 
4678     const char* text_display_end;
4679     if (hide_text_after_double_hash)
4680         text_display_end = FindRenderedTextEnd(text, text_end);      // Hide anything after a '##' string
4681     else
4682         text_display_end = text_end;
4683 
4684     ImFont* font = g.Font;
4685     const float font_size = g.FontSize;
4686     if (text == text_display_end)
4687         return ImVec2(0.0f, font_size);
4688     ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
4689 
4690     // Round
4691     text_size.x = IM_FLOOR(text_size.x + 0.95f);
4692 
4693     return text_size;
4694 }
4695 
4696 // Find window given position, search front-to-back
4697 // FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically
4698 // with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
4699 // called, aka before the next Begin(). Moving window isn't affected.
FindHoveredWindow()4700 static void FindHoveredWindow()
4701 {
4702     ImGuiContext& g = *GImGui;
4703 
4704     // Special handling for the window being moved: Ignore the mouse viewport check (because it may reset/lose its viewport during the undocking frame)
4705     ImGuiViewportP* moving_window_viewport = g.MovingWindow ? g.MovingWindow->Viewport : NULL;
4706     if (g.MovingWindow)
4707         g.MovingWindow->Viewport = g.MouseViewport;
4708 
4709     ImGuiWindow* hovered_window = NULL;
4710     ImGuiWindow* hovered_window_ignoring_moving_window = NULL;
4711     if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
4712         hovered_window = g.MovingWindow;
4713 
4714     ImVec2 padding_regular = g.Style.TouchExtraPadding;
4715     ImVec2 padding_for_resize_from_edges = g.IO.ConfigWindowsResizeFromEdges ? ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS)) : padding_regular;
4716     for (int i = g.Windows.Size - 1; i >= 0; i--)
4717     {
4718         ImGuiWindow* window = g.Windows[i];
4719         if (!window->Active || window->Hidden)
4720             continue;
4721         if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
4722             continue;
4723         IM_ASSERT(window->Viewport);
4724         if (window->Viewport != g.MouseViewport)
4725             continue;
4726 
4727         // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
4728         ImRect bb(window->OuterRectClipped);
4729         if (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize))
4730             bb.Expand(padding_regular);
4731         else
4732             bb.Expand(padding_for_resize_from_edges);
4733         if (!bb.Contains(g.IO.MousePos))
4734             continue;
4735 
4736         // Support for one rectangular hole in any given window
4737         // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512)
4738         if (window->HitTestHoleSize.x != 0)
4739         {
4740             ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y);
4741             ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y);
4742             if (ImRect(hole_pos, hole_pos + hole_size).Contains(g.IO.MousePos))
4743                 continue;
4744         }
4745 
4746         if (hovered_window == NULL)
4747             hovered_window = window;
4748         if (hovered_window_ignoring_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow))
4749             hovered_window_ignoring_moving_window = window;
4750         if (hovered_window && hovered_window_ignoring_moving_window)
4751             break;
4752     }
4753 
4754     g.HoveredWindow = hovered_window;
4755     g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
4756     g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window;
4757 
4758     if (g.MovingWindow)
4759         g.MovingWindow->Viewport = moving_window_viewport;
4760 }
4761 
4762 // Test if mouse cursor is hovering given rectangle
4763 // NB- Rectangle is clipped by our current clip setting
4764 // 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)4765 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
4766 {
4767     ImGuiContext& g = *GImGui;
4768 
4769     // Clip
4770     ImRect rect_clipped(r_min, r_max);
4771     if (clip)
4772         rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
4773 
4774     // Expand for touch input
4775     const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
4776     if (!rect_for_touch.Contains(g.IO.MousePos))
4777         return false;
4778     if (!g.MouseViewport->GetMainRect().Overlaps(rect_clipped))
4779         return false;
4780     return true;
4781 }
4782 
GetKeyIndex(ImGuiKey imgui_key)4783 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
4784 {
4785     IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
4786     ImGuiContext& g = *GImGui;
4787     return g.IO.KeyMap[imgui_key];
4788 }
4789 
4790 // Note that dear imgui doesn't know the semantic of each entry of io.KeysDown[]!
4791 // Use your own indices/enums according to how your backend/engine stored them into io.KeysDown[]!
IsKeyDown(int user_key_index)4792 bool ImGui::IsKeyDown(int user_key_index)
4793 {
4794     if (user_key_index < 0)
4795         return false;
4796     ImGuiContext& g = *GImGui;
4797     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4798     return g.IO.KeysDown[user_key_index];
4799 }
4800 
4801 // t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
4802 // t1 = current time (e.g.: g.Time)
4803 // An event is triggered at:
4804 //  t = 0.0f     t = repeat_delay,    t = repeat_delay + repeat_rate*N
CalcTypematicRepeatAmount(float t0,float t1,float repeat_delay,float repeat_rate)4805 int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)
4806 {
4807     if (t1 == 0.0f)
4808         return 1;
4809     if (t0 >= t1)
4810         return 0;
4811     if (repeat_rate <= 0.0f)
4812         return (t0 < repeat_delay) && (t1 >= repeat_delay);
4813     const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);
4814     const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);
4815     const int count = count_t1 - count_t0;
4816     return count;
4817 }
4818 
GetKeyPressedAmount(int key_index,float repeat_delay,float repeat_rate)4819 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
4820 {
4821     ImGuiContext& g = *GImGui;
4822     if (key_index < 0)
4823         return 0;
4824     IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4825     const float t = g.IO.KeysDownDuration[key_index];
4826     return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
4827 }
4828 
IsKeyPressed(int user_key_index,bool repeat)4829 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
4830 {
4831     ImGuiContext& g = *GImGui;
4832     if (user_key_index < 0)
4833         return false;
4834     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4835     const float t = g.IO.KeysDownDuration[user_key_index];
4836     if (t == 0.0f)
4837         return true;
4838     if (repeat && t > g.IO.KeyRepeatDelay)
4839         return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
4840     return false;
4841 }
4842 
IsKeyReleased(int user_key_index)4843 bool ImGui::IsKeyReleased(int user_key_index)
4844 {
4845     ImGuiContext& g = *GImGui;
4846     if (user_key_index < 0) return false;
4847     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4848     return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
4849 }
4850 
IsMouseDown(ImGuiMouseButton button)4851 bool ImGui::IsMouseDown(ImGuiMouseButton button)
4852 {
4853     ImGuiContext& g = *GImGui;
4854     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4855     return g.IO.MouseDown[button];
4856 }
4857 
IsMouseClicked(ImGuiMouseButton button,bool repeat)4858 bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)
4859 {
4860     ImGuiContext& g = *GImGui;
4861     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4862     const float t = g.IO.MouseDownDuration[button];
4863     if (t == 0.0f)
4864         return true;
4865 
4866     if (repeat && t > g.IO.KeyRepeatDelay)
4867     {
4868         // 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.
4869         int amount = CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.50f);
4870         if (amount > 0)
4871             return true;
4872     }
4873     return false;
4874 }
4875 
IsMouseReleased(ImGuiMouseButton button)4876 bool ImGui::IsMouseReleased(ImGuiMouseButton button)
4877 {
4878     ImGuiContext& g = *GImGui;
4879     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4880     return g.IO.MouseReleased[button];
4881 }
4882 
IsMouseDoubleClicked(ImGuiMouseButton button)4883 bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
4884 {
4885     ImGuiContext& g = *GImGui;
4886     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4887     return g.IO.MouseDoubleClicked[button];
4888 }
4889 
4890 // Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame.
4891 // [Internal] This doesn't test if the button is pressed
IsMouseDragPastThreshold(ImGuiMouseButton button,float lock_threshold)4892 bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold)
4893 {
4894     ImGuiContext& g = *GImGui;
4895     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4896     if (lock_threshold < 0.0f)
4897         lock_threshold = g.IO.MouseDragThreshold;
4898     return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
4899 }
4900 
IsMouseDragging(ImGuiMouseButton button,float lock_threshold)4901 bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold)
4902 {
4903     ImGuiContext& g = *GImGui;
4904     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4905     if (!g.IO.MouseDown[button])
4906         return false;
4907     return IsMouseDragPastThreshold(button, lock_threshold);
4908 }
4909 
GetMousePos()4910 ImVec2 ImGui::GetMousePos()
4911 {
4912     ImGuiContext& g = *GImGui;
4913     return g.IO.MousePos;
4914 }
4915 
4916 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()4917 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
4918 {
4919     ImGuiContext& g = *GImGui;
4920     if (g.BeginPopupStack.Size > 0)
4921         return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos;
4922     return g.IO.MousePos;
4923 }
4924 
4925 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
IsMousePosValid(const ImVec2 * mouse_pos)4926 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
4927 {
4928     // The assert is only to silence a false-positive in XCode Static Analysis.
4929     // 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).
4930     IM_ASSERT(GImGui != NULL);
4931     const float MOUSE_INVALID = -256000.0f;
4932     ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
4933     return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
4934 }
4935 
IsAnyMouseDown()4936 bool ImGui::IsAnyMouseDown()
4937 {
4938     ImGuiContext& g = *GImGui;
4939     for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
4940         if (g.IO.MouseDown[n])
4941             return true;
4942     return false;
4943 }
4944 
4945 // Return the delta from the initial clicking position while the mouse button is clicked or was just released.
4946 // This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
4947 // 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)4948 ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold)
4949 {
4950     ImGuiContext& g = *GImGui;
4951     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4952     if (lock_threshold < 0.0f)
4953         lock_threshold = g.IO.MouseDragThreshold;
4954     if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])
4955         if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
4956             if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))
4957                 return g.IO.MousePos - g.IO.MouseClickedPos[button];
4958     return ImVec2(0.0f, 0.0f);
4959 }
4960 
ResetMouseDragDelta(ImGuiMouseButton button)4961 void ImGui::ResetMouseDragDelta(ImGuiMouseButton button)
4962 {
4963     ImGuiContext& g = *GImGui;
4964     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4965     // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
4966     g.IO.MouseClickedPos[button] = g.IO.MousePos;
4967 }
4968 
GetMouseCursor()4969 ImGuiMouseCursor ImGui::GetMouseCursor()
4970 {
4971     return GImGui->MouseCursor;
4972 }
4973 
SetMouseCursor(ImGuiMouseCursor cursor_type)4974 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
4975 {
4976     GImGui->MouseCursor = cursor_type;
4977 }
4978 
CaptureKeyboardFromApp(bool capture)4979 void ImGui::CaptureKeyboardFromApp(bool capture)
4980 {
4981     GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
4982 }
4983 
CaptureMouseFromApp(bool capture)4984 void ImGui::CaptureMouseFromApp(bool capture)
4985 {
4986     GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
4987 }
4988 
IsItemActive()4989 bool ImGui::IsItemActive()
4990 {
4991     ImGuiContext& g = *GImGui;
4992     if (g.ActiveId)
4993     {
4994         ImGuiWindow* window = g.CurrentWindow;
4995         return g.ActiveId == window->DC.LastItemId;
4996     }
4997     return false;
4998 }
4999 
IsItemActivated()5000 bool ImGui::IsItemActivated()
5001 {
5002     ImGuiContext& g = *GImGui;
5003     if (g.ActiveId)
5004     {
5005         ImGuiWindow* window = g.CurrentWindow;
5006         if (g.ActiveId == window->DC.LastItemId && g.ActiveIdPreviousFrame != window->DC.LastItemId)
5007             return true;
5008     }
5009     return false;
5010 }
5011 
IsItemDeactivated()5012 bool ImGui::IsItemDeactivated()
5013 {
5014     ImGuiContext& g = *GImGui;
5015     ImGuiWindow* window = g.CurrentWindow;
5016     if (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDeactivated)
5017         return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Deactivated) != 0;
5018     return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId);
5019 }
5020 
IsItemDeactivatedAfterEdit()5021 bool ImGui::IsItemDeactivatedAfterEdit()
5022 {
5023     ImGuiContext& g = *GImGui;
5024     return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore));
5025 }
5026 
5027 // == GetItemID() == GetFocusID()
IsItemFocused()5028 bool ImGui::IsItemFocused()
5029 {
5030     ImGuiContext& g = *GImGui;
5031     ImGuiWindow* window = g.CurrentWindow;
5032 
5033     if (g.NavId != window->DC.LastItemId || g.NavId == 0)
5034         return false;
5035 
5036     // Special handling for the dummy item after Begin() which represent the title bar or tab.
5037     // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
5038     if (window->DC.LastItemId == window->ID && window->WriteAccessed)
5039         return false;
5040 
5041     return true;
5042 }
5043 
IsItemClicked(ImGuiMouseButton mouse_button)5044 bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button)
5045 {
5046     return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
5047 }
5048 
IsItemToggledOpen()5049 bool ImGui::IsItemToggledOpen()
5050 {
5051     ImGuiContext& g = *GImGui;
5052     return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false;
5053 }
5054 
IsItemToggledSelection()5055 bool ImGui::IsItemToggledSelection()
5056 {
5057     ImGuiContext& g = *GImGui;
5058     return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;
5059 }
5060 
IsAnyItemHovered()5061 bool ImGui::IsAnyItemHovered()
5062 {
5063     ImGuiContext& g = *GImGui;
5064     return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
5065 }
5066 
IsAnyItemActive()5067 bool ImGui::IsAnyItemActive()
5068 {
5069     ImGuiContext& g = *GImGui;
5070     return g.ActiveId != 0;
5071 }
5072 
IsAnyItemFocused()5073 bool ImGui::IsAnyItemFocused()
5074 {
5075     ImGuiContext& g = *GImGui;
5076     return g.NavId != 0 && !g.NavDisableHighlight;
5077 }
5078 
IsItemVisible()5079 bool ImGui::IsItemVisible()
5080 {
5081     ImGuiWindow* window = GetCurrentWindowRead();
5082     return window->ClipRect.Overlaps(window->DC.LastItemRect);
5083 }
5084 
IsItemEdited()5085 bool ImGui::IsItemEdited()
5086 {
5087     ImGuiWindow* window = GetCurrentWindowRead();
5088     return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0;
5089 }
5090 
5091 // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
5092 // FIXME: Although this is exposed, its interaction and ideal idiom with using ImGuiButtonFlags_AllowItemOverlap flag are extremely confusing, need rework.
SetItemAllowOverlap()5093 void ImGui::SetItemAllowOverlap()
5094 {
5095     ImGuiContext& g = *GImGui;
5096     ImGuiID id = g.CurrentWindow->DC.LastItemId;
5097     if (g.HoveredId == id)
5098         g.HoveredIdAllowOverlap = true;
5099     if (g.ActiveId == id)
5100         g.ActiveIdAllowOverlap = true;
5101 }
5102 
SetItemUsingMouseWheel()5103 void ImGui::SetItemUsingMouseWheel()
5104 {
5105     ImGuiContext& g = *GImGui;
5106     ImGuiID id = g.CurrentWindow->DC.LastItemId;
5107     if (g.HoveredId == id)
5108         g.HoveredIdUsingMouseWheel = true;
5109     if (g.ActiveId == id)
5110         g.ActiveIdUsingMouseWheel = true;
5111 }
5112 
GetItemRectMin()5113 ImVec2 ImGui::GetItemRectMin()
5114 {
5115     ImGuiWindow* window = GetCurrentWindowRead();
5116     return window->DC.LastItemRect.Min;
5117 }
5118 
GetItemRectMax()5119 ImVec2 ImGui::GetItemRectMax()
5120 {
5121     ImGuiWindow* window = GetCurrentWindowRead();
5122     return window->DC.LastItemRect.Max;
5123 }
5124 
GetItemRectSize()5125 ImVec2 ImGui::GetItemRectSize()
5126 {
5127     ImGuiWindow* window = GetCurrentWindowRead();
5128     return window->DC.LastItemRect.GetSize();
5129 }
5130 
BeginChildEx(const char * name,ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags flags)5131 bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags)
5132 {
5133     ImGuiContext& g = *GImGui;
5134     ImGuiWindow* parent_window = g.CurrentWindow;
5135 
5136     flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoDocking;
5137     flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove);  // Inherit the NoMove flag
5138 
5139     // Size
5140     const ImVec2 content_avail = GetContentRegionAvail();
5141     ImVec2 size = ImFloor(size_arg);
5142     const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
5143     if (size.x <= 0.0f)
5144         size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
5145     if (size.y <= 0.0f)
5146         size.y = ImMax(content_avail.y + size.y, 4.0f);
5147     SetNextWindowSize(size);
5148 
5149     // 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.
5150     if (name)
5151         ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%s/%s_%08X", parent_window->Name, name, id);
5152     else
5153         ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%s/%08X", parent_window->Name, id);
5154 
5155     const float backup_border_size = g.Style.ChildBorderSize;
5156     if (!border)
5157         g.Style.ChildBorderSize = 0.0f;
5158     bool ret = Begin(g.TempBuffer, NULL, flags);
5159     g.Style.ChildBorderSize = backup_border_size;
5160 
5161     ImGuiWindow* child_window = g.CurrentWindow;
5162     child_window->ChildId = id;
5163     child_window->AutoFitChildAxises = (ImS8)auto_fit_axises;
5164 
5165     // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
5166     // While this is not really documented/defined, it seems that the expected thing to do.
5167     if (child_window->BeginCount == 1)
5168         parent_window->DC.CursorPos = child_window->Pos;
5169 
5170     // Process navigation-in immediately so NavInit can run on first frame
5171     if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll))
5172     {
5173         FocusWindow(child_window);
5174         NavInitWindow(child_window, false);
5175         SetActiveID(id + 1, child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item
5176         g.ActiveIdSource = ImGuiInputSource_Nav;
5177     }
5178     return ret;
5179 }
5180 
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)5181 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
5182 {
5183     ImGuiWindow* window = GetCurrentWindow();
5184     return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
5185 }
5186 
BeginChild(ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)5187 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
5188 {
5189     IM_ASSERT(id != 0);
5190     return BeginChildEx(NULL, id, size_arg, border, extra_flags);
5191 }
5192 
EndChild()5193 void ImGui::EndChild()
5194 {
5195     ImGuiContext& g = *GImGui;
5196     ImGuiWindow* window = g.CurrentWindow;
5197 
5198     IM_ASSERT(g.WithinEndChild == false);
5199     IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow);   // Mismatched BeginChild()/EndChild() calls
5200 
5201     g.WithinEndChild = true;
5202     if (window->BeginCount > 1)
5203     {
5204         End();
5205     }
5206     else
5207     {
5208         ImVec2 sz = window->Size;
5209         if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
5210             sz.x = ImMax(4.0f, sz.x);
5211         if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
5212             sz.y = ImMax(4.0f, sz.y);
5213         End();
5214 
5215         ImGuiWindow* parent_window = g.CurrentWindow;
5216         ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
5217         ItemSize(sz);
5218         if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
5219         {
5220             ItemAdd(bb, window->ChildId);
5221             RenderNavHighlight(bb, window->ChildId);
5222 
5223             // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
5224             if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow)
5225                 RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
5226         }
5227         else
5228         {
5229             // Not navigable into
5230             ItemAdd(bb, 0);
5231         }
5232     }
5233     g.WithinEndChild = false;
5234 }
5235 
5236 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)5237 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
5238 {
5239     ImGuiContext& g = *GImGui;
5240     const ImGuiStyle& style = g.Style;
5241     PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
5242     PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
5243     PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
5244     PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
5245     bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
5246     PopStyleVar(3);
5247     PopStyleColor();
5248     return ret;
5249 }
5250 
EndChildFrame()5251 void ImGui::EndChildFrame()
5252 {
5253     EndChild();
5254 }
5255 
SetWindowConditionAllowFlags(ImGuiWindow * window,ImGuiCond flags,bool enabled)5256 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
5257 {
5258     window->SetWindowPosAllowFlags       = enabled ? (window->SetWindowPosAllowFlags       | flags) : (window->SetWindowPosAllowFlags       & ~flags);
5259     window->SetWindowSizeAllowFlags      = enabled ? (window->SetWindowSizeAllowFlags      | flags) : (window->SetWindowSizeAllowFlags      & ~flags);
5260     window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
5261     window->SetWindowDockAllowFlags      = enabled ? (window->SetWindowDockAllowFlags      | flags) : (window->SetWindowDockAllowFlags      & ~flags);
5262 }
5263 
FindWindowByID(ImGuiID id)5264 ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
5265 {
5266     ImGuiContext& g = *GImGui;
5267     return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
5268 }
5269 
FindWindowByName(const char * name)5270 ImGuiWindow* ImGui::FindWindowByName(const char* name)
5271 {
5272     ImGuiID id = ImHashStr(name);
5273     return FindWindowByID(id);
5274 }
5275 
ApplyWindowSettings(ImGuiWindow * window,ImGuiWindowSettings * settings)5276 static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
5277 {
5278     ImGuiViewport* main_viewport = ImGui::GetMainViewport();
5279     window->ViewportPos = main_viewport->Pos;
5280     if (settings->ViewportId)
5281     {
5282         window->ViewportId = settings->ViewportId;
5283         window->ViewportPos = ImVec2(settings->ViewportPos.x, settings->ViewportPos.y);
5284     }
5285     window->Pos = ImFloor(ImVec2(settings->Pos.x + window->ViewportPos.x, settings->Pos.y + window->ViewportPos.y));
5286     if (settings->Size.x > 0 && settings->Size.y > 0)
5287         window->Size = window->SizeFull = ImFloor(ImVec2(settings->Size.x, settings->Size.y));
5288     window->Collapsed = settings->Collapsed;
5289     window->DockId = settings->DockId;
5290     window->DockOrder = settings->DockOrder;
5291 }
5292 
CreateNewWindow(const char * name,ImGuiWindowFlags flags)5293 static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags)
5294 {
5295     ImGuiContext& g = *GImGui;
5296     //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags);
5297 
5298     // Create window the first time
5299     ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
5300     window->Flags = flags;
5301     g.WindowsById.SetVoidPtr(window->ID, window);
5302 
5303     // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
5304     ImGuiViewport* main_viewport = ImGui::GetMainViewport();
5305     window->Pos = main_viewport->Pos + ImVec2(60, 60);
5306     window->ViewportPos = main_viewport->Pos;
5307 
5308     // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
5309     if (!(flags & ImGuiWindowFlags_NoSavedSettings))
5310         if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
5311         {
5312             // Retrieve settings from .ini file
5313             window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
5314             SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
5315             ApplyWindowSettings(window, settings);
5316         }
5317     window->DC.CursorStartPos = window->DC.CursorMaxPos = window->Pos; // So first call to CalcContentSize() doesn't return crazy values
5318 
5319     if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
5320     {
5321         window->AutoFitFramesX = window->AutoFitFramesY = 2;
5322         window->AutoFitOnlyGrows = false;
5323     }
5324     else
5325     {
5326         if (window->Size.x <= 0.0f)
5327             window->AutoFitFramesX = 2;
5328         if (window->Size.y <= 0.0f)
5329             window->AutoFitFramesY = 2;
5330         window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
5331     }
5332 
5333     g.WindowsFocusOrder.push_back(window);
5334     if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
5335         g.Windows.push_front(window); // Quite slow but rare and only once
5336     else
5337         g.Windows.push_back(window);
5338     return window;
5339 }
5340 
GetWindowForTitleDisplay(ImGuiWindow * window)5341 static ImGuiWindow* GetWindowForTitleDisplay(ImGuiWindow* window)
5342 {
5343     return window->DockNodeAsHost ? window->DockNodeAsHost->VisibleWindow : window;
5344 }
5345 
GetWindowForTitleAndMenuHeight(ImGuiWindow * window)5346 static ImGuiWindow* GetWindowForTitleAndMenuHeight(ImGuiWindow* window)
5347 {
5348     return (window->DockNodeAsHost && window->DockNodeAsHost->VisibleWindow) ? window->DockNodeAsHost->VisibleWindow : window;
5349 }
5350 
CalcWindowSizeAfterConstraint(ImGuiWindow * window,ImVec2 new_size)5351 static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size)
5352 {
5353     ImGuiContext& g = *GImGui;
5354     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)
5355     {
5356         // Using -1,-1 on either X/Y axis to preserve the current size.
5357         ImRect cr = g.NextWindowData.SizeConstraintRect;
5358         new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
5359         new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
5360         if (g.NextWindowData.SizeCallback)
5361         {
5362             ImGuiSizeCallbackData data;
5363             data.UserData = g.NextWindowData.SizeCallbackUserData;
5364             data.Pos = window->Pos;
5365             data.CurrentSize = window->SizeFull;
5366             data.DesiredSize = new_size;
5367             g.NextWindowData.SizeCallback(&data);
5368             new_size = data.DesiredSize;
5369         }
5370         new_size.x = IM_FLOOR(new_size.x);
5371         new_size.y = IM_FLOOR(new_size.y);
5372     }
5373 
5374     // Minimum size
5375     if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
5376     {
5377         ImGuiWindow* window_for_height = GetWindowForTitleAndMenuHeight(window);
5378         new_size = ImMax(new_size, g.Style.WindowMinSize);
5379         new_size.y = ImMax(new_size.y, window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows
5380     }
5381     return new_size;
5382 }
5383 
CalcWindowContentSizes(ImGuiWindow * window,ImVec2 * content_size_current,ImVec2 * content_size_ideal)5384 static void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_current, ImVec2* content_size_ideal)
5385 {
5386     bool preserve_old_content_sizes = false;
5387     if (window->Collapsed && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5388         preserve_old_content_sizes = true;
5389     else if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0)
5390         preserve_old_content_sizes = true;
5391     if (preserve_old_content_sizes)
5392     {
5393         *content_size_current = window->ContentSize;
5394         *content_size_ideal = window->ContentSizeIdeal;
5395         return;
5396     }
5397 
5398     content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_FLOOR(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);
5399     content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_FLOOR(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);
5400     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);
5401     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);
5402 }
5403 
CalcWindowAutoFitSize(ImGuiWindow * window,const ImVec2 & size_contents)5404 static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents)
5405 {
5406     ImGuiContext& g = *GImGui;
5407     ImGuiStyle& style = g.Style;
5408     ImVec2 size_decorations = ImVec2(0.0f, window->TitleBarHeight() + window->MenuBarHeight());
5409     ImVec2 size_pad = window->WindowPadding * 2.0f;
5410     ImVec2 size_desired = size_contents + size_pad + size_decorations;
5411     if (window->Flags & ImGuiWindowFlags_Tooltip)
5412     {
5413         // Tooltip always resize
5414         return size_desired;
5415     }
5416     else
5417     {
5418         // Maximum window size is determined by the viewport size or monitor size
5419         const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0;
5420         const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0;
5421         ImVec2 size_min = style.WindowMinSize;
5422         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)
5423             size_min = ImMin(size_min, ImVec2(4.0f, 4.0f));
5424 
5425         // FIXME-VIEWPORT-WORKAREA: May want to use GetWorkSize() instead of Size depending on the type of windows?
5426         ImVec2 avail_size = window->Viewport->Size;
5427         if (window->ViewportOwned)
5428             avail_size = ImVec2(FLT_MAX, FLT_MAX);
5429         const int monitor_idx = window->ViewportAllowPlatformMonitorExtend;
5430         if (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size)
5431             avail_size = g.PlatformIO.Monitors[monitor_idx].WorkSize;
5432         ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, avail_size - g.Style.DisplaySafeAreaPadding * 2.0f));
5433 
5434         // When the window cannot fit all contents (either because of constraints, either because screen is too small),
5435         // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
5436         ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5437         bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - size_decorations.x < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar);
5438         bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - size_decorations.y < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar);
5439         if (will_have_scrollbar_x)
5440             size_auto_fit.y += style.ScrollbarSize;
5441         if (will_have_scrollbar_y)
5442             size_auto_fit.x += style.ScrollbarSize;
5443         return size_auto_fit;
5444     }
5445 }
5446 
CalcWindowNextAutoFitSize(ImGuiWindow * window)5447 ImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window)
5448 {
5449     ImVec2 size_contents_current;
5450     ImVec2 size_contents_ideal;
5451     CalcWindowContentSizes(window, &size_contents_current, &size_contents_ideal);
5452     ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents_ideal);
5453     ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5454     return size_final;
5455 }
5456 
GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)5457 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
5458 {
5459     if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
5460         return ImGuiCol_PopupBg;
5461     if (flags & ImGuiWindowFlags_ChildWindow)
5462         return ImGuiCol_ChildBg;
5463     return ImGuiCol_WindowBg;
5464 }
5465 
CalcResizePosSizeFromAnyCorner(ImGuiWindow * window,const ImVec2 & corner_target,const ImVec2 & corner_norm,ImVec2 * out_pos,ImVec2 * out_size)5466 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
5467 {
5468     ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm);                // Expected window upper-left
5469     ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
5470     ImVec2 size_expected = pos_max - pos_min;
5471     ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected);
5472     *out_pos = pos_min;
5473     if (corner_norm.x == 0.0f)
5474         out_pos->x -= (size_constrained.x - size_expected.x);
5475     if (corner_norm.y == 0.0f)
5476         out_pos->y -= (size_constrained.y - size_expected.y);
5477     *out_size = size_constrained;
5478 }
5479 
5480 struct ImGuiResizeGripDef
5481 {
5482     ImVec2  CornerPosN;
5483     ImVec2  InnerDir;
5484     int     AngleMin12, AngleMax12;
5485 };
5486 
5487 static const ImGuiResizeGripDef resize_grip_def[4] =
5488 {
5489     { ImVec2(1, 1), ImVec2(-1, -1), 0, 3 }, // Lower-right
5490     { ImVec2(0, 1), ImVec2(+1, -1), 3, 6 }, // Lower-left
5491     { ImVec2(0, 0), ImVec2(+1, +1), 6, 9 }, // Upper-left (Unused)
5492     { ImVec2(1, 0), ImVec2(-1, +1), 9, 12 }, // Upper-right (Unused)
5493 };
5494 
5495 struct ImGuiResizeBorderDef
5496 {
5497     ImVec2 InnerDir;
5498     ImVec2 CornerPosN1, CornerPosN2;
5499     float  OuterAngle;
5500 };
5501 
5502 static const ImGuiResizeBorderDef resize_border_def[4] =
5503 {
5504     { ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Top
5505     { ImVec2(-1, 0), ImVec2(1, 0), ImVec2(1, 1), IM_PI * 0.00f }, // Right
5506     { ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f }, // Bottom
5507     { ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f } // Left
5508 };
5509 
GetResizeBorderRect(ImGuiWindow * window,int border_n,float perp_padding,float thickness)5510 static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
5511 {
5512     ImRect rect = window->Rect();
5513     if (thickness == 0.0f) rect.Max -= ImVec2(1, 1);
5514     if (border_n == 0) { return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness,    rect.Max.x - perp_padding, rect.Min.y + thickness);    } // Top
5515     if (border_n == 1) { return ImRect(rect.Max.x - thickness,    rect.Min.y + perp_padding, rect.Max.x + thickness,    rect.Max.y - perp_padding); } // Right
5516     if (border_n == 2) { return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness,    rect.Max.x - perp_padding, rect.Max.y + thickness);    } // Bottom
5517     if (border_n == 3) { return ImRect(rect.Min.x - thickness,    rect.Min.y + perp_padding, rect.Min.x + thickness,    rect.Max.y - perp_padding); } // Left
5518     IM_ASSERT(0);
5519     return ImRect();
5520 }
5521 
5522 // 0..3: corners (Lower-right, Lower-left, Unused, Unused)
5523 // 4..7: borders (Top, Right, Bottom, Left)
GetWindowResizeID(ImGuiWindow * window,int n)5524 ImGuiID ImGui::GetWindowResizeID(ImGuiWindow* window, int n)
5525 {
5526     IM_ASSERT(n >= 0 && n <= 7);
5527     ImGuiID id = window->DockIsActive ? window->DockNode->HostWindow->ID : window->ID;
5528     id = ImHashStr("#RESIZE", 0, id);
5529     id = ImHashData(&n, sizeof(int), id);
5530     return id;
5531 }
5532 
5533 // Handle resize for: Resize Grips, Borders, Gamepad
5534 // 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)5535 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)
5536 {
5537     ImGuiContext& g = *GImGui;
5538     ImGuiWindowFlags flags = window->Flags;
5539 
5540     if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
5541         return false;
5542     if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window.
5543         return false;
5544 
5545     bool ret_auto_fit = false;
5546     const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0;
5547     const float grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
5548     const float grip_hover_inner_size = IM_FLOOR(grip_draw_size * 0.75f);
5549     const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS : 0.0f;
5550 
5551     ImVec2 pos_target(FLT_MAX, FLT_MAX);
5552     ImVec2 size_target(FLT_MAX, FLT_MAX);
5553 
5554     // Clip mouse interaction rectangles within the viewport rectangle (in practice the narrowing is going to happen most of the time).
5555     // - Not narrowing would mostly benefit the situation where OS windows _without_ decoration have a threshold for hovering when outside their limits.
5556     //   This is however not the case with current backends under Win32, but a custom borderless window implementation would benefit from it.
5557     // - When decoration are enabled we typically benefit from that distance, but then our resize elements would be conflicting with OS resize elements, so we also narrow.
5558     // - Note that we are unable to tell if the platform setup allows hovering with a distance threshold (on Win32, decorated window have such threshold).
5559     // We only clip interaction so we overwrite window->ClipRect, cannot call PushClipRect() yet as DrawList is not yet setup.
5560     const bool clip_with_viewport_rect = !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) || (g.IO.MouseHoveredViewport != window->ViewportId) || !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration);
5561     if (clip_with_viewport_rect)
5562         window->ClipRect = window->Viewport->GetMainRect();
5563 
5564     // Resize grips and borders are on layer 1
5565     window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5566 
5567     // Manual resize grips
5568     PushID("#RESIZE");
5569     for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5570     {
5571         const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5572         const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
5573 
5574         // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
5575         ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size);
5576         if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
5577         if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
5578         bool hovered, held;
5579         ButtonBehavior(resize_rect, window->GetID(resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
5580         //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
5581         if (hovered || held)
5582             g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
5583 
5584         if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
5585         {
5586             // Manual auto-fit when double-clicking
5587             size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5588             ret_auto_fit = true;
5589             ClearActiveID();
5590         }
5591         else if (held)
5592         {
5593             // Resize from any of the four corners
5594             // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
5595             ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(grip.InnerDir * grip_hover_outer_size, grip.InnerDir * -grip_hover_inner_size, grip.CornerPosN); // Corner of the window corresponding to our corner grip
5596             ImVec2 clamp_min = ImVec2(grip.CornerPosN.x == 1.0f ? visibility_rect.Min.x : -FLT_MAX, grip.CornerPosN.y == 1.0f ? visibility_rect.Min.y : -FLT_MAX);
5597             ImVec2 clamp_max = ImVec2(grip.CornerPosN.x == 0.0f ? visibility_rect.Max.x : +FLT_MAX, grip.CornerPosN.y == 0.0f ? visibility_rect.Max.y : +FLT_MAX);
5598             corner_target = ImClamp(corner_target, clamp_min, clamp_max);
5599             CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPosN, &pos_target, &size_target);
5600         }
5601         if (resize_grip_n == 0 || held || hovered)
5602             resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
5603     }
5604     for (int border_n = 0; border_n < resize_border_count; border_n++)
5605     {
5606         bool hovered, held;
5607         ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS);
5608         ButtonBehavior(border_rect, window->GetID(border_n + 4), &hovered, &held, ImGuiButtonFlags_FlattenChildren);
5609         //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
5610         if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held)
5611         {
5612             g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
5613             if (held)
5614                 *border_held = border_n;
5615         }
5616         if (held)
5617         {
5618             ImVec2 border_target = window->Pos;
5619             ImVec2 border_posn;
5620             if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Top
5621             if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Right
5622             if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Bottom
5623             if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Left
5624             ImVec2 clamp_min = ImVec2(border_n == 1 ? visibility_rect.Min.x : -FLT_MAX, border_n == 2 ? visibility_rect.Min.y : -FLT_MAX);
5625             ImVec2 clamp_max = ImVec2(border_n == 3 ? visibility_rect.Max.x : +FLT_MAX, border_n == 0 ? visibility_rect.Max.y : +FLT_MAX);
5626             border_target = ImClamp(border_target, clamp_min, clamp_max);
5627             CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target);
5628         }
5629     }
5630     PopID();
5631 
5632     // Restore nav layer
5633     window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5634 
5635     // Navigation resize (keyboard/gamepad)
5636     if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
5637     {
5638         ImVec2 nav_resize_delta;
5639         if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift)
5640             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
5641         if (g.NavInputSource == ImGuiInputSource_NavGamepad)
5642             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down);
5643         if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f)
5644         {
5645             const float NAV_RESIZE_SPEED = 600.0f;
5646             nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
5647             nav_resize_delta = ImMax(nav_resize_delta, visibility_rect.Min - window->Pos - window->Size);
5648             g.NavWindowingToggleLayer = false;
5649             g.NavDisableMouseHover = true;
5650             resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
5651             // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
5652             size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + nav_resize_delta);
5653         }
5654     }
5655 
5656     // Apply back modified position/size to window
5657     if (size_target.x != FLT_MAX)
5658     {
5659         window->SizeFull = size_target;
5660         MarkIniSettingsDirty(window);
5661     }
5662     if (pos_target.x != FLT_MAX)
5663     {
5664         window->Pos = ImFloor(pos_target);
5665         MarkIniSettingsDirty(window);
5666     }
5667 
5668     window->Size = window->SizeFull;
5669     return ret_auto_fit;
5670 }
5671 
ClampWindowRect(ImGuiWindow * window,const ImRect & visibility_rect)5672 static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& visibility_rect)
5673 {
5674     ImGuiContext& g = *GImGui;
5675     ImVec2 size_for_clamping = window->Size;
5676     if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
5677         size_for_clamping.y = window->TitleBarHeight();
5678     window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max);
5679 }
5680 
RenderWindowOuterBorders(ImGuiWindow * window)5681 static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
5682 {
5683     ImGuiContext& g = *GImGui;
5684     float rounding = window->WindowRounding;
5685     float border_size = window->WindowBorderSize;
5686     if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground))
5687         window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
5688 
5689     int border_held = window->ResizeBorderHeld;
5690     if (border_held != -1)
5691     {
5692         const ImGuiResizeBorderDef& def = resize_border_def[border_held];
5693         ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f);
5694         window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI * 0.25f, def.OuterAngle);
5695         window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f);
5696         window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), false, ImMax(2.0f, border_size)); // Thicker than usual
5697     }
5698     if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
5699     {
5700         float y = window->Pos.y + window->TitleBarHeight() - 1;
5701         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);
5702     }
5703 }
5704 
5705 // Draw background and borders
5706 // Draw and handle scrollbars
RenderWindowDecorations(ImGuiWindow * window,const ImRect & title_bar_rect,bool title_bar_is_highlight,bool handle_borders_and_resize_grips,int resize_grip_count,const ImU32 resize_grip_col[4],float resize_grip_draw_size)5707 void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size)
5708 {
5709     ImGuiContext& g = *GImGui;
5710     ImGuiStyle& style = g.Style;
5711     ImGuiWindowFlags flags = window->Flags;
5712 
5713     // Ensure that ScrollBar doesn't read last frame's SkipItems
5714     IM_ASSERT(window->BeginCount == 0);
5715     window->SkipItems = false;
5716 
5717     // Draw window + handle manual resize
5718     // 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.
5719     const float window_rounding = window->WindowRounding;
5720     const float window_border_size = window->WindowBorderSize;
5721     if (window->Collapsed)
5722     {
5723         // Title bar only
5724         float backup_border_size = style.FrameBorderSize;
5725         g.Style.FrameBorderSize = window->WindowBorderSize;
5726         ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
5727         RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
5728         g.Style.FrameBorderSize = backup_border_size;
5729     }
5730     else
5731     {
5732         // Window background
5733         if (!(flags & ImGuiWindowFlags_NoBackground))
5734         {
5735             bool is_docking_transparent_payload = false;
5736             if (g.DragDropActive && (g.FrameCount - g.DragDropAcceptFrameCount) <= 1 && g.IO.ConfigDockingTransparentPayload)
5737                 if (g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) && *(ImGuiWindow**)g.DragDropPayload.Data == window)
5738                     is_docking_transparent_payload = true;
5739 
5740             ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
5741             if (window->ViewportOwned)
5742             {
5743                 // No alpha
5744                 bg_col = (bg_col | IM_COL32_A_MASK);
5745                 if (is_docking_transparent_payload)
5746                     window->Viewport->Alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA;
5747             }
5748             else
5749             {
5750                 // Adjust alpha. For docking
5751                 bool override_alpha = false;
5752                 float alpha = 1.0f;
5753                 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha)
5754                 {
5755                     alpha = g.NextWindowData.BgAlphaVal;
5756                     override_alpha = true;
5757                 }
5758                 if (is_docking_transparent_payload)
5759                 {
5760                     alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA; // FIXME-DOCK: Should that be an override?
5761                     override_alpha = true;
5762                 }
5763                 if (override_alpha)
5764                     bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
5765             }
5766             window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot);
5767         }
5768 
5769         // Title bar
5770         // (when docked, DockNode are drawing their own title bar. Individual windows however do NOT set the _NoTitleBar flag,
5771         // in order for their pos/size to be matching their undocking state.)
5772         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
5773         {
5774             ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
5775             window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top);
5776         }
5777 
5778         // Menu bar
5779         if (flags & ImGuiWindowFlags_MenuBar)
5780         {
5781             ImRect menu_bar_rect = window->MenuBarRect();
5782             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.
5783             window->DrawList->AddRectFilled(menu_bar_rect.Min + ImVec2(window_border_size, 0), menu_bar_rect.Max - ImVec2(window_border_size, 0), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top);
5784             if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
5785                 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
5786         }
5787 
5788         // Docking: Unhide tab bar (small triangle in the corner), drag from small triangle to quickly undock
5789         ImGuiDockNode* node = window->DockNode;
5790         if (window->DockIsActive && node->IsHiddenTabBar() && !node->IsNoTabBar())
5791         {
5792             float unhide_sz_draw = ImFloor(g.FontSize * 0.70f);
5793             float unhide_sz_hit = ImFloor(g.FontSize * 0.55f);
5794             ImVec2 p = node->Pos;
5795             ImRect r(p, p + ImVec2(unhide_sz_hit, unhide_sz_hit));
5796             bool hovered, held;
5797             if (ButtonBehavior(r, window->GetID("#UNHIDE"), &hovered, &held, ImGuiButtonFlags_FlattenChildren))
5798                 node->WantHiddenTabBarToggle = true;
5799             else if (held && IsMouseDragging(0))
5800                 StartMouseMovingWindowOrNode(window, node, true);
5801 
5802             // FIXME-DOCK: Ideally we'd use ImGuiCol_TitleBgActive/ImGuiCol_TitleBg here, but neither is guaranteed to be visible enough at this sort of size..
5803             ImU32 col = GetColorU32(((held && hovered) || (node->IsFocused && !hovered)) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
5804             window->DrawList->AddTriangleFilled(p, p + ImVec2(unhide_sz_draw, 0.0f), p + ImVec2(0.0f, unhide_sz_draw), col);
5805         }
5806 
5807         // Scrollbars
5808         if (window->ScrollbarX)
5809             Scrollbar(ImGuiAxis_X);
5810         if (window->ScrollbarY)
5811             Scrollbar(ImGuiAxis_Y);
5812 
5813         // Render resize grips (after their input handling so we don't have a frame of latency)
5814         if (handle_borders_and_resize_grips && !(flags & ImGuiWindowFlags_NoResize))
5815         {
5816             for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5817             {
5818                 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5819                 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
5820                 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)));
5821                 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)));
5822                 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);
5823                 window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]);
5824             }
5825         }
5826 
5827         // Borders (for dock node host they will be rendered over after the tab bar)
5828         if (handle_borders_and_resize_grips && !window->DockNodeAsHost)
5829             RenderWindowOuterBorders(window);
5830     }
5831 }
5832 
5833 // Render title text, collapse button, close button
5834 // When inside a dock node, this is handled in DockNodeUpdateTabBar() instead.
RenderWindowTitleBarContents(ImGuiWindow * window,const ImRect & title_bar_rect,const char * name,bool * p_open)5835 void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)
5836 {
5837     ImGuiContext& g = *GImGui;
5838     ImGuiStyle& style = g.Style;
5839     ImGuiWindowFlags flags = window->Flags;
5840 
5841     const bool has_close_button = (p_open != NULL);
5842     const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None);
5843 
5844     // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)
5845     const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
5846     window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
5847     window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5848 
5849     // Layout buttons
5850     // FIXME: Would be nice to generalize the subtleties expressed here into reusable code.
5851     float pad_l = style.FramePadding.x;
5852     float pad_r = style.FramePadding.x;
5853     float button_sz = g.FontSize;
5854     ImVec2 close_button_pos;
5855     ImVec2 collapse_button_pos;
5856     if (has_close_button)
5857     {
5858         pad_r += button_sz;
5859         close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5860     }
5861     if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)
5862     {
5863         pad_r += button_sz;
5864         collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5865     }
5866     if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)
5867     {
5868         collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l - style.FramePadding.x, title_bar_rect.Min.y);
5869         pad_l += button_sz;
5870     }
5871 
5872     // Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
5873     if (has_collapse_button)
5874         if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos, NULL))
5875             window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function
5876 
5877     // Close button
5878     if (has_close_button)
5879         if (CloseButton(window->GetID("#CLOSE"), close_button_pos))
5880             *p_open = false;
5881 
5882     window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5883     window->DC.ItemFlags = item_flags_backup;
5884 
5885     // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
5886     // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
5887     const char* UNSAVED_DOCUMENT_MARKER = "*";
5888     const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f;
5889     const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
5890 
5891     // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
5892     // while uncentered title text will still reach edges correct.
5893     if (pad_l > style.FramePadding.x)
5894         pad_l += g.Style.ItemInnerSpacing.x;
5895     if (pad_r > style.FramePadding.x)
5896         pad_r += g.Style.ItemInnerSpacing.x;
5897     if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f)
5898     {
5899         float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center
5900         float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x);
5901         pad_l = ImMax(pad_l, pad_extend * centerness);
5902         pad_r = ImMax(pad_r, pad_extend * centerness);
5903     }
5904 
5905     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);
5906     ImRect clip_r(layout_r.Min.x, layout_r.Min.y, layout_r.Max.x + g.Style.ItemInnerSpacing.x, layout_r.Max.y);
5907     //if (g.IO.KeyCtrl) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
5908     RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r);
5909     if (flags & ImGuiWindowFlags_UnsavedDocument)
5910     {
5911         ImVec2 marker_pos = ImVec2(ImMax(layout_r.Min.x, layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x) + text_size.x, layout_r.Min.y) + ImVec2(2 - marker_size_x, 0.0f);
5912         ImVec2 off = ImVec2(0.0f, IM_FLOOR(-g.FontSize * 0.25f));
5913         RenderTextClipped(marker_pos + off, layout_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_r);
5914     }
5915 }
5916 
UpdateWindowParentAndRootLinks(ImGuiWindow * window,ImGuiWindowFlags flags,ImGuiWindow * parent_window)5917 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
5918 {
5919     window->ParentWindow = parent_window;
5920     window->RootWindow = window->RootWindowDockStop = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
5921     if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
5922     {
5923         window->RootWindow = parent_window->RootWindow;
5924         if (!window->DockIsActive && !(parent_window->Flags & ImGuiWindowFlags_DockNodeHost))
5925             window->RootWindowDockStop = parent_window->RootWindowDockStop;
5926     }
5927     if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
5928         window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
5929     while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
5930     {
5931         IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);
5932         window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
5933     }
5934 }
5935 
5936 // Push a new Dear ImGui window to add widgets to.
5937 // - 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.
5938 // - Begin/End can be called multiple times during the frame with the same window name to append content.
5939 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
5940 //   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.
5941 // - 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.
5942 // - 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)5943 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
5944 {
5945     ImGuiContext& g = *GImGui;
5946     const ImGuiStyle& style = g.Style;
5947     IM_ASSERT(name != NULL && name[0] != '\0');     // Window name required
5948     IM_ASSERT(g.WithinFrameScope);                  // Forgot to call ImGui::NewFrame()
5949     IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
5950 
5951     // Find or create
5952     ImGuiWindow* window = FindWindowByName(name);
5953     const bool window_just_created = (window == NULL);
5954     if (window_just_created)
5955         window = CreateNewWindow(name, flags);
5956 
5957     // Automatically disable manual moving/resizing when NoInputs is set
5958     if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
5959         flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
5960 
5961     if (flags & ImGuiWindowFlags_NavFlattened)
5962         IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
5963 
5964     const int current_frame = g.FrameCount;
5965     const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
5966     window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow);
5967 
5968     // Update the Appearing flag
5969     bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1);   // Not using !WasActive because the implicit "Debug" window would always toggle off->on
5970     const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);
5971     if (flags & ImGuiWindowFlags_Popup)
5972     {
5973         ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5974         window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
5975         window_just_activated_by_user |= (window != popup_ref.Window);
5976     }
5977     window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
5978     if (window->Appearing)
5979         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
5980 
5981     // Update Flags, LastFrameActive, BeginOrderXXX fields
5982     if (first_begin_of_the_frame)
5983     {
5984         window->FlagsPreviousFrame = window->Flags;
5985         window->Flags = (ImGuiWindowFlags)flags;
5986         window->LastFrameActive = current_frame;
5987         window->LastTimeActive = (float)g.Time;
5988         window->BeginOrderWithinParent = 0;
5989         window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
5990     }
5991     else
5992     {
5993         flags = window->Flags;
5994     }
5995 
5996     // Docking
5997     // (NB: during the frame dock nodes are created, it is possible that (window->DockIsActive == false) even though (window->DockNode->Windows.Size > 1)
5998     IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL); // Cannot be both
5999     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasDock)
6000         SetWindowDock(window, g.NextWindowData.DockId, g.NextWindowData.DockCond);
6001     if (first_begin_of_the_frame)
6002     {
6003         bool has_dock_node = (window->DockId != 0 || window->DockNode != NULL);
6004         bool new_auto_dock_node = !has_dock_node && GetWindowAlwaysWantOwnTabBar(window);
6005         if (has_dock_node || new_auto_dock_node)
6006         {
6007             BeginDocked(window, p_open);
6008             flags = window->Flags;
6009             if (window->DockIsActive)
6010                 IM_ASSERT(window->DockNode != NULL);
6011 
6012             // Docking currently override constraints
6013             g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint;
6014         }
6015     }
6016 
6017     // 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
6018     ImGuiWindow* parent_window_in_stack = window->DockIsActive ? window->DockNode->HostWindow : g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
6019     ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
6020     IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
6021 
6022     // We allow window memory to be compacted so recreate the base stack when needed.
6023     if (window->IDStack.Size == 0)
6024         window->IDStack.push_back(window->ID);
6025 
6026     // Add to stack
6027     // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
6028     g.CurrentWindowStack.push_back(window);
6029     g.CurrentWindow = window;
6030     window->DC.StackSizesOnBegin.SetToCurrentState();
6031     g.CurrentWindow = NULL;
6032 
6033     if (flags & ImGuiWindowFlags_Popup)
6034     {
6035         ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
6036         popup_ref.Window = window;
6037         g.BeginPopupStack.push_back(popup_ref);
6038         window->PopupId = popup_ref.PopupId;
6039     }
6040 
6041     if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow))
6042         window->NavLastIds[0] = 0;
6043 
6044     // Update ->RootWindow and others pointers (before any possible call to FocusWindow)
6045     if (first_begin_of_the_frame)
6046         UpdateWindowParentAndRootLinks(window, flags, parent_window);
6047 
6048     // Process SetNextWindow***() calls
6049     // (FIXME: Consider splitting the HasXXX flags into X/Y components
6050     bool window_pos_set_by_api = false;
6051     bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
6052     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos)
6053     {
6054         window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
6055         if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
6056         {
6057             // May be processed on the next frame if this is our first frame and we are measuring size
6058             // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
6059             window->SetWindowPosVal = g.NextWindowData.PosVal;
6060             window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
6061             window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6062         }
6063         else
6064         {
6065             SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
6066         }
6067     }
6068     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)
6069     {
6070         window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
6071         window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
6072         SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
6073     }
6074     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasScroll)
6075     {
6076         if (g.NextWindowData.ScrollVal.x >= 0.0f)
6077         {
6078             window->ScrollTarget.x = g.NextWindowData.ScrollVal.x;
6079             window->ScrollTargetCenterRatio.x = 0.0f;
6080         }
6081         if (g.NextWindowData.ScrollVal.y >= 0.0f)
6082         {
6083             window->ScrollTarget.y = g.NextWindowData.ScrollVal.y;
6084             window->ScrollTargetCenterRatio.y = 0.0f;
6085         }
6086     }
6087     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize)
6088         window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;
6089     else if (first_begin_of_the_frame)
6090         window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);
6091     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasWindowClass)
6092         window->WindowClass = g.NextWindowData.WindowClass;
6093     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed)
6094         SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
6095     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus)
6096         FocusWindow(window);
6097     if (window->Appearing)
6098         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
6099 
6100     // When reusing window again multiple times a frame, just append content (don't need to setup again)
6101     if (first_begin_of_the_frame)
6102     {
6103         // Initialize
6104         const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
6105         window->Active = true;
6106         window->HasCloseButton = (p_open != NULL);
6107         window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX);
6108         window->IDStack.resize(1);
6109         window->DrawList->_ResetForNewFrame();
6110         window->DC.CurrentTableIdx = -1;
6111 
6112         // Restore buffer capacity when woken from a compacted state, to avoid
6113         if (window->MemoryCompacted)
6114             GcAwakeTransientWindowBuffers(window);
6115 
6116         // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
6117         // 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.
6118         bool window_title_visible_elsewhere = false;
6119         if ((window->Viewport && window->Viewport->Window == window) || (window->DockIsActive))
6120             window_title_visible_elsewhere = true;
6121         else if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0)   // Window titles visible when using CTRL+TAB
6122             window_title_visible_elsewhere = true;
6123         if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
6124         {
6125             size_t buf_len = (size_t)window->NameBufLen;
6126             window->Name = ImStrdupcpy(window->Name, &buf_len, name);
6127             window->NameBufLen = (int)buf_len;
6128         }
6129 
6130         // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
6131 
6132         // Update contents size from last frame for auto-fitting (or use explicit size)
6133         CalcWindowContentSizes(window, &window->ContentSize, &window->ContentSizeIdeal);
6134         if (window->HiddenFramesCanSkipItems > 0)
6135             window->HiddenFramesCanSkipItems--;
6136         if (window->HiddenFramesCannotSkipItems > 0)
6137             window->HiddenFramesCannotSkipItems--;
6138         if (window->HiddenFramesForRenderOnly > 0)
6139             window->HiddenFramesForRenderOnly--;
6140 
6141         // Hide new windows for one frame until they calculate their size
6142         if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
6143             window->HiddenFramesCannotSkipItems = 1;
6144 
6145         // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
6146         // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
6147         if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
6148         {
6149             window->HiddenFramesCannotSkipItems = 1;
6150             if (flags & ImGuiWindowFlags_AlwaysAutoResize)
6151             {
6152                 if (!window_size_x_set_by_api)
6153                     window->Size.x = window->SizeFull.x = 0.f;
6154                 if (!window_size_y_set_by_api)
6155                     window->Size.y = window->SizeFull.y = 0.f;
6156                 window->ContentSize = window->ContentSizeIdeal = ImVec2(0.f, 0.f);
6157             }
6158         }
6159 
6160         // SELECT VIEWPORT
6161         // We need to do this before using any style/font sizes, as viewport with a different DPI may affect font sizes.
6162 
6163         UpdateSelectWindowViewport(window);
6164         SetCurrentViewport(window, window->Viewport);
6165         window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f;
6166         SetCurrentWindow(window);
6167         flags = window->Flags;
6168 
6169         // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)
6170         // We read Style data after the call to UpdateSelectWindowViewport() which might be swapping the style.
6171 
6172         if (flags & ImGuiWindowFlags_ChildWindow)
6173             window->WindowBorderSize = style.ChildBorderSize;
6174         else
6175             window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
6176         if (!window->DockIsActive && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
6177             window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
6178         else
6179             window->WindowPadding = style.WindowPadding;
6180 
6181         // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size.
6182         window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
6183         window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
6184 
6185         // Collapse window by double-clicking on title bar
6186         // 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
6187         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse) && !window->DockIsActive)
6188         {
6189             // 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.
6190             ImRect title_bar_rect = window->TitleBarRect();
6191             if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
6192                 window->WantCollapseToggle = true;
6193             if (window->WantCollapseToggle)
6194             {
6195                 window->Collapsed = !window->Collapsed;
6196                 MarkIniSettingsDirty(window);
6197                 FocusWindow(window);
6198             }
6199         }
6200         else
6201         {
6202             window->Collapsed = false;
6203         }
6204         window->WantCollapseToggle = false;
6205 
6206         // SIZE
6207 
6208         // Calculate auto-fit size, handle automatic resize
6209         const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal);
6210         bool use_current_size_for_scrollbar_x = window_just_created;
6211         bool use_current_size_for_scrollbar_y = window_just_created;
6212         if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
6213         {
6214             // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
6215             if (!window_size_x_set_by_api)
6216             {
6217                 window->SizeFull.x = size_auto_fit.x;
6218                 use_current_size_for_scrollbar_x = true;
6219             }
6220             if (!window_size_y_set_by_api)
6221             {
6222                 window->SizeFull.y = size_auto_fit.y;
6223                 use_current_size_for_scrollbar_y = true;
6224             }
6225         }
6226         else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
6227         {
6228             // Auto-fit may only grow window during the first few frames
6229             // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
6230             if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
6231             {
6232                 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
6233                 use_current_size_for_scrollbar_x = true;
6234             }
6235             if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
6236             {
6237                 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
6238                 use_current_size_for_scrollbar_y = true;
6239             }
6240             if (!window->Collapsed)
6241                 MarkIniSettingsDirty(window);
6242         }
6243 
6244         // Apply minimum/maximum window size constraints and final size
6245         window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull);
6246         window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
6247 
6248         // Decoration size
6249         const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
6250 
6251         // POSITION
6252 
6253         // Popup latch its initial position, will position itself when it appears next frame
6254         if (window_just_activated_by_user)
6255         {
6256             window->AutoPosLastDirection = ImGuiDir_None;
6257             if ((flags & ImGuiWindowFlags_Popup) != 0 && !(flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api) // FIXME: BeginPopup() could use SetNextWindowPos()
6258                 window->Pos = g.BeginPopupStack.back().OpenPopupPos;
6259         }
6260 
6261         // Position child window
6262         if (flags & ImGuiWindowFlags_ChildWindow)
6263         {
6264             IM_ASSERT(parent_window && parent_window->Active);
6265             window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
6266             parent_window->DC.ChildWindows.push_back(window);
6267             if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
6268                 window->Pos = parent_window->DC.CursorPos;
6269         }
6270 
6271         const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0);
6272         if (window_pos_with_pivot)
6273             SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering)
6274         else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
6275             window->Pos = FindBestWindowPosForPopup(window);
6276         else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
6277             window->Pos = FindBestWindowPosForPopup(window);
6278         else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
6279             window->Pos = FindBestWindowPosForPopup(window);
6280 
6281         // Late create viewport if we don't fit within our current host viewport.
6282         if (window->ViewportAllowPlatformMonitorExtend >= 0 && !window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_Minimized))
6283             if (!window->Viewport->GetMainRect().Contains(window->Rect()))
6284             {
6285                 // This is based on the assumption that the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport)
6286                 //ImGuiViewport* old_viewport = window->Viewport;
6287                 window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing);
6288 
6289                 // FIXME-DPI
6290                 //IM_ASSERT(old_viewport->DpiScale == window->Viewport->DpiScale); // FIXME-DPI: Something went wrong
6291                 SetCurrentViewport(window, window->Viewport);
6292                 window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f;
6293                 SetCurrentWindow(window);
6294             }
6295 
6296         bool viewport_rect_changed = false;
6297         if (window->ViewportOwned)
6298         {
6299             // Synchronize window --> viewport in most situations
6300             // Synchronize viewport -> window in case the platform window has been moved or resized from the OS/WM
6301             if (window->Viewport->PlatformRequestMove)
6302             {
6303                 window->Pos = window->Viewport->Pos;
6304                 MarkIniSettingsDirty(window);
6305             }
6306             else if (memcmp(&window->Viewport->Pos, &window->Pos, sizeof(window->Pos)) != 0)
6307             {
6308                 viewport_rect_changed = true;
6309                 window->Viewport->Pos = window->Pos;
6310             }
6311 
6312             if (window->Viewport->PlatformRequestResize)
6313             {
6314                 window->Size = window->SizeFull = window->Viewport->Size;
6315                 MarkIniSettingsDirty(window);
6316             }
6317             else if (memcmp(&window->Viewport->Size, &window->Size, sizeof(window->Size)) != 0)
6318             {
6319                 viewport_rect_changed = true;
6320                 window->Viewport->Size = window->Size;
6321             }
6322 
6323             // The viewport may have changed monitor since the global update in UpdateViewportsNewFrame()
6324             // Either a SetNextWindowPos() call in the current frame or a SetWindowPos() call in the previous frame may have this effect.
6325             if (viewport_rect_changed)
6326                 UpdateViewportPlatformMonitor(window->Viewport);
6327 
6328             // Update common viewport flags
6329             const ImGuiViewportFlags viewport_flags_to_clear = ImGuiViewportFlags_TopMost | ImGuiViewportFlags_NoTaskBarIcon | ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoRendererClear;
6330             ImGuiViewportFlags viewport_flags = window->Viewport->Flags & ~viewport_flags_to_clear;
6331             const bool is_modal = (flags & ImGuiWindowFlags_Modal) != 0;
6332             const bool is_short_lived_floating_window = (flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0;
6333             if (flags & ImGuiWindowFlags_Tooltip)
6334                 viewport_flags |= ImGuiViewportFlags_TopMost;
6335             if ((g.IO.ConfigViewportsNoTaskBarIcon || is_short_lived_floating_window) && !is_modal)
6336                 viewport_flags |= ImGuiViewportFlags_NoTaskBarIcon;
6337             if (g.IO.ConfigViewportsNoDecoration || is_short_lived_floating_window)
6338                 viewport_flags |= ImGuiViewportFlags_NoDecoration;
6339 
6340             // Not correct to set modal as topmost because:
6341             // - Because other popups can be stacked above a modal (e.g. combo box in a modal)
6342             // - ImGuiViewportFlags_TopMost is currently handled different in backends: in Win32 it is "appear top most" whereas in GLFW and SDL it is "stay topmost"
6343             //if (flags & ImGuiWindowFlags_Modal)
6344             //    viewport_flags |= ImGuiViewportFlags_TopMost;
6345 
6346             // For popups and menus that may be protruding out of their parent viewport, we enable _NoFocusOnClick so that clicking on them
6347             // won't steal the OS focus away from their parent window (which may be reflected in OS the title bar decoration).
6348             // Setting _NoFocusOnClick would technically prevent us from bringing back to front in case they are being covered by an OS window from a different app,
6349             // but it shouldn't be much of a problem considering those are already popups that are closed when clicking elsewhere.
6350             if (is_short_lived_floating_window && !is_modal)
6351                 viewport_flags |= ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoFocusOnClick;
6352 
6353             // We can overwrite viewport flags using ImGuiWindowClass (advanced users)
6354             // We don't default to the main viewport because.
6355             if (window->WindowClass.ParentViewportId)
6356                 window->Viewport->ParentViewportId = window->WindowClass.ParentViewportId;
6357             else if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && parent_window_in_stack)
6358                 window->Viewport->ParentViewportId = parent_window_in_stack->Viewport->ID;
6359             else
6360                 window->Viewport->ParentViewportId = g.IO.ConfigViewportsNoDefaultParent ? 0 : IMGUI_VIEWPORT_DEFAULT_ID;
6361             if (window->WindowClass.ViewportFlagsOverrideSet)
6362                 viewport_flags |= window->WindowClass.ViewportFlagsOverrideSet;
6363             if (window->WindowClass.ViewportFlagsOverrideClear)
6364                 viewport_flags &= ~window->WindowClass.ViewportFlagsOverrideClear;
6365 
6366             // We also tell the backend that clearing the platform window won't be necessary, as our window is filling the viewport and we have disabled BgAlpha
6367             if (!(flags & ImGuiWindowFlags_NoBackground))
6368                 viewport_flags &= ~ImGuiViewportFlags_NoRendererClear;
6369 
6370             window->Viewport->Flags = viewport_flags;
6371         }
6372 
6373         // Calculate the range of allowed position for that window (to be movable and visible past safe area padding)
6374         // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect.
6375         ImRect viewport_rect(window->Viewport->GetMainRect());
6376         ImRect viewport_work_rect(window->Viewport->GetWorkRect());
6377         ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
6378         ImRect visibility_rect(viewport_work_rect.Min + visibility_padding, viewport_work_rect.Max - visibility_padding);
6379 
6380         // Clamp position/size so window stays visible within its viewport or monitor
6381         // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
6382         // FIXME: Similar to code in GetWindowAllowedExtentRect()
6383         if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
6384         {
6385             if (!window->ViewportOwned && viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f)
6386             {
6387                 ClampWindowRect(window, visibility_rect);
6388             }
6389             else if (window->ViewportOwned && g.PlatformIO.Monitors.Size > 0)
6390             {
6391                 if (window->Viewport->PlatformMonitor == -1)
6392                 {
6393                     // Fallback for "lost" window (e.g. a monitor disconnected): we move the window back over the main viewport
6394                     SetWindowPos(window, g.Viewports[0]->Pos + style.DisplayWindowPadding, ImGuiCond_Always);
6395                 }
6396                 else
6397                 {
6398                     ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->Viewport->PlatformMonitor];
6399                     visibility_rect.Min = monitor.WorkPos + visibility_padding;
6400                     visibility_rect.Max = monitor.WorkPos + monitor.WorkSize - visibility_padding;
6401                     ClampWindowRect(window, visibility_rect);
6402                 }
6403             }
6404         }
6405         window->Pos = ImFloor(window->Pos);
6406 
6407         // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
6408         // Large values tend to lead to variety of artifacts and are not recommended.
6409         if (window->ViewportOwned || window->DockIsActive)
6410             window->WindowRounding = 0.0f;
6411         else
6412             window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
6413 
6414         // For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts.
6415         //if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar))
6416         //    window->WindowRounding = ImMin(window->WindowRounding, g.FontSize + style.FramePadding.y * 2.0f);
6417 
6418         // Apply window focus (new and reactivated windows are moved to front)
6419         bool want_focus = false;
6420         if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
6421         {
6422             if (flags & ImGuiWindowFlags_Popup)
6423                 want_focus = true;
6424             else if ((window->DockIsActive || (flags & ImGuiWindowFlags_ChildWindow) == 0) && !(flags & ImGuiWindowFlags_Tooltip))
6425                 want_focus = true;
6426         }
6427 
6428         // Decide if we are going to handle borders and resize grips
6429         const bool handle_borders_and_resize_grips = (window->DockNodeAsHost || !window->DockIsActive);
6430 
6431         // Handle manual resize: Resize Grips, Borders, Gamepad
6432         int border_held = -1;
6433         ImU32 resize_grip_col[4] = {};
6434         const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it.
6435         const float resize_grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
6436         if (handle_borders_and_resize_grips && !window->Collapsed)
6437             if (UpdateWindowManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect))
6438                 use_current_size_for_scrollbar_x = use_current_size_for_scrollbar_y = true;
6439         window->ResizeBorderHeld = (signed char)border_held;
6440 
6441         // Synchronize window --> viewport again and one last time (clamping and manual resize may have affected either)
6442         if (window->ViewportOwned)
6443         {
6444             if (!window->Viewport->PlatformRequestMove)
6445                 window->Viewport->Pos = window->Pos;
6446             if (!window->Viewport->PlatformRequestResize)
6447                 window->Viewport->Size = window->Size;
6448             viewport_rect = window->Viewport->GetMainRect();
6449         }
6450 
6451         // Save last known viewport position within the window itself (so it can be saved in .ini file and restored)
6452         window->ViewportPos = window->Viewport->Pos;
6453 
6454         // SCROLLBAR VISIBILITY
6455 
6456         // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).
6457         if (!window->Collapsed)
6458         {
6459             // When reading the current size we need to read it after size constraints have been applied.
6460             // When we use InnerRect here we are intentionally reading last frame size, same for ScrollbarSizes values before we set them again.
6461             ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - decoration_up_height);
6462             ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + window->ScrollbarSizes;
6463             ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;
6464             float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;
6465             float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;
6466             //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?
6467             window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
6468             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));
6469             if (window->ScrollbarX && !window->ScrollbarY)
6470                 window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar);
6471             window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
6472         }
6473 
6474         // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)
6475         // Update various regions. Variables they depends on should be set above in this function.
6476         // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.
6477 
6478         // Outer rectangle
6479         // Not affected by window border size. Used by:
6480         // - FindHoveredWindow() (w/ extra padding when border resize is enabled)
6481         // - Begin() initial clipping rect for drawing window background and borders.
6482         // - Begin() clipping whole child
6483         const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;
6484         const ImRect outer_rect = window->Rect();
6485         const ImRect title_bar_rect = window->TitleBarRect();
6486         window->OuterRectClipped = outer_rect;
6487         if (window->DockIsActive)
6488             window->OuterRectClipped.Min.y += window->TitleBarHeight();
6489         window->OuterRectClipped.ClipWith(host_rect);
6490 
6491         // Inner rectangle
6492         // Not affected by window border size. Used by:
6493         // - InnerClipRect
6494         // - ScrollToBringRectIntoView()
6495         // - NavUpdatePageUpPageDown()
6496         // - Scrollbar()
6497         window->InnerRect.Min.x = window->Pos.x;
6498         window->InnerRect.Min.y = window->Pos.y + decoration_up_height;
6499         window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x;
6500         window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y;
6501 
6502         // Inner clipping rectangle.
6503         // Will extend a little bit outside the normal work region.
6504         // This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
6505         // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
6506         // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
6507         // Affected by window/frame border size. Used by:
6508         // - Begin() initial clip rect
6509         float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
6510         window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
6511         window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size);
6512         window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
6513         window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize);
6514         window->InnerClipRect.ClipWithFull(host_rect);
6515 
6516         // Default item width. Make it proportional to window size if window manually resizes
6517         if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
6518             window->ItemWidthDefault = ImFloor(window->Size.x * 0.65f);
6519         else
6520             window->ItemWidthDefault = ImFloor(g.FontSize * 16.0f);
6521 
6522         // SCROLLING
6523 
6524         // Lock down maximum scrolling
6525         // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate
6526         // for right/bottom aligned items without creating a scrollbar.
6527         window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth());
6528         window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());
6529 
6530         // Apply scrolling
6531         window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);
6532         window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
6533 
6534         // DRAWING
6535 
6536         // Setup draw list and outer clipping rectangle
6537         IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0);
6538         window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
6539         PushClipRect(host_rect.Min, host_rect.Max, false);
6540 
6541         // Draw modal or window list full viewport dimming background (for other viewports we'll render them in EndFrame)
6542         ImGuiWindow* window_window_list = g.NavWindowingListWindow;
6543         const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0;
6544         const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && ((window == g.NavWindowingTargetAnim->RootWindow) || (window == window_window_list && window_window_list->Viewport != g.NavWindowingTargetAnim->Viewport));
6545         if (dim_bg_for_modal || dim_bg_for_window_list)
6546         {
6547             const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
6548             window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col);
6549         }
6550 
6551         // Draw navigation selection/windowing rectangle background
6552         if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim)
6553         {
6554             ImRect bb = window->Rect();
6555             bb.Expand(g.FontSize);
6556             if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway
6557                 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
6558         }
6559 
6560         // Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call.
6561         // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
6562         // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child.
6563         // We also disabled this when we have dimming overlay behind this specific one child.
6564         // FIXME: More code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected.
6565         const bool is_undocked_or_docked_visible = !window->DockIsActive || window->DockTabIsVisible;
6566         if (is_undocked_or_docked_visible)
6567         {
6568             bool render_decorations_in_parent = false;
6569             if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
6570                 if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0)
6571                     render_decorations_in_parent = true;
6572             if (render_decorations_in_parent)
6573                 window->DrawList = parent_window->DrawList;
6574 
6575             // Handle title bar, scrollbar, resize grips and resize borders
6576             const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
6577             const bool title_bar_is_highlight = want_focus || (window_to_highlight && (window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight || (window->DockNode && window->DockNode == window_to_highlight->DockNode)));
6578             RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, handle_borders_and_resize_grips, resize_grip_count, resize_grip_col, resize_grip_draw_size);
6579 
6580             if (render_decorations_in_parent)
6581                 window->DrawList = &window->DrawListInst;
6582         }
6583 
6584         // Draw navigation selection/windowing rectangle border
6585         if (g.NavWindowingTargetAnim == window)
6586         {
6587             float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
6588             ImRect bb = window->Rect();
6589             bb.Expand(g.FontSize);
6590             if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward
6591             {
6592                 bb.Expand(-g.FontSize - 1.0f);
6593                 rounding = window->WindowRounding;
6594             }
6595             window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f);
6596         }
6597 
6598         // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
6599 
6600         // Work rectangle.
6601         // Affected by window padding and border size. Used by:
6602         // - Columns() for right-most edge
6603         // - TreeNode(), CollapsingHeader() for right-most edge
6604         // - BeginTabBar() for right-most edge
6605         const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);
6606         const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);
6607         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));
6608         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));
6609         window->WorkRect.Min.x = ImFloor(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));
6610         window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
6611         window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
6612         window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
6613         window->ParentWorkRect = window->WorkRect;
6614 
6615         // [LEGACY] Content Region
6616         // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
6617         // Used by:
6618         // - Mouse wheel scrolling + many other things
6619         window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
6620         window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + decoration_up_height;
6621         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));
6622         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));
6623 
6624         // Setup drawing context
6625         // (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.)
6626         window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x;
6627         window->DC.GroupOffset.x = 0.0f;
6628         window->DC.ColumnsOffset.x = 0.0f;
6629         window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, decoration_up_height + window->WindowPadding.y - window->Scroll.y);
6630         window->DC.CursorPos = window->DC.CursorStartPos;
6631         window->DC.CursorPosPrevLine = window->DC.CursorPos;
6632         window->DC.CursorMaxPos = window->DC.CursorStartPos;
6633         window->DC.IdealMaxPos = window->DC.CursorStartPos;
6634         window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
6635         window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
6636 
6637         window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
6638         window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext;
6639         window->DC.NavLayerActiveMaskNext = 0x00;
6640         window->DC.NavHideHighlightOneFrame = false;
6641         window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f);
6642 
6643         window->DC.MenuBarAppending = false;
6644         window->DC.MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user);
6645         window->DC.TreeDepth = 0;
6646         window->DC.TreeJumpToParentOnPopMask = 0x00;
6647         window->DC.ChildWindows.resize(0);
6648         window->DC.StateStorage = &window->StateStorage;
6649         window->DC.CurrentColumns = NULL;
6650         window->DC.LayoutType = ImGuiLayoutType_Vertical;
6651         window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
6652         window->DC.FocusCounterRegular = window->DC.FocusCounterTabStop = -1;
6653 
6654         window->DC.ItemWidth = window->ItemWidthDefault;
6655         window->DC.TextWrapPos = -1.0f; // disabled
6656         window->DC.ItemWidthStack.resize(0);
6657         window->DC.TextWrapPosStack.resize(0);
6658 
6659         if (window->AutoFitFramesX > 0)
6660             window->AutoFitFramesX--;
6661         if (window->AutoFitFramesY > 0)
6662             window->AutoFitFramesY--;
6663 
6664         // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
6665         if (want_focus)
6666         {
6667             FocusWindow(window);
6668             NavInitWindow(window, false);
6669         }
6670 
6671         // Close requested by platform window
6672         if (p_open != NULL && window->Viewport->PlatformRequestClose && window->Viewport != GetMainViewport())
6673         {
6674             if (!window->DockIsActive || window->DockTabIsVisible)
6675             {
6676                 window->Viewport->PlatformRequestClose = false;
6677                 g.NavWindowingToggleLayer = false; // Assume user mapped PlatformRequestClose on ALT-F4 so we disable ALT for menu toggle. False positive not an issue.
6678                 IMGUI_DEBUG_LOG_VIEWPORT("Window '%s' PlatformRequestClose\n", window->Name);
6679                 *p_open = false;
6680             }
6681         }
6682 
6683         // Title bar
6684         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
6685             RenderWindowTitleBarContents(window, title_bar_rect, name, p_open);
6686 
6687         // Clear hit test shape every frame
6688         window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0;
6689 
6690         // Pressing CTRL+C while holding on a window copy its content to the clipboard
6691         // 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.
6692         // Maybe we can support CTRL+C on every element?
6693         /*
6694         //if (g.NavWindow == window && g.ActiveId == 0)
6695         if (g.ActiveId == window->MoveId)
6696             if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
6697                 LogToClipboard();
6698         */
6699 
6700         if (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)
6701         {
6702             // Docking: Dragging a dockable window (or any of its child) turns it into a drag and drop source.
6703             // We need to do this _before_ we overwrite window->DC.LastItemId below because BeginDockableDragDropSource() also overwrites it.
6704             if ((g.MovingWindow == window) && (g.IO.ConfigDockingWithShift == g.IO.KeyShift))
6705                 if ((window->RootWindow->Flags & ImGuiWindowFlags_NoDocking) == 0)
6706                     BeginDockableDragDropSource(window);
6707 
6708             // Docking: Any dockable window can act as a target. For dock node hosts we call BeginDockableDragDropTarget() in DockNodeUpdate() instead.
6709             if (g.DragDropActive && !(flags & ImGuiWindowFlags_NoDocking))
6710                 if (g.MovingWindow == NULL || g.MovingWindow->RootWindow != window)
6711                     if ((window == window->RootWindow) && !(window->Flags & ImGuiWindowFlags_DockNodeHost))
6712                         BeginDockableDragDropTarget(window);
6713         }
6714 
6715         // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
6716         // This is useful to allow creating context menus on title bar only, etc.
6717         if (window->DockIsActive)
6718             SetLastItemData(window, window->ID, window->DockTabItemStatusFlags, window->DockTabItemRect);
6719         else
6720             SetLastItemData(window, window->MoveId, IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, title_bar_rect);
6721 
6722 #ifdef IMGUI_ENABLE_TEST_ENGINE
6723         if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
6724             IMGUI_TEST_ENGINE_ITEM_ADD(window->DC.LastItemRect, window->DC.LastItemId);
6725 #endif
6726     }
6727     else
6728     {
6729         // Append
6730         SetCurrentViewport(window, window->Viewport);
6731         SetCurrentWindow(window);
6732     }
6733 
6734     // Pull/inherit current state
6735     window->DC.ItemFlags = g.ItemFlagsStack.back(); // Inherit from shared stack
6736     window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : 0; // Inherit from parent only // -V595
6737 
6738     if (!(flags & ImGuiWindowFlags_DockNodeHost))
6739         PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
6740 
6741     // 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)
6742     window->WriteAccessed = false;
6743     window->BeginCount++;
6744     g.NextWindowData.ClearFlags();
6745 
6746     // Update visibility
6747     if (first_begin_of_the_frame)
6748     {
6749         // When we are about to select this tab (which will only be visible on the _next frame_), flag it with a non-zero HiddenFramesCannotSkipItems.
6750         // This will have the important effect of actually returning true in Begin() and not setting SkipItems, allowing an earlier submission of the window contents.
6751         // This is analogous to regular windows being hidden from one frame.
6752         // It is especially important as e.g. nested TabBars would otherwise generate flicker in the form of one empty frame, or focus requests won't be processed.
6753         if (window->DockIsActive && !window->DockTabIsVisible)
6754         {
6755             if (window->LastFrameJustFocused == g.FrameCount)
6756                 window->HiddenFramesCannotSkipItems = 1;
6757             else
6758                 window->HiddenFramesCanSkipItems = 1;
6759         }
6760 
6761         if (flags & ImGuiWindowFlags_ChildWindow)
6762         {
6763             // Child window can be out of sight and have "negative" clip windows.
6764             // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
6765             IM_ASSERT((flags& ImGuiWindowFlags_NoTitleBar) != 0 || (window->DockIsActive));
6766             if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) // FIXME: Doesn't make sense for ChildWindow??
6767                 if (!g.LogEnabled)
6768                     if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
6769                         window->HiddenFramesCanSkipItems = 1;
6770 
6771             // Hide along with parent or if parent is collapsed
6772             if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))
6773                 window->HiddenFramesCanSkipItems = 1;
6774             if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0))
6775                 window->HiddenFramesCannotSkipItems = 1;
6776         }
6777 
6778         // 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)
6779         if (style.Alpha <= 0.0f)
6780             window->HiddenFramesCanSkipItems = 1;
6781 
6782         // Update the Hidden flag
6783         window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0) || (window->HiddenFramesForRenderOnly > 0);
6784 
6785         // Update the SkipItems flag, used to early out of all items functions (no layout required)
6786         bool skip_items = false;
6787         if (window->Collapsed || !window->Active || window->Hidden)
6788             if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0)
6789                 skip_items = true;
6790         window->SkipItems = skip_items;
6791     }
6792 
6793     return !window->SkipItems;
6794 }
6795 
End()6796 void ImGui::End()
6797 {
6798     ImGuiContext& g = *GImGui;
6799     ImGuiWindow* window = g.CurrentWindow;
6800 
6801     // Error checking: verify that user hasn't called End() too many times!
6802     if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow)
6803     {
6804         IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!");
6805         return;
6806     }
6807     IM_ASSERT(g.CurrentWindowStack.Size > 0);
6808 
6809     // Error checking: verify that user doesn't directly call End() on a child window.
6810     if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_DockNodeHost) && !window->DockIsActive)
6811         IM_ASSERT_USER_ERROR(g.WithinEndChild, "Must call EndChild() and not End()!");
6812 
6813     // Close anything that is open
6814     if (window->DC.CurrentColumns)
6815         EndColumns();
6816     if (!(window->Flags & ImGuiWindowFlags_DockNodeHost))   // Pop inner window clip rectangle
6817         PopClipRect();
6818 
6819     // Stop logging
6820     if (!(window->Flags & ImGuiWindowFlags_ChildWindow))    // FIXME: add more options for scope of logging
6821         LogFinish();
6822 
6823     // Docking: report contents sizes to parent to allow for auto-resize
6824     if (window->DockNode && window->DockTabIsVisible)
6825         if (ImGuiWindow* host_window = window->DockNode->HostWindow)         // FIXME-DOCK
6826             host_window->DC.CursorMaxPos = window->DC.CursorMaxPos + window->WindowPadding - host_window->WindowPadding;
6827 
6828     // Pop from window stack
6829     g.CurrentWindowStack.pop_back();
6830     if (window->Flags & ImGuiWindowFlags_Popup)
6831         g.BeginPopupStack.pop_back();
6832     window->DC.StackSizesOnBegin.CompareWithCurrentState();
6833     SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
6834     if (g.CurrentWindow)
6835         SetCurrentViewport(g.CurrentWindow, g.CurrentWindow->Viewport);
6836 }
6837 
BringWindowToFocusFront(ImGuiWindow * window)6838 void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
6839 {
6840     ImGuiContext& g = *GImGui;
6841     if (g.WindowsFocusOrder.back() == window)
6842         return;
6843     for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the top-most window
6844         if (g.WindowsFocusOrder[i] == window)
6845         {
6846             memmove(&g.WindowsFocusOrder[i], &g.WindowsFocusOrder[i + 1], (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*));
6847             g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window;
6848             break;
6849         }
6850 }
6851 
BringWindowToDisplayFront(ImGuiWindow * window)6852 void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
6853 {
6854     ImGuiContext& g = *GImGui;
6855     ImGuiWindow* current_front_window = g.Windows.back();
6856     if (current_front_window == window || current_front_window->RootWindow == window) // Cheap early out (could be better)
6857         return;
6858     for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window
6859         if (g.Windows[i] == window)
6860         {
6861             memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
6862             g.Windows[g.Windows.Size - 1] = window;
6863             break;
6864         }
6865 }
6866 
BringWindowToDisplayBack(ImGuiWindow * window)6867 void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
6868 {
6869     ImGuiContext& g = *GImGui;
6870     if (g.Windows[0] == window)
6871         return;
6872     for (int i = 0; i < g.Windows.Size; i++)
6873         if (g.Windows[i] == window)
6874         {
6875             memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
6876             g.Windows[0] = window;
6877             break;
6878         }
6879 }
6880 
6881 // Moving window to front of display and set focus (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)6882 void ImGui::FocusWindow(ImGuiWindow* window)
6883 {
6884     ImGuiContext& g = *GImGui;
6885 
6886     if (g.NavWindow != window)
6887     {
6888         g.NavWindow = window;
6889         if (window && g.NavDisableMouseHover)
6890             g.NavMousePosDirty = true;
6891         g.NavInitRequest = false;
6892         g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
6893         g.NavFocusScopeId = 0;
6894         g.NavIdIsAlive = false;
6895         g.NavLayer = ImGuiNavLayer_Main;
6896         //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL);
6897     }
6898 
6899     // Close popups if any
6900     ClosePopupsOverWindow(window, false);
6901 
6902     // Move the root window to the top of the pile
6903     IM_ASSERT(window == NULL || window->RootWindow != NULL);
6904     ImGuiWindow* focus_front_window = window ? window->RootWindowDockStop : NULL;
6905     ImGuiWindow* display_front_window = window ? window->RootWindow : NULL;
6906     ImGuiDockNode* dock_node = window ? window->DockNode : NULL;
6907     bool active_id_window_is_dock_node_host = (g.ActiveIdWindow && dock_node && dock_node->HostWindow == g.ActiveIdWindow);
6908 
6909     // Steal active widgets. Some of the cases it triggers includes:
6910     // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run.
6911     // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId)
6912     // - Using dock host items (tab, collapse button) can trigger this before we redirect the ActiveIdWindow toward the child window.
6913     if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindowDockStop != focus_front_window)
6914         if (!g.ActiveIdNoClearOnFocusLoss && !active_id_window_is_dock_node_host)
6915             ClearActiveID();
6916 
6917     // Passing NULL allow to disable keyboard focus
6918     if (!window)
6919         return;
6920     window->LastFrameJustFocused = g.FrameCount;
6921 
6922     // Select in dock node
6923     if (dock_node && dock_node->TabBar)
6924         dock_node->TabBar->SelectedTabId = dock_node->TabBar->NextSelectedTabId = window->ID;
6925 
6926     // Bring to front
6927     BringWindowToFocusFront(focus_front_window);
6928     if (((window->Flags | focus_front_window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
6929         BringWindowToDisplayFront(display_front_window);
6930 }
6931 
FocusTopMostWindowUnderOne(ImGuiWindow * under_this_window,ImGuiWindow * ignore_window)6932 void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window)
6933 {
6934     ImGuiContext& g = *GImGui;
6935 
6936     int start_idx = g.WindowsFocusOrder.Size - 1;
6937     if (under_this_window != NULL)
6938     {
6939         int under_this_window_idx = FindWindowFocusIndex(under_this_window);
6940         if (under_this_window_idx != -1)
6941             start_idx = under_this_window_idx - 1;
6942     }
6943     for (int i = start_idx; i >= 0; i--)
6944     {
6945         // 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.
6946         ImGuiWindow* window = g.WindowsFocusOrder[i];
6947         if (window != ignore_window && window->WasActive && window->RootWindowDockStop == window)
6948             if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
6949             {
6950                 // FIXME-DOCK: This is failing (lagging by one frame) for docked windows.
6951                 // If A and B are docked into window and B disappear, at the NewFrame() call site window->NavLastChildNavWindow will still point to B.
6952                 // We might leverage the tab order implicitly stored in window->DockNodeAsHost->TabBar (essentially the 'most_recently_selected_tab' code in tab bar will do that but on next update)
6953                 // to tell which is the "previous" window. Or we may leverage 'LastFrameFocused/LastFrameJustFocused' and have this function handle child window itself?
6954                 ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window);
6955                 FocusWindow(focus_window);
6956                 return;
6957             }
6958     }
6959     FocusWindow(NULL);
6960 }
6961 
SetCurrentFont(ImFont * font)6962 void ImGui::SetCurrentFont(ImFont* font)
6963 {
6964     ImGuiContext& g = *GImGui;
6965     IM_ASSERT(font && font->IsLoaded());    // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
6966     IM_ASSERT(font->Scale > 0.0f);
6967     g.Font = font;
6968     g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale);
6969     g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
6970 
6971     ImFontAtlas* atlas = g.Font->ContainerAtlas;
6972     g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
6973     g.DrawListSharedData.TexUvLines = atlas->TexUvLines;
6974     g.DrawListSharedData.Font = g.Font;
6975     g.DrawListSharedData.FontSize = g.FontSize;
6976 }
6977 
PushFont(ImFont * font)6978 void ImGui::PushFont(ImFont* font)
6979 {
6980     ImGuiContext& g = *GImGui;
6981     if (!font)
6982         font = GetDefaultFont();
6983     SetCurrentFont(font);
6984     g.FontStack.push_back(font);
6985     g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
6986 }
6987 
PopFont()6988 void  ImGui::PopFont()
6989 {
6990     ImGuiContext& g = *GImGui;
6991     g.CurrentWindow->DrawList->PopTextureID();
6992     g.FontStack.pop_back();
6993     SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
6994 }
6995 
PushItemFlag(ImGuiItemFlags option,bool enabled)6996 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
6997 {
6998     ImGuiContext& g = *GImGui;
6999     ImGuiWindow* window = g.CurrentWindow;
7000     ImGuiItemFlags item_flags = window->DC.ItemFlags;
7001     IM_ASSERT(item_flags == g.ItemFlagsStack.back());
7002     if (enabled)
7003         item_flags |= option;
7004     else
7005         item_flags &= ~option;
7006     window->DC.ItemFlags = item_flags;
7007     g.ItemFlagsStack.push_back(item_flags);
7008 }
7009 
PopItemFlag()7010 void ImGui::PopItemFlag()
7011 {
7012     ImGuiContext& g = *GImGui;
7013     ImGuiWindow* window = g.CurrentWindow;
7014     IM_ASSERT(g.ItemFlagsStack.Size > 1); // Too many calls to PopItemFlag() - we always leave a 0 at the bottom of the stack.
7015     g.ItemFlagsStack.pop_back();
7016     window->DC.ItemFlags = g.ItemFlagsStack.back();
7017 }
7018 
7019 // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system.
PushAllowKeyboardFocus(bool allow_keyboard_focus)7020 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
7021 {
7022     PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus);
7023 }
7024 
PopAllowKeyboardFocus()7025 void ImGui::PopAllowKeyboardFocus()
7026 {
7027     PopItemFlag();
7028 }
7029 
PushButtonRepeat(bool repeat)7030 void ImGui::PushButtonRepeat(bool repeat)
7031 {
7032     PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
7033 }
7034 
PopButtonRepeat()7035 void ImGui::PopButtonRepeat()
7036 {
7037     PopItemFlag();
7038 }
7039 
PushTextWrapPos(float wrap_pos_x)7040 void ImGui::PushTextWrapPos(float wrap_pos_x)
7041 {
7042     ImGuiWindow* window = GetCurrentWindow();
7043     window->DC.TextWrapPos = wrap_pos_x;
7044     window->DC.TextWrapPosStack.push_back(wrap_pos_x);
7045 }
7046 
PopTextWrapPos()7047 void ImGui::PopTextWrapPos()
7048 {
7049     ImGuiWindow* window = GetCurrentWindow();
7050     window->DC.TextWrapPosStack.pop_back();
7051     window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
7052 }
7053 
IsWindowChildOf(ImGuiWindow * window,ImGuiWindow * potential_parent)7054 bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
7055 {
7056     if (window->RootWindow == potential_parent)
7057         return true;
7058     while (window != NULL)
7059     {
7060         if (window == potential_parent)
7061             return true;
7062         window = window->ParentWindow;
7063     }
7064     return false;
7065 }
7066 
IsWindowAbove(ImGuiWindow * potential_above,ImGuiWindow * potential_below)7067 bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below)
7068 {
7069     ImGuiContext& g = *GImGui;
7070     for (int i = g.Windows.Size - 1; i >= 0; i--)
7071     {
7072         ImGuiWindow* candidate_window = g.Windows[i];
7073         if (candidate_window == potential_above)
7074             return true;
7075         if (candidate_window == potential_below)
7076             return false;
7077     }
7078     return false;
7079 }
7080 
IsWindowHovered(ImGuiHoveredFlags flags)7081 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
7082 {
7083     IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0);   // Flags not supported by this function
7084     ImGuiContext& g = *GImGui;
7085 
7086     if (flags & ImGuiHoveredFlags_AnyWindow)
7087     {
7088         if (g.HoveredWindow == NULL)
7089             return false;
7090     }
7091     else
7092     {
7093         switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
7094         {
7095         case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
7096             if (g.HoveredWindow == NULL || g.HoveredWindow->RootWindowDockStop != g.CurrentWindow->RootWindowDockStop)
7097                 return false;
7098             break;
7099         case ImGuiHoveredFlags_RootWindow:
7100             if (g.HoveredWindow != g.CurrentWindow->RootWindowDockStop)
7101                 return false;
7102             break;
7103         case ImGuiHoveredFlags_ChildWindows:
7104             if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow))
7105                 return false;
7106             break;
7107         default:
7108             if (g.HoveredWindow != g.CurrentWindow)
7109                 return false;
7110             break;
7111         }
7112     }
7113 
7114     if (!IsWindowContentHoverable(g.HoveredWindow, flags))
7115         return false;
7116     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
7117         if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
7118             return false;
7119     return true;
7120 }
7121 
IsWindowFocused(ImGuiFocusedFlags flags)7122 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
7123 {
7124     ImGuiContext& g = *GImGui;
7125 
7126     if (flags & ImGuiFocusedFlags_AnyWindow)
7127         return g.NavWindow != NULL;
7128 
7129     IM_ASSERT(g.CurrentWindow);     // Not inside a Begin()/End()
7130     switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
7131     {
7132     case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
7133         return g.NavWindow && g.NavWindow->RootWindowDockStop == g.CurrentWindow->RootWindowDockStop;
7134     case ImGuiFocusedFlags_RootWindow:
7135         return g.NavWindow == g.CurrentWindow->RootWindowDockStop;
7136     case ImGuiFocusedFlags_ChildWindows:
7137         return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
7138     default:
7139         return g.NavWindow == g.CurrentWindow;
7140     }
7141 }
7142 
GetWindowDockID()7143 ImGuiID ImGui::GetWindowDockID()
7144 {
7145     ImGuiContext& g = *GImGui;
7146     return g.CurrentWindow->DockId;
7147 }
7148 
IsWindowDocked()7149 bool ImGui::IsWindowDocked()
7150 {
7151     ImGuiContext& g = *GImGui;
7152     return g.CurrentWindow->DockIsActive;
7153 }
7154 
7155 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
7156 // Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmatically.
7157 // If you want a window to never be focused, you may use the e.g. NoInputs flag.
IsWindowNavFocusable(ImGuiWindow * window)7158 bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
7159 {
7160     return window->WasActive && window == window->RootWindowDockStop && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
7161 }
7162 
GetWindowWidth()7163 float ImGui::GetWindowWidth()
7164 {
7165     ImGuiWindow* window = GImGui->CurrentWindow;
7166     return window->Size.x;
7167 }
7168 
GetWindowHeight()7169 float ImGui::GetWindowHeight()
7170 {
7171     ImGuiWindow* window = GImGui->CurrentWindow;
7172     return window->Size.y;
7173 }
7174 
GetWindowPos()7175 ImVec2 ImGui::GetWindowPos()
7176 {
7177     ImGuiContext& g = *GImGui;
7178     ImGuiWindow* window = g.CurrentWindow;
7179     return window->Pos;
7180 }
7181 
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiCond cond)7182 void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
7183 {
7184     // Test condition (NB: bit 0 is always true) and clear flags for next time
7185     if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
7186         return;
7187 
7188     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
7189     window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
7190     window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
7191 
7192     // Set
7193     const ImVec2 old_pos = window->Pos;
7194     window->Pos = ImFloor(pos);
7195     ImVec2 offset = window->Pos - old_pos;
7196     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
7197     window->DC.CursorMaxPos += offset;      // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.
7198     window->DC.CursorStartPos += offset;
7199 }
7200 
SetWindowPos(const ImVec2 & pos,ImGuiCond cond)7201 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
7202 {
7203     ImGuiWindow* window = GetCurrentWindowRead();
7204     SetWindowPos(window, pos, cond);
7205 }
7206 
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiCond cond)7207 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
7208 {
7209     if (ImGuiWindow* window = FindWindowByName(name))
7210         SetWindowPos(window, pos, cond);
7211 }
7212 
GetWindowSize()7213 ImVec2 ImGui::GetWindowSize()
7214 {
7215     ImGuiWindow* window = GetCurrentWindowRead();
7216     return window->Size;
7217 }
7218 
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiCond cond)7219 void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
7220 {
7221     // Test condition (NB: bit 0 is always true) and clear flags for next time
7222     if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
7223         return;
7224 
7225     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
7226     window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
7227 
7228     // Set
7229     if (size.x > 0.0f)
7230     {
7231         window->AutoFitFramesX = 0;
7232         window->SizeFull.x = IM_FLOOR(size.x);
7233     }
7234     else
7235     {
7236         window->AutoFitFramesX = 2;
7237         window->AutoFitOnlyGrows = false;
7238     }
7239     if (size.y > 0.0f)
7240     {
7241         window->AutoFitFramesY = 0;
7242         window->SizeFull.y = IM_FLOOR(size.y);
7243     }
7244     else
7245     {
7246         window->AutoFitFramesY = 2;
7247         window->AutoFitOnlyGrows = false;
7248     }
7249 }
7250 
SetWindowSize(const ImVec2 & size,ImGuiCond cond)7251 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
7252 {
7253     SetWindowSize(GImGui->CurrentWindow, size, cond);
7254 }
7255 
SetWindowSize(const char * name,const ImVec2 & size,ImGuiCond cond)7256 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
7257 {
7258     if (ImGuiWindow* window = FindWindowByName(name))
7259         SetWindowSize(window, size, cond);
7260 }
7261 
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiCond cond)7262 void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
7263 {
7264     // Test condition (NB: bit 0 is always true) and clear flags for next time
7265     if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
7266         return;
7267     window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
7268 
7269     // Set
7270     window->Collapsed = collapsed;
7271 }
7272 
SetWindowHitTestHole(ImGuiWindow * window,const ImVec2 & pos,const ImVec2 & size)7273 void ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size)
7274 {
7275     IM_ASSERT(window->HitTestHoleSize.x == 0);     // We don't support multiple holes/hit test filters
7276     window->HitTestHoleSize = ImVec2ih(size);
7277     window->HitTestHoleOffset = ImVec2ih(pos - window->Pos);
7278 }
7279 
SetWindowCollapsed(bool collapsed,ImGuiCond cond)7280 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
7281 {
7282     SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
7283 }
7284 
IsWindowCollapsed()7285 bool ImGui::IsWindowCollapsed()
7286 {
7287     ImGuiWindow* window = GetCurrentWindowRead();
7288     return window->Collapsed;
7289 }
7290 
IsWindowAppearing()7291 bool ImGui::IsWindowAppearing()
7292 {
7293     ImGuiWindow* window = GetCurrentWindowRead();
7294     return window->Appearing;
7295 }
7296 
SetWindowCollapsed(const char * name,bool collapsed,ImGuiCond cond)7297 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
7298 {
7299     if (ImGuiWindow* window = FindWindowByName(name))
7300         SetWindowCollapsed(window, collapsed, cond);
7301 }
7302 
SetWindowFocus()7303 void ImGui::SetWindowFocus()
7304 {
7305     FocusWindow(GImGui->CurrentWindow);
7306 }
7307 
SetWindowFocus(const char * name)7308 void ImGui::SetWindowFocus(const char* name)
7309 {
7310     if (name)
7311     {
7312         if (ImGuiWindow* window = FindWindowByName(name))
7313             FocusWindow(window);
7314     }
7315     else
7316     {
7317         FocusWindow(NULL);
7318     }
7319 }
7320 
SetNextWindowPos(const ImVec2 & pos,ImGuiCond cond,const ImVec2 & pivot)7321 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
7322 {
7323     ImGuiContext& g = *GImGui;
7324     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
7325     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos;
7326     g.NextWindowData.PosVal = pos;
7327     g.NextWindowData.PosPivotVal = pivot;
7328     g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
7329     g.NextWindowData.PosUndock = true;
7330 }
7331 
SetNextWindowSize(const ImVec2 & size,ImGuiCond cond)7332 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
7333 {
7334     ImGuiContext& g = *GImGui;
7335     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
7336     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize;
7337     g.NextWindowData.SizeVal = size;
7338     g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
7339 }
7340 
SetNextWindowSizeConstraints(const ImVec2 & size_min,const ImVec2 & size_max,ImGuiSizeCallback custom_callback,void * custom_callback_user_data)7341 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
7342 {
7343     ImGuiContext& g = *GImGui;
7344     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint;
7345     g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
7346     g.NextWindowData.SizeCallback = custom_callback;
7347     g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
7348 }
7349 
7350 // Content size = inner scrollable rectangle, padded with WindowPadding.
7351 // SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
SetNextWindowContentSize(const ImVec2 & size)7352 void ImGui::SetNextWindowContentSize(const ImVec2& size)
7353 {
7354     ImGuiContext& g = *GImGui;
7355     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize;
7356     g.NextWindowData.ContentSizeVal = ImFloor(size);
7357 }
7358 
SetNextWindowScroll(const ImVec2 & scroll)7359 void ImGui::SetNextWindowScroll(const ImVec2& scroll)
7360 {
7361     ImGuiContext& g = *GImGui;
7362     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasScroll;
7363     g.NextWindowData.ScrollVal = scroll;
7364 }
7365 
SetNextWindowCollapsed(bool collapsed,ImGuiCond cond)7366 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
7367 {
7368     ImGuiContext& g = *GImGui;
7369     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
7370     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed;
7371     g.NextWindowData.CollapsedVal = collapsed;
7372     g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
7373 }
7374 
SetNextWindowFocus()7375 void ImGui::SetNextWindowFocus()
7376 {
7377     ImGuiContext& g = *GImGui;
7378     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus;
7379 }
7380 
SetNextWindowBgAlpha(float alpha)7381 void ImGui::SetNextWindowBgAlpha(float alpha)
7382 {
7383     ImGuiContext& g = *GImGui;
7384     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha;
7385     g.NextWindowData.BgAlphaVal = alpha;
7386 }
7387 
SetNextWindowViewport(ImGuiID id)7388 void ImGui::SetNextWindowViewport(ImGuiID id)
7389 {
7390     ImGuiContext& g = *GImGui;
7391     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasViewport;
7392     g.NextWindowData.ViewportId = id;
7393 }
7394 
SetNextWindowDockID(ImGuiID id,ImGuiCond cond)7395 void ImGui::SetNextWindowDockID(ImGuiID id, ImGuiCond cond)
7396 {
7397     ImGuiContext& g = *GImGui;
7398     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasDock;
7399     g.NextWindowData.DockCond = cond ? cond : ImGuiCond_Always;
7400     g.NextWindowData.DockId = id;
7401 }
7402 
SetNextWindowClass(const ImGuiWindowClass * window_class)7403 void ImGui::SetNextWindowClass(const ImGuiWindowClass* window_class)
7404 {
7405     ImGuiContext& g = *GImGui;
7406     IM_ASSERT((window_class->ViewportFlagsOverrideSet & window_class->ViewportFlagsOverrideClear) == 0); // Cannot set both set and clear for the same bit
7407     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasWindowClass;
7408     g.NextWindowData.WindowClass = *window_class;
7409 }
7410 
GetWindowDrawList()7411 ImDrawList* ImGui::GetWindowDrawList()
7412 {
7413     ImGuiWindow* window = GetCurrentWindow();
7414     return window->DrawList;
7415 }
7416 
GetWindowDpiScale()7417 float ImGui::GetWindowDpiScale()
7418 {
7419     ImGuiContext& g = *GImGui;
7420     return g.CurrentDpiScale;
7421 }
7422 
GetWindowViewport()7423 ImGuiViewport* ImGui::GetWindowViewport()
7424 {
7425     ImGuiContext& g = *GImGui;
7426     IM_ASSERT(g.CurrentViewport != NULL && g.CurrentViewport == g.CurrentWindow->Viewport);
7427     return g.CurrentViewport;
7428 }
7429 
GetFont()7430 ImFont* ImGui::GetFont()
7431 {
7432     return GImGui->Font;
7433 }
7434 
GetFontSize()7435 float ImGui::GetFontSize()
7436 {
7437     return GImGui->FontSize;
7438 }
7439 
GetFontTexUvWhitePixel()7440 ImVec2 ImGui::GetFontTexUvWhitePixel()
7441 {
7442     return GImGui->DrawListSharedData.TexUvWhitePixel;
7443 }
7444 
SetWindowFontScale(float scale)7445 void ImGui::SetWindowFontScale(float scale)
7446 {
7447     IM_ASSERT(scale > 0.0f);
7448     ImGuiContext& g = *GImGui;
7449     ImGuiWindow* window = GetCurrentWindow();
7450     window->FontWindowScale = scale;
7451     g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
7452 }
7453 
ActivateItem(ImGuiID id)7454 void ImGui::ActivateItem(ImGuiID id)
7455 {
7456     ImGuiContext& g = *GImGui;
7457     g.NavNextActivateId = id;
7458 }
7459 
PushFocusScope(ImGuiID id)7460 void ImGui::PushFocusScope(ImGuiID id)
7461 {
7462     ImGuiContext& g = *GImGui;
7463     ImGuiWindow* window = g.CurrentWindow;
7464     g.FocusScopeStack.push_back(window->DC.NavFocusScopeIdCurrent);
7465     window->DC.NavFocusScopeIdCurrent = id;
7466 }
7467 
PopFocusScope()7468 void ImGui::PopFocusScope()
7469 {
7470     ImGuiContext& g = *GImGui;
7471     ImGuiWindow* window = g.CurrentWindow;
7472     IM_ASSERT(g.FocusScopeStack.Size > 0); // Too many PopFocusScope() ?
7473     window->DC.NavFocusScopeIdCurrent = g.FocusScopeStack.back();
7474     g.FocusScopeStack.pop_back();
7475 }
7476 
SetKeyboardFocusHere(int offset)7477 void ImGui::SetKeyboardFocusHere(int offset)
7478 {
7479     IM_ASSERT(offset >= -1);    // -1 is allowed but not below
7480     ImGuiContext& g = *GImGui;
7481     ImGuiWindow* window = g.CurrentWindow;
7482     g.FocusRequestNextWindow = window;
7483     g.FocusRequestNextCounterRegular = window->DC.FocusCounterRegular + 1 + offset;
7484     g.FocusRequestNextCounterTabStop = INT_MAX;
7485 }
7486 
SetItemDefaultFocus()7487 void ImGui::SetItemDefaultFocus()
7488 {
7489     ImGuiContext& g = *GImGui;
7490     ImGuiWindow* window = g.CurrentWindow;
7491     if (!window->Appearing)
7492         return;
7493     if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent)
7494     {
7495         g.NavInitRequest = false;
7496         g.NavInitResultId = g.NavWindow->DC.LastItemId;
7497         g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos);
7498         NavUpdateAnyRequestFlag();
7499         if (!IsItemVisible())
7500             SetScrollHereY();
7501     }
7502 }
7503 
SetStateStorage(ImGuiStorage * tree)7504 void ImGui::SetStateStorage(ImGuiStorage* tree)
7505 {
7506     ImGuiWindow* window = GImGui->CurrentWindow;
7507     window->DC.StateStorage = tree ? tree : &window->StateStorage;
7508 }
7509 
GetStateStorage()7510 ImGuiStorage* ImGui::GetStateStorage()
7511 {
7512     ImGuiWindow* window = GImGui->CurrentWindow;
7513     return window->DC.StateStorage;
7514 }
7515 
PushID(const char * str_id)7516 void ImGui::PushID(const char* str_id)
7517 {
7518     ImGuiContext& g = *GImGui;
7519     ImGuiWindow* window = g.CurrentWindow;
7520     ImGuiID id = window->GetIDNoKeepAlive(str_id);
7521     window->IDStack.push_back(id);
7522 }
7523 
PushID(const char * str_id_begin,const char * str_id_end)7524 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
7525 {
7526     ImGuiContext& g = *GImGui;
7527     ImGuiWindow* window = g.CurrentWindow;
7528     ImGuiID id = window->GetIDNoKeepAlive(str_id_begin, str_id_end);
7529     window->IDStack.push_back(id);
7530 }
7531 
PushID(const void * ptr_id)7532 void ImGui::PushID(const void* ptr_id)
7533 {
7534     ImGuiContext& g = *GImGui;
7535     ImGuiWindow* window = g.CurrentWindow;
7536     ImGuiID id = window->GetIDNoKeepAlive(ptr_id);
7537     window->IDStack.push_back(id);
7538 }
7539 
PushID(int int_id)7540 void ImGui::PushID(int int_id)
7541 {
7542     ImGuiContext& g = *GImGui;
7543     ImGuiWindow* window = g.CurrentWindow;
7544     ImGuiID id = window->GetIDNoKeepAlive(int_id);
7545     window->IDStack.push_back(id);
7546 }
7547 
7548 // Push a given id value ignoring the ID stack as a seed.
PushOverrideID(ImGuiID id)7549 void ImGui::PushOverrideID(ImGuiID id)
7550 {
7551     ImGuiContext& g = *GImGui;
7552     ImGuiWindow* window = g.CurrentWindow;
7553     window->IDStack.push_back(id);
7554 }
7555 
7556 // Helper to avoid a common series of PushOverrideID -> GetID() -> PopID() call
7557 // (note that when using this pattern, TestEngine's "Stack Tool" will tend to not display the intermediate stack level.
7558 //  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)7559 ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed)
7560 {
7561     ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
7562     ImGui::KeepAliveID(id);
7563 #ifdef IMGUI_ENABLE_TEST_ENGINE
7564     ImGuiContext& g = *GImGui;
7565     IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
7566 #endif
7567     return id;
7568 }
7569 
PopID()7570 void ImGui::PopID()
7571 {
7572     ImGuiWindow* window = GImGui->CurrentWindow;
7573     IM_ASSERT(window->IDStack.Size > 1); // Too many PopID(), or could be popping in a wrong/different window?
7574     window->IDStack.pop_back();
7575 }
7576 
GetID(const char * str_id)7577 ImGuiID ImGui::GetID(const char* str_id)
7578 {
7579     ImGuiWindow* window = GImGui->CurrentWindow;
7580     return window->GetID(str_id);
7581 }
7582 
GetID(const char * str_id_begin,const char * str_id_end)7583 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
7584 {
7585     ImGuiWindow* window = GImGui->CurrentWindow;
7586     return window->GetID(str_id_begin, str_id_end);
7587 }
7588 
GetID(const void * ptr_id)7589 ImGuiID ImGui::GetID(const void* ptr_id)
7590 {
7591     ImGuiWindow* window = GImGui->CurrentWindow;
7592     return window->GetID(ptr_id);
7593 }
7594 
IsRectVisible(const ImVec2 & size)7595 bool ImGui::IsRectVisible(const ImVec2& size)
7596 {
7597     ImGuiWindow* window = GImGui->CurrentWindow;
7598     return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
7599 }
7600 
IsRectVisible(const ImVec2 & rect_min,const ImVec2 & rect_max)7601 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
7602 {
7603     ImGuiWindow* window = GImGui->CurrentWindow;
7604     return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
7605 }
7606 
7607 
7608 //-----------------------------------------------------------------------------
7609 // [SECTION] ERROR CHECKING
7610 //-----------------------------------------------------------------------------
7611 
7612 // Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui.
7613 // Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
7614 // If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. your user code
7615 // may see different structures than what imgui.cpp sees, which is problematic.
7616 // 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)7617 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)
7618 {
7619     bool error = false;
7620     if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); }
7621     if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
7622     if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
7623     if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
7624     if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
7625     if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
7626     if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); }
7627     return !error;
7628 }
7629 
ErrorCheckNewFrameSanityChecks()7630 static void ImGui::ErrorCheckNewFrameSanityChecks()
7631 {
7632     ImGuiContext& g = *GImGui;
7633 
7634     // Check user IM_ASSERT macro
7635     // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means you assert macro is incorrectly defined!
7636     //  If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block.
7637     //  This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.)
7638     // #define IM_ASSERT(EXPR)   if (SomeCode(EXPR)) SomeMoreCode();                    // Wrong!
7639     // #define IM_ASSERT(EXPR)   do { if (SomeCode(EXPR)) SomeMoreCode(); } while (0)   // Correct!
7640     if (true) IM_ASSERT(1); else IM_ASSERT(0);
7641 
7642     // Check user data
7643     // (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)
7644     IM_ASSERT(g.Initialized);
7645     IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0)              && "Need a positive DeltaTime!");
7646     IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount)  && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
7647     IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f  && "Invalid DisplaySize value!");
7648     IM_ASSERT(g.IO.Fonts->Fonts.Size > 0                                && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()?");
7649     IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded()                          && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()?");
7650     IM_ASSERT(g.Style.CurveTessellationTol > 0.0f                       && "Invalid style setting!");
7651     IM_ASSERT(g.Style.CircleSegmentMaxError > 0.0f                      && "Invalid style setting!");
7652     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
7653     IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting.");
7654     IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
7655     for (int n = 0; n < ImGuiKey_COUNT; n++)
7656         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)");
7657 
7658     // 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)
7659     if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
7660         IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
7661 
7662     // Check: the io.ConfigWindowsResizeFromEdges option requires backend to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
7663     if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
7664         g.IO.ConfigWindowsResizeFromEdges = false;
7665 
7666     // Perform simple check: error if Docking or Viewport are enabled _exactly_ on frame 1 (instead of frame 0 or later), which is a common error leading to loss of .ini data.
7667     if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_DockingEnable) == 0)
7668         IM_ASSERT(0 && "Please set DockingEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!");
7669     if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_ViewportsEnable) == 0)
7670         IM_ASSERT(0 && "Please set ViewportsEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!");
7671 
7672     // Perform simple checks: multi-viewport and platform windows support
7673     if (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
7674     {
7675         if ((g.IO.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasViewports))
7676         {
7677             IM_ASSERT((g.FrameCount == 0 || g.FrameCount == g.FrameCountPlatformEnded) && "Forgot to call UpdatePlatformWindows() in main loop after EndFrame()? Check examples/ applications for reference.");
7678             IM_ASSERT(g.PlatformIO.Platform_CreateWindow  != NULL && "Platform init didn't install handlers?");
7679             IM_ASSERT(g.PlatformIO.Platform_DestroyWindow != NULL && "Platform init didn't install handlers?");
7680             IM_ASSERT(g.PlatformIO.Platform_GetWindowPos  != NULL && "Platform init didn't install handlers?");
7681             IM_ASSERT(g.PlatformIO.Platform_SetWindowPos  != NULL && "Platform init didn't install handlers?");
7682             IM_ASSERT(g.PlatformIO.Platform_GetWindowSize != NULL && "Platform init didn't install handlers?");
7683             IM_ASSERT(g.PlatformIO.Platform_SetWindowSize != NULL && "Platform init didn't install handlers?");
7684             IM_ASSERT(g.PlatformIO.Monitors.Size > 0 && "Platform init didn't setup Monitors list?");
7685             IM_ASSERT((g.Viewports[0]->PlatformUserData != NULL || g.Viewports[0]->PlatformHandle != NULL) && "Platform init didn't setup main viewport.");
7686             if (g.IO.ConfigDockingTransparentPayload && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
7687                 IM_ASSERT(g.PlatformIO.Platform_SetWindowAlpha != NULL && "Platform_SetWindowAlpha handler is required to use io.ConfigDockingTransparent!");
7688         }
7689         else
7690         {
7691             // Disable feature, our backends do not support it
7692             g.IO.ConfigFlags &= ~ImGuiConfigFlags_ViewportsEnable;
7693         }
7694 
7695         // Perform simple checks on platform monitor data + compute a total bounding box for quick early outs
7696         for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++)
7697         {
7698             ImGuiPlatformMonitor& mon = g.PlatformIO.Monitors[monitor_n];
7699             IM_ASSERT(mon.MainSize.x > 0.0f && mon.MainSize.y > 0.0f && "Monitor main bounds not setup properly.");
7700             IM_ASSERT(ImRect(mon.MainPos, mon.MainPos + mon.MainSize).Contains(ImRect(mon.WorkPos, mon.WorkPos + mon.WorkSize)) && "Monitor work bounds not setup properly. If you don't have work area information, just copy MainPos/MainSize into them.");
7701             IM_ASSERT(mon.DpiScale != 0.0f);
7702         }
7703     }
7704 }
7705 
ErrorCheckEndFrameSanityChecks()7706 static void ImGui::ErrorCheckEndFrameSanityChecks()
7707 {
7708     ImGuiContext& g = *GImGui;
7709 
7710     // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame()
7711     // One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame().
7712     // It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will
7713     // send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs.
7714     // We silently accommodate for this case by ignoring/ the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0),
7715     // while still correctly asserting on mid-frame key press events.
7716     const ImGuiKeyModFlags key_mod_flags = GetMergedKeyModFlags();
7717     IM_ASSERT((key_mod_flags == 0 || g.IO.KeyMods == key_mod_flags) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
7718     IM_UNUSED(key_mod_flags);
7719 
7720     // Recover from errors
7721     //ErrorCheckEndFrameRecover();
7722 
7723     // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you
7724     // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API).
7725     if (g.CurrentWindowStack.Size != 1)
7726     {
7727         if (g.CurrentWindowStack.Size > 1)
7728         {
7729             IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?");
7730             while (g.CurrentWindowStack.Size > 1)
7731                 End();
7732         }
7733         else
7734         {
7735             IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?");
7736         }
7737     }
7738 
7739     IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, "Missing EndGroup call!");
7740 }
7741 
7742 // Experimental recovery from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.
7743 // Must be called during or before EndFrame().
7744 // This is generally flawed as we are not necessarily End/Popping things in the right order.
7745 // FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.
7746 // FIXME: Can't recover from interleaved BeginTabBar/Begin
ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback,void * user_data)7747 void    ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data)
7748 {
7749     // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations"
7750     ImGuiContext& g = *GImGui;
7751     while (g.CurrentWindowStack.Size > 0)
7752     {
7753 #ifdef IMGUI_HAS_TABLE
7754         while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow))
7755         {
7756             if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name);
7757             EndTable();
7758         }
7759 #endif
7760         ImGuiWindow* window = g.CurrentWindow;
7761         IM_ASSERT(window != NULL);
7762         while (g.CurrentTabBar != NULL) //-V1044
7763         {
7764             if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name);
7765             EndTabBar();
7766         }
7767         while (window->DC.TreeDepth > 0)
7768         {
7769             if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name);
7770             TreePop();
7771         }
7772         while (g.GroupStack.Size > window->DC.StackSizesOnBegin.SizeOfGroupStack)
7773         {
7774             if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name);
7775             EndGroup();
7776         }
7777         while (window->IDStack.Size > 1)
7778         {
7779             if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name);
7780             PopID();
7781         }
7782         while (g.ColorStack.Size > window->DC.StackSizesOnBegin.SizeOfColorStack)
7783         {
7784             if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col));
7785             PopStyleColor();
7786         }
7787         while (g.StyleVarStack.Size > window->DC.StackSizesOnBegin.SizeOfStyleVarStack)
7788         {
7789             if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name);
7790             PopStyleVar();
7791         }
7792         while (g.FocusScopeStack.Size > window->DC.StackSizesOnBegin.SizeOfFocusScopeStack)
7793         {
7794             if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name);
7795             PopFocusScope();
7796         }
7797         if (g.CurrentWindowStack.Size == 1)
7798         {
7799             IM_ASSERT(g.CurrentWindow->IsFallbackWindow);
7800             break;
7801         }
7802         IM_ASSERT(window == g.CurrentWindow);
7803         if (window->Flags & ImGuiWindowFlags_ChildWindow)
7804         {
7805             if (log_callback) log_callback(user_data, "Recovered from missing EndChild() for '%s'", window->Name);
7806             EndChild();
7807         }
7808         else
7809         {
7810             if (log_callback) log_callback(user_data, "Recovered from missing End() for '%s'", window->Name);
7811             End();
7812         }
7813     }
7814 }
7815 
7816 // Save current stack sizes for later compare
SetToCurrentState()7817 void ImGuiStackSizes::SetToCurrentState()
7818 {
7819     ImGuiContext& g = *GImGui;
7820     ImGuiWindow* window = g.CurrentWindow;
7821     SizeOfIDStack = (short)window->IDStack.Size;
7822     SizeOfColorStack = (short)g.ColorStack.Size;
7823     SizeOfStyleVarStack = (short)g.StyleVarStack.Size;
7824     SizeOfFontStack = (short)g.FontStack.Size;
7825     SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size;
7826     SizeOfGroupStack = (short)g.GroupStack.Size;
7827     SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size;
7828 }
7829 
7830 // Compare to detect usage errors
CompareWithCurrentState()7831 void ImGuiStackSizes::CompareWithCurrentState()
7832 {
7833     ImGuiContext& g = *GImGui;
7834     ImGuiWindow* window = g.CurrentWindow;
7835     IM_UNUSED(window);
7836 
7837     // Window stacks
7838     // NOT checking: DC.ItemWidth, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin)
7839     IM_ASSERT(SizeOfIDStack         == window->IDStack.Size     && "PushID/PopID or TreeNode/TreePop Mismatch!");
7840 
7841     // Global stacks
7842     // 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.
7843     IM_ASSERT(SizeOfGroupStack      == g.GroupStack.Size        && "BeginGroup/EndGroup Mismatch!");
7844     IM_ASSERT(SizeOfBeginPopupStack == g.BeginPopupStack.Size   && "BeginPopup/EndPopup or BeginMenu/EndMenu Mismatch!");
7845     IM_ASSERT(SizeOfColorStack      >= g.ColorStack.Size        && "PushStyleColor/PopStyleColor Mismatch!");
7846     IM_ASSERT(SizeOfStyleVarStack   >= g.StyleVarStack.Size     && "PushStyleVar/PopStyleVar Mismatch!");
7847     IM_ASSERT(SizeOfFontStack       >= g.FontStack.Size         && "PushFont/PopFont Mismatch!");
7848     IM_ASSERT(SizeOfFocusScopeStack == g.FocusScopeStack.Size   && "PushFocusScope/PopFocusScope Mismatch!");
7849 }
7850 
7851 
7852 //-----------------------------------------------------------------------------
7853 // [SECTION] LAYOUT
7854 //-----------------------------------------------------------------------------
7855 // - ItemSize()
7856 // - ItemAdd()
7857 // - SameLine()
7858 // - GetCursorScreenPos()
7859 // - SetCursorScreenPos()
7860 // - GetCursorPos(), GetCursorPosX(), GetCursorPosY()
7861 // - SetCursorPos(), SetCursorPosX(), SetCursorPosY()
7862 // - GetCursorStartPos()
7863 // - Indent()
7864 // - Unindent()
7865 // - SetNextItemWidth()
7866 // - PushItemWidth()
7867 // - PushMultiItemsWidths()
7868 // - PopItemWidth()
7869 // - CalcItemWidth()
7870 // - CalcItemSize()
7871 // - GetTextLineHeight()
7872 // - GetTextLineHeightWithSpacing()
7873 // - GetFrameHeight()
7874 // - GetFrameHeightWithSpacing()
7875 // - GetContentRegionMax()
7876 // - GetContentRegionMaxAbs() [Internal]
7877 // - GetContentRegionAvail(),
7878 // - GetWindowContentRegionMin(), GetWindowContentRegionMax()
7879 // - GetWindowContentRegionWidth()
7880 // - BeginGroup()
7881 // - EndGroup()
7882 // Also see in imgui_widgets: tab bars, columns.
7883 //-----------------------------------------------------------------------------
7884 
7885 // Advance cursor given item size for layout.
7886 // Register minimum needed size so it can extend the bounding box used for auto-fit calculation.
7887 // 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)7888 void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
7889 {
7890     ImGuiContext& g = *GImGui;
7891     ImGuiWindow* window = g.CurrentWindow;
7892     if (window->SkipItems)
7893         return;
7894 
7895     // We increase the height in this function to accommodate for baseline offset.
7896     // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
7897     // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
7898     const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
7899     const float line_height = ImMax(window->DC.CurrLineSize.y, size.y + offset_to_match_baseline_y);
7900 
7901     // Always align ourselves on pixel boundaries
7902     //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]
7903     window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
7904     window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y;
7905     window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);    // Next line
7906     window->DC.CursorPos.y = IM_FLOOR(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y);        // Next line
7907     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
7908     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
7909     //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
7910 
7911     window->DC.PrevLineSize.y = line_height;
7912     window->DC.CurrLineSize.y = 0.0f;
7913     window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
7914     window->DC.CurrLineTextBaseOffset = 0.0f;
7915 
7916     // Horizontal layout mode
7917     if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
7918         SameLine();
7919 }
7920 
ItemSize(const ImRect & bb,float text_baseline_y)7921 void ImGui::ItemSize(const ImRect& bb, float text_baseline_y)
7922 {
7923     ItemSize(bb.GetSize(), text_baseline_y);
7924 }
7925 
7926 // Declare item bounding box for clipping and interaction.
7927 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
7928 // 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)7929 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg)
7930 {
7931     ImGuiContext& g = *GImGui;
7932     ImGuiWindow* window = g.CurrentWindow;
7933 
7934     if (id != 0)
7935     {
7936         // Navigation processing runs prior to clipping early-out
7937         //  (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
7938         //  (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
7939         //      unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
7940         //      thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
7941         //      We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
7942         //      to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
7943         // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
7944         // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
7945         window->DC.NavLayerActiveMaskNext |= (1 << window->DC.NavLayerCurrent);
7946         if (g.NavId == id || g.NavAnyRequest)
7947             if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
7948                 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
7949                     NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
7950 
7951         // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd()
7952 #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
7953         if (id == g.DebugItemPickerBreakId)
7954         {
7955             IM_DEBUG_BREAK();
7956             g.DebugItemPickerBreakId = 0;
7957         }
7958 #endif
7959     }
7960 
7961     // Equivalent to calling SetLastItemData()
7962     window->DC.LastItemId = id;
7963     window->DC.LastItemRect = bb;
7964     window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None;
7965     g.NextItemData.Flags = ImGuiNextItemDataFlags_None;
7966 
7967 #ifdef IMGUI_ENABLE_TEST_ENGINE
7968     if (id != 0)
7969         IMGUI_TEST_ENGINE_ITEM_ADD(nav_bb_arg ? *nav_bb_arg : bb, id);
7970 #endif
7971 
7972     // Clipping test
7973     const bool is_clipped = IsClippedEx(bb, id, false);
7974     if (is_clipped)
7975         return false;
7976     //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
7977 
7978     // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
7979     if (IsMouseHoveringRect(bb.Min, bb.Max))
7980         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect;
7981     return true;
7982 }
7983 
7984 // Gets back to previous line and continue with horizontal layout
7985 //      offset_from_start_x == 0 : follow right after previous item
7986 //      offset_from_start_x != 0 : align to specified x position (relative to window/group left)
7987 //      spacing_w < 0            : use default spacing if pos_x == 0, no spacing if pos_x != 0
7988 //      spacing_w >= 0           : enforce spacing amount
SameLine(float offset_from_start_x,float spacing_w)7989 void ImGui::SameLine(float offset_from_start_x, float spacing_w)
7990 {
7991     ImGuiWindow* window = GetCurrentWindow();
7992     if (window->SkipItems)
7993         return;
7994 
7995     ImGuiContext& g = *GImGui;
7996     if (offset_from_start_x != 0.0f)
7997     {
7998         if (spacing_w < 0.0f) spacing_w = 0.0f;
7999         window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
8000         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
8001     }
8002     else
8003     {
8004         if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
8005         window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
8006         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
8007     }
8008     window->DC.CurrLineSize = window->DC.PrevLineSize;
8009     window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
8010 }
8011 
GetCursorScreenPos()8012 ImVec2 ImGui::GetCursorScreenPos()
8013 {
8014     ImGuiWindow* window = GetCurrentWindowRead();
8015     return window->DC.CursorPos;
8016 }
8017 
SetCursorScreenPos(const ImVec2 & pos)8018 void ImGui::SetCursorScreenPos(const ImVec2& pos)
8019 {
8020     ImGuiWindow* window = GetCurrentWindow();
8021     window->DC.CursorPos = pos;
8022     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
8023 }
8024 
8025 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
8026 // 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()8027 ImVec2 ImGui::GetCursorPos()
8028 {
8029     ImGuiWindow* window = GetCurrentWindowRead();
8030     return window->DC.CursorPos - window->Pos + window->Scroll;
8031 }
8032 
GetCursorPosX()8033 float ImGui::GetCursorPosX()
8034 {
8035     ImGuiWindow* window = GetCurrentWindowRead();
8036     return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
8037 }
8038 
GetCursorPosY()8039 float ImGui::GetCursorPosY()
8040 {
8041     ImGuiWindow* window = GetCurrentWindowRead();
8042     return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
8043 }
8044 
SetCursorPos(const ImVec2 & local_pos)8045 void ImGui::SetCursorPos(const ImVec2& local_pos)
8046 {
8047     ImGuiWindow* window = GetCurrentWindow();
8048     window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
8049     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
8050 }
8051 
SetCursorPosX(float x)8052 void ImGui::SetCursorPosX(float x)
8053 {
8054     ImGuiWindow* window = GetCurrentWindow();
8055     window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
8056     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
8057 }
8058 
SetCursorPosY(float y)8059 void ImGui::SetCursorPosY(float y)
8060 {
8061     ImGuiWindow* window = GetCurrentWindow();
8062     window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
8063     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
8064 }
8065 
GetCursorStartPos()8066 ImVec2 ImGui::GetCursorStartPos()
8067 {
8068     ImGuiWindow* window = GetCurrentWindowRead();
8069     return window->DC.CursorStartPos - window->Pos;
8070 }
8071 
Indent(float indent_w)8072 void ImGui::Indent(float indent_w)
8073 {
8074     ImGuiContext& g = *GImGui;
8075     ImGuiWindow* window = GetCurrentWindow();
8076     window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
8077     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
8078 }
8079 
Unindent(float indent_w)8080 void ImGui::Unindent(float indent_w)
8081 {
8082     ImGuiContext& g = *GImGui;
8083     ImGuiWindow* window = GetCurrentWindow();
8084     window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
8085     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
8086 }
8087 
8088 // Affect large frame+labels widgets only.
SetNextItemWidth(float item_width)8089 void ImGui::SetNextItemWidth(float item_width)
8090 {
8091     ImGuiContext& g = *GImGui;
8092     g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasWidth;
8093     g.NextItemData.Width = item_width;
8094 }
8095 
PushItemWidth(float item_width)8096 void ImGui::PushItemWidth(float item_width)
8097 {
8098     ImGuiContext& g = *GImGui;
8099     ImGuiWindow* window = g.CurrentWindow;
8100     window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
8101     window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
8102     g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
8103 }
8104 
PushMultiItemsWidths(int components,float w_full)8105 void ImGui::PushMultiItemsWidths(int components, float w_full)
8106 {
8107     ImGuiContext& g = *GImGui;
8108     ImGuiWindow* window = g.CurrentWindow;
8109     const ImGuiStyle& style = g.Style;
8110     const float w_item_one  = ImMax(1.0f, IM_FLOOR((w_full - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components));
8111     const float w_item_last = ImMax(1.0f, IM_FLOOR(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components - 1)));
8112     window->DC.ItemWidthStack.push_back(w_item_last);
8113     for (int i = 0; i < components - 1; i++)
8114         window->DC.ItemWidthStack.push_back(w_item_one);
8115     window->DC.ItemWidth = window->DC.ItemWidthStack.back();
8116     g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
8117 }
8118 
PopItemWidth()8119 void ImGui::PopItemWidth()
8120 {
8121     ImGuiWindow* window = GetCurrentWindow();
8122     window->DC.ItemWidthStack.pop_back();
8123     window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
8124 }
8125 
8126 // Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().
8127 // The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()
CalcItemWidth()8128 float ImGui::CalcItemWidth()
8129 {
8130     ImGuiContext& g = *GImGui;
8131     ImGuiWindow* window = g.CurrentWindow;
8132     float w;
8133     if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth)
8134         w = g.NextItemData.Width;
8135     else
8136         w = window->DC.ItemWidth;
8137     if (w < 0.0f)
8138     {
8139         float region_max_x = GetContentRegionMaxAbs().x;
8140         w = ImMax(1.0f, region_max_x - window->DC.CursorPos.x + w);
8141     }
8142     w = IM_FLOOR(w);
8143     return w;
8144 }
8145 
8146 // [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().
8147 // Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.
8148 // Note that only CalcItemWidth() is publicly exposed.
8149 // 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)8150 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)
8151 {
8152     ImGuiWindow* window = GImGui->CurrentWindow;
8153 
8154     ImVec2 region_max;
8155     if (size.x < 0.0f || size.y < 0.0f)
8156         region_max = GetContentRegionMaxAbs();
8157 
8158     if (size.x == 0.0f)
8159         size.x = default_w;
8160     else if (size.x < 0.0f)
8161         size.x = ImMax(4.0f, region_max.x - window->DC.CursorPos.x + size.x);
8162 
8163     if (size.y == 0.0f)
8164         size.y = default_h;
8165     else if (size.y < 0.0f)
8166         size.y = ImMax(4.0f, region_max.y - window->DC.CursorPos.y + size.y);
8167 
8168     return size;
8169 }
8170 
GetTextLineHeight()8171 float ImGui::GetTextLineHeight()
8172 {
8173     ImGuiContext& g = *GImGui;
8174     return g.FontSize;
8175 }
8176 
GetTextLineHeightWithSpacing()8177 float ImGui::GetTextLineHeightWithSpacing()
8178 {
8179     ImGuiContext& g = *GImGui;
8180     return g.FontSize + g.Style.ItemSpacing.y;
8181 }
8182 
GetFrameHeight()8183 float ImGui::GetFrameHeight()
8184 {
8185     ImGuiContext& g = *GImGui;
8186     return g.FontSize + g.Style.FramePadding.y * 2.0f;
8187 }
8188 
GetFrameHeightWithSpacing()8189 float ImGui::GetFrameHeightWithSpacing()
8190 {
8191     ImGuiContext& g = *GImGui;
8192     return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
8193 }
8194 
8195 // 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!
8196 
8197 // FIXME: This is in window space (not screen space!).
GetContentRegionMax()8198 ImVec2 ImGui::GetContentRegionMax()
8199 {
8200     ImGuiContext& g = *GImGui;
8201     ImGuiWindow* window = g.CurrentWindow;
8202     ImVec2 mx = window->ContentRegionRect.Max - window->Pos;
8203     if (window->DC.CurrentColumns || g.CurrentTable)
8204         mx.x = window->WorkRect.Max.x - window->Pos.x;
8205     return mx;
8206 }
8207 
8208 // [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features.
GetContentRegionMaxAbs()8209 ImVec2 ImGui::GetContentRegionMaxAbs()
8210 {
8211     ImGuiContext& g = *GImGui;
8212     ImGuiWindow* window = g.CurrentWindow;
8213     ImVec2 mx = window->ContentRegionRect.Max;
8214     if (window->DC.CurrentColumns || g.CurrentTable)
8215         mx.x = window->WorkRect.Max.x;
8216     return mx;
8217 }
8218 
GetContentRegionAvail()8219 ImVec2 ImGui::GetContentRegionAvail()
8220 {
8221     ImGuiWindow* window = GImGui->CurrentWindow;
8222     return GetContentRegionMaxAbs() - window->DC.CursorPos;
8223 }
8224 
8225 // In window space (not screen space!)
GetWindowContentRegionMin()8226 ImVec2 ImGui::GetWindowContentRegionMin()
8227 {
8228     ImGuiWindow* window = GImGui->CurrentWindow;
8229     return window->ContentRegionRect.Min - window->Pos;
8230 }
8231 
GetWindowContentRegionMax()8232 ImVec2 ImGui::GetWindowContentRegionMax()
8233 {
8234     ImGuiWindow* window = GImGui->CurrentWindow;
8235     return window->ContentRegionRect.Max - window->Pos;
8236 }
8237 
GetWindowContentRegionWidth()8238 float ImGui::GetWindowContentRegionWidth()
8239 {
8240     ImGuiWindow* window = GImGui->CurrentWindow;
8241     return window->ContentRegionRect.GetWidth();
8242 }
8243 
8244 // 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.)
8245 // Groups are currently a mishmash of functionalities which should perhaps be clarified and separated.
BeginGroup()8246 void ImGui::BeginGroup()
8247 {
8248     ImGuiContext& g = *GImGui;
8249     ImGuiWindow* window = g.CurrentWindow;
8250 
8251     g.GroupStack.resize(g.GroupStack.Size + 1);
8252     ImGuiGroupData& group_data = g.GroupStack.back();
8253     group_data.WindowID = window->ID;
8254     group_data.BackupCursorPos = window->DC.CursorPos;
8255     group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
8256     group_data.BackupIndent = window->DC.Indent;
8257     group_data.BackupGroupOffset = window->DC.GroupOffset;
8258     group_data.BackupCurrLineSize = window->DC.CurrLineSize;
8259     group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;
8260     group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
8261     group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
8262     group_data.EmitItem = true;
8263 
8264     window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
8265     window->DC.Indent = window->DC.GroupOffset;
8266     window->DC.CursorMaxPos = window->DC.CursorPos;
8267     window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
8268     if (g.LogEnabled)
8269         g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return
8270 }
8271 
EndGroup()8272 void ImGui::EndGroup()
8273 {
8274     ImGuiContext& g = *GImGui;
8275     ImGuiWindow* window = g.CurrentWindow;
8276     IM_ASSERT(g.GroupStack.Size > 0); // Mismatched BeginGroup()/EndGroup() calls
8277 
8278     ImGuiGroupData& group_data = g.GroupStack.back();
8279     IM_ASSERT(group_data.WindowID == window->ID); // EndGroup() in wrong window?
8280 
8281     ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos));
8282 
8283     window->DC.CursorPos = group_data.BackupCursorPos;
8284     window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
8285     window->DC.Indent = group_data.BackupIndent;
8286     window->DC.GroupOffset = group_data.BackupGroupOffset;
8287     window->DC.CurrLineSize = group_data.BackupCurrLineSize;
8288     window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;
8289     if (g.LogEnabled)
8290         g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return
8291 
8292     if (!group_data.EmitItem)
8293     {
8294         g.GroupStack.pop_back();
8295         return;
8296     }
8297 
8298     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.
8299     ItemSize(group_bb.GetSize());
8300     ItemAdd(group_bb, 0);
8301 
8302     // 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.
8303     // 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.
8304     // Also if you grep for LastItemId you'll notice it is only used in that context.
8305     // (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.)
8306     const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId;
8307     const bool group_contains_prev_active_id = (group_data.BackupActiveIdPreviousFrameIsAlive == false) && (g.ActiveIdPreviousFrameIsAlive == true);
8308     if (group_contains_curr_active_id)
8309         window->DC.LastItemId = g.ActiveId;
8310     else if (group_contains_prev_active_id)
8311         window->DC.LastItemId = g.ActiveIdPreviousFrame;
8312     window->DC.LastItemRect = group_bb;
8313 
8314     // Forward Edited flag
8315     if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame)
8316         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
8317 
8318     // Forward Deactivated flag
8319     window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDeactivated;
8320     if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame)
8321         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Deactivated;
8322 
8323     g.GroupStack.pop_back();
8324     //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255));   // [Debug]
8325 }
8326 
8327 
8328 //-----------------------------------------------------------------------------
8329 // [SECTION] SCROLLING
8330 //-----------------------------------------------------------------------------
8331 
8332 // Helper to snap on edges when aiming at an item very close to the edge,
8333 // So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling.
8334 // When we refactor the scrolling API this may be configurable with a flag?
8335 // 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)8336 static float CalcScrollEdgeSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio)
8337 {
8338     if (target <= snap_min + snap_threshold)
8339         return ImLerp(snap_min, target, center_ratio);
8340     if (target >= snap_max - snap_threshold)
8341         return ImLerp(target, snap_max, center_ratio);
8342     return target;
8343 }
8344 
CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow * window)8345 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
8346 {
8347     ImVec2 scroll = window->Scroll;
8348     if (window->ScrollTarget.x < FLT_MAX)
8349     {
8350         float center_x_ratio = window->ScrollTargetCenterRatio.x;
8351         float scroll_target_x = window->ScrollTarget.x;
8352         float snap_x_min = 0.0f;
8353         float snap_x_max = window->ScrollMax.x + window->Size.x;
8354         if (window->ScrollTargetEdgeSnapDist.x > 0.0f)
8355             scroll_target_x = CalcScrollEdgeSnap(scroll_target_x, snap_x_min, snap_x_max, window->ScrollTargetEdgeSnapDist.x, center_x_ratio);
8356         scroll.x = scroll_target_x - center_x_ratio * (window->SizeFull.x - window->ScrollbarSizes.x);
8357     }
8358     if (window->ScrollTarget.y < FLT_MAX)
8359     {
8360         float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
8361         float center_y_ratio = window->ScrollTargetCenterRatio.y;
8362         float scroll_target_y = window->ScrollTarget.y;
8363         float snap_y_min = 0.0f;
8364         float snap_y_max = window->ScrollMax.y + window->Size.y - decoration_up_height;
8365         if (window->ScrollTargetEdgeSnapDist.y > 0.0f)
8366             scroll_target_y = CalcScrollEdgeSnap(scroll_target_y, snap_y_min, snap_y_max, window->ScrollTargetEdgeSnapDist.y, center_y_ratio);
8367         scroll.y = scroll_target_y - center_y_ratio * (window->SizeFull.y - window->ScrollbarSizes.y - decoration_up_height);
8368     }
8369     scroll.x = IM_FLOOR(ImMax(scroll.x, 0.0f));
8370     scroll.y = IM_FLOOR(ImMax(scroll.y, 0.0f));
8371     if (!window->Collapsed && !window->SkipItems)
8372     {
8373         scroll.x = ImMin(scroll.x, window->ScrollMax.x);
8374         scroll.y = ImMin(scroll.y, window->ScrollMax.y);
8375     }
8376     return scroll;
8377 }
8378 
8379 // Scroll to keep newly navigated item fully into view
ScrollToBringRectIntoView(ImGuiWindow * window,const ImRect & item_rect)8380 ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect)
8381 {
8382     ImGuiContext& g = *GImGui;
8383     ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));
8384     //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
8385 
8386     ImVec2 delta_scroll;
8387     if (!window_rect.Contains(item_rect))
8388     {
8389         if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x)
8390             SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x - g.Style.ItemSpacing.x, 0.0f);
8391         else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x)
8392             SetScrollFromPosX(window, item_rect.Max.x - window->Pos.x + g.Style.ItemSpacing.x, 1.0f);
8393         if (item_rect.Min.y < window_rect.Min.y)
8394             SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y - g.Style.ItemSpacing.y, 0.0f);
8395         else if (item_rect.Max.y >= window_rect.Max.y)
8396             SetScrollFromPosY(window, item_rect.Max.y - window->Pos.y + g.Style.ItemSpacing.y, 1.0f);
8397 
8398         ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
8399         delta_scroll = next_scroll - window->Scroll;
8400     }
8401 
8402     // Also scroll parent window to keep us into view if necessary
8403     if (window->Flags & ImGuiWindowFlags_ChildWindow)
8404         delta_scroll += ScrollToBringRectIntoView(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll));
8405 
8406     return delta_scroll;
8407 }
8408 
GetScrollX()8409 float ImGui::GetScrollX()
8410 {
8411     ImGuiWindow* window = GImGui->CurrentWindow;
8412     return window->Scroll.x;
8413 }
8414 
GetScrollY()8415 float ImGui::GetScrollY()
8416 {
8417     ImGuiWindow* window = GImGui->CurrentWindow;
8418     return window->Scroll.y;
8419 }
8420 
GetScrollMaxX()8421 float ImGui::GetScrollMaxX()
8422 {
8423     ImGuiWindow* window = GImGui->CurrentWindow;
8424     return window->ScrollMax.x;
8425 }
8426 
GetScrollMaxY()8427 float ImGui::GetScrollMaxY()
8428 {
8429     ImGuiWindow* window = GImGui->CurrentWindow;
8430     return window->ScrollMax.y;
8431 }
8432 
SetScrollX(ImGuiWindow * window,float scroll_x)8433 void ImGui::SetScrollX(ImGuiWindow* window, float scroll_x)
8434 {
8435     window->ScrollTarget.x = scroll_x;
8436     window->ScrollTargetCenterRatio.x = 0.0f;
8437     window->ScrollTargetEdgeSnapDist.x = 0.0f;
8438 }
8439 
SetScrollY(ImGuiWindow * window,float scroll_y)8440 void ImGui::SetScrollY(ImGuiWindow* window, float scroll_y)
8441 {
8442     window->ScrollTarget.y = scroll_y;
8443     window->ScrollTargetCenterRatio.y = 0.0f;
8444     window->ScrollTargetEdgeSnapDist.y = 0.0f;
8445 }
8446 
SetScrollX(float scroll_x)8447 void ImGui::SetScrollX(float scroll_x)
8448 {
8449     ImGuiContext& g = *GImGui;
8450     SetScrollX(g.CurrentWindow, scroll_x);
8451 }
8452 
SetScrollY(float scroll_y)8453 void ImGui::SetScrollY(float scroll_y)
8454 {
8455     ImGuiContext& g = *GImGui;
8456     SetScrollY(g.CurrentWindow, scroll_y);
8457 }
8458 
8459 // Note that a local position will vary depending on initial scroll value,
8460 // This is a little bit confusing so bear with us:
8461 //  - local_pos = (absolution_pos - window->Pos)
8462 //  - So local_x/local_y are 0.0f for a position at the upper-left corner of a window,
8463 //    and generally local_x/local_y are >(padding+decoration) && <(size-padding-decoration) when in the visible area.
8464 //  - They mostly exists because of legacy API.
8465 // Following the rules above, when trying to work with scrolling code, consider that:
8466 //  - SetScrollFromPosY(0.0f) == SetScrollY(0.0f + scroll.y) == has no effect!
8467 //  - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0f) == reset scroll. Of course writing SetScrollY(0.0f) directly then makes more sense
8468 // 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)8469 void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
8470 {
8471     IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);
8472     window->ScrollTarget.x = IM_FLOOR(local_x + window->Scroll.x); // Convert local position to scroll offset
8473     window->ScrollTargetCenterRatio.x = center_x_ratio;
8474     window->ScrollTargetEdgeSnapDist.x = 0.0f;
8475 }
8476 
SetScrollFromPosY(ImGuiWindow * window,float local_y,float center_y_ratio)8477 void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
8478 {
8479     IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
8480     local_y -= window->TitleBarHeight() + window->MenuBarHeight(); // FIXME: Would be nice to have a more standardized access to our scrollable/client rect
8481     window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y); // Convert local position to scroll offset
8482     window->ScrollTargetCenterRatio.y = center_y_ratio;
8483     window->ScrollTargetEdgeSnapDist.y = 0.0f;
8484 }
8485 
SetScrollFromPosX(float local_x,float center_x_ratio)8486 void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)
8487 {
8488     ImGuiContext& g = *GImGui;
8489     SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio);
8490 }
8491 
SetScrollFromPosY(float local_y,float center_y_ratio)8492 void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
8493 {
8494     ImGuiContext& g = *GImGui;
8495     SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio);
8496 }
8497 
8498 // 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)8499 void ImGui::SetScrollHereX(float center_x_ratio)
8500 {
8501     ImGuiContext& g = *GImGui;
8502     ImGuiWindow* window = g.CurrentWindow;
8503     float spacing_x = g.Style.ItemSpacing.x;
8504     float target_pos_x = ImLerp(window->DC.LastItemRect.Min.x - spacing_x, window->DC.LastItemRect.Max.x + spacing_x, center_x_ratio);
8505     SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos
8506 
8507     // Tweak: snap on edges when aiming at an item very close to the edge
8508     window->ScrollTargetEdgeSnapDist.x = ImMax(0.0f, window->WindowPadding.x - spacing_x);
8509 }
8510 
8511 // 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)8512 void ImGui::SetScrollHereY(float center_y_ratio)
8513 {
8514     ImGuiContext& g = *GImGui;
8515     ImGuiWindow* window = g.CurrentWindow;
8516     float spacing_y = g.Style.ItemSpacing.y;
8517     float target_pos_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio);
8518     SetScrollFromPosY(window, target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos
8519 
8520     // Tweak: snap on edges when aiming at an item very close to the edge
8521     window->ScrollTargetEdgeSnapDist.y = ImMax(0.0f, window->WindowPadding.y - spacing_y);
8522 }
8523 
8524 //-----------------------------------------------------------------------------
8525 // [SECTION] TOOLTIPS
8526 //-----------------------------------------------------------------------------
8527 
BeginTooltip()8528 void ImGui::BeginTooltip()
8529 {
8530     BeginTooltipEx(ImGuiWindowFlags_None, ImGuiTooltipFlags_None);
8531 }
8532 
BeginTooltipEx(ImGuiWindowFlags extra_flags,ImGuiTooltipFlags tooltip_flags)8533 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags)
8534 {
8535     ImGuiContext& g = *GImGui;
8536 
8537     if (g.DragDropWithinSource || g.DragDropWithinTarget)
8538     {
8539         // 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)
8540         // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
8541         // 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.
8542         //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
8543         ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale);
8544         SetNextWindowPos(tooltip_pos);
8545         SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
8546         //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
8547         tooltip_flags |= ImGuiTooltipFlags_OverridePreviousTooltip;
8548     }
8549 
8550     char window_name[16];
8551     ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
8552     if (tooltip_flags & ImGuiTooltipFlags_OverridePreviousTooltip)
8553         if (ImGuiWindow* window = FindWindowByName(window_name))
8554             if (window->Active)
8555             {
8556                 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
8557                 window->Hidden = true;
8558                 window->HiddenFramesCanSkipItems = 1; // FIXME: This may not be necessary?
8559                 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
8560             }
8561     ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking;
8562     Begin(window_name, NULL, flags | extra_flags);
8563 }
8564 
EndTooltip()8565 void ImGui::EndTooltip()
8566 {
8567     IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip);   // Mismatched BeginTooltip()/EndTooltip() calls
8568     End();
8569 }
8570 
SetTooltipV(const char * fmt,va_list args)8571 void ImGui::SetTooltipV(const char* fmt, va_list args)
8572 {
8573     BeginTooltipEx(0, ImGuiTooltipFlags_OverridePreviousTooltip);
8574     TextV(fmt, args);
8575     EndTooltip();
8576 }
8577 
SetTooltip(const char * fmt,...)8578 void ImGui::SetTooltip(const char* fmt, ...)
8579 {
8580     va_list args;
8581     va_start(args, fmt);
8582     SetTooltipV(fmt, args);
8583     va_end(args);
8584 }
8585 
8586 //-----------------------------------------------------------------------------
8587 // [SECTION] POPUPS
8588 //-----------------------------------------------------------------------------
8589 
8590 // Supported flags: ImGuiPopupFlags_AnyPopupId, ImGuiPopupFlags_AnyPopupLevel
IsPopupOpen(ImGuiID id,ImGuiPopupFlags popup_flags)8591 bool ImGui::IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags)
8592 {
8593     ImGuiContext& g = *GImGui;
8594     if (popup_flags & ImGuiPopupFlags_AnyPopupId)
8595     {
8596         // Return true if any popup is open at the current BeginPopup() level of the popup stack
8597         // This may be used to e.g. test for another popups already opened to handle popups priorities at the same level.
8598         IM_ASSERT(id == 0);
8599         if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
8600             return g.OpenPopupStack.Size > 0;
8601         else
8602             return g.OpenPopupStack.Size > g.BeginPopupStack.Size;
8603     }
8604     else
8605     {
8606         if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
8607         {
8608             // Return true if the popup is open anywhere in the popup stack
8609             for (int n = 0; n < g.OpenPopupStack.Size; n++)
8610                 if (g.OpenPopupStack[n].PopupId == id)
8611                     return true;
8612             return false;
8613         }
8614         else
8615         {
8616             // Return true if the popup is open at the current BeginPopup() level of the popup stack (this is the most-common query)
8617             return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
8618         }
8619     }
8620 }
8621 
IsPopupOpen(const char * str_id,ImGuiPopupFlags popup_flags)8622 bool ImGui::IsPopupOpen(const char* str_id, ImGuiPopupFlags popup_flags)
8623 {
8624     ImGuiContext& g = *GImGui;
8625     ImGuiID id = (popup_flags & ImGuiPopupFlags_AnyPopupId) ? 0 : g.CurrentWindow->GetID(str_id);
8626     if ((popup_flags & ImGuiPopupFlags_AnyPopupLevel) && id != 0)
8627         IM_ASSERT(0 && "Cannot use IsPopupOpen() with a string id and ImGuiPopupFlags_AnyPopupLevel."); // But non-string version is legal and used internally
8628     return IsPopupOpen(id, popup_flags);
8629 }
8630 
GetTopMostPopupModal()8631 ImGuiWindow* ImGui::GetTopMostPopupModal()
8632 {
8633     ImGuiContext& g = *GImGui;
8634     for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
8635         if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
8636             if (popup->Flags & ImGuiWindowFlags_Modal)
8637                 return popup;
8638     return NULL;
8639 }
8640 
OpenPopup(const char * str_id,ImGuiPopupFlags popup_flags)8641 void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags)
8642 {
8643     ImGuiContext& g = *GImGui;
8644     OpenPopupEx(g.CurrentWindow->GetID(str_id), popup_flags);
8645 }
8646 
8647 // Mark popup as open (toggle toward open state).
8648 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
8649 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
8650 // 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)8651 void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags)
8652 {
8653     ImGuiContext& g = *GImGui;
8654     ImGuiWindow* parent_window = g.CurrentWindow;
8655     const int current_stack_size = g.BeginPopupStack.Size;
8656 
8657     if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup)
8658         if (IsPopupOpen(0u, ImGuiPopupFlags_AnyPopupId))
8659             return;
8660 
8661     ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
8662     popup_ref.PopupId = id;
8663     popup_ref.Window = NULL;
8664     popup_ref.SourceWindow = g.NavWindow;
8665     popup_ref.OpenFrameCount = g.FrameCount;
8666     popup_ref.OpenParentId = parent_window->IDStack.back();
8667     popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
8668     popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
8669 
8670     IMGUI_DEBUG_LOG_POPUP("OpenPopupEx(0x%08X)\n", id);
8671     if (g.OpenPopupStack.Size < current_stack_size + 1)
8672     {
8673         g.OpenPopupStack.push_back(popup_ref);
8674     }
8675     else
8676     {
8677         // 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
8678         // 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
8679         // 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.
8680         if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
8681         {
8682             g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
8683         }
8684         else
8685         {
8686             // Close child popups if any, then flag popup for open/reopen
8687             ClosePopupToLevel(current_stack_size, false);
8688             g.OpenPopupStack.push_back(popup_ref);
8689         }
8690 
8691         // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
8692         // This is equivalent to what ClosePopupToLevel() does.
8693         //if (g.OpenPopupStack[current_stack_size].PopupId == id)
8694         //    FocusWindow(parent_window);
8695     }
8696 }
8697 
8698 // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
8699 // This function closes any popups that are over 'ref_window'.
ClosePopupsOverWindow(ImGuiWindow * ref_window,bool restore_focus_to_window_under_popup)8700 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
8701 {
8702     ImGuiContext& g = *GImGui;
8703     if (g.OpenPopupStack.Size == 0)
8704         return;
8705 
8706     // Don't close our own child popup windows.
8707     int popup_count_to_keep = 0;
8708     if (ref_window)
8709     {
8710         // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
8711         for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
8712         {
8713             ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];
8714             if (!popup.Window)
8715                 continue;
8716             IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
8717             if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
8718                 continue;
8719 
8720             // Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow)
8721             // - With this stack of window, clicking/focusing Popup1 will close Popup2 and Popup3:
8722             //     Window -> Popup1 -> Popup2 -> Popup3
8723             // - Each popups may contain child windows, which is why we compare ->RootWindow!
8724             //     Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child
8725             bool ref_window_is_descendent_of_popup = false;
8726             for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++)
8727                 if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window)
8728                     if (popup_window->RootWindow == ref_window->RootWindow)
8729                     {
8730                         ref_window_is_descendent_of_popup = true;
8731                         break;
8732                     }
8733             if (!ref_window_is_descendent_of_popup)
8734                 break;
8735         }
8736     }
8737     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
8738     {
8739         IMGUI_DEBUG_LOG_POPUP("ClosePopupsOverWindow(\"%s\") -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep);
8740         ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup);
8741     }
8742 }
8743 
ClosePopupToLevel(int remaining,bool restore_focus_to_window_under_popup)8744 void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
8745 {
8746     ImGuiContext& g = *GImGui;
8747     IMGUI_DEBUG_LOG_POPUP("ClosePopupToLevel(%d), restore_focus_to_window_under_popup=%d\n", remaining, restore_focus_to_window_under_popup);
8748     IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
8749 
8750     // Trim open popup stack
8751     ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow;
8752     ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window;
8753     g.OpenPopupStack.resize(remaining);
8754 
8755     if (restore_focus_to_window_under_popup)
8756     {
8757         if (focus_window && !focus_window->WasActive && popup_window)
8758         {
8759             // Fallback
8760             FocusTopMostWindowUnderOne(popup_window, NULL);
8761         }
8762         else
8763         {
8764             if (g.NavLayer == ImGuiNavLayer_Main && focus_window)
8765                 focus_window = NavRestoreLastChildNavWindow(focus_window);
8766             FocusWindow(focus_window);
8767         }
8768     }
8769 }
8770 
8771 // Close the popup we have begin-ed into.
CloseCurrentPopup()8772 void ImGui::CloseCurrentPopup()
8773 {
8774     ImGuiContext& g = *GImGui;
8775     int popup_idx = g.BeginPopupStack.Size - 1;
8776     if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
8777         return;
8778 
8779     // Closing a menu closes its top-most parent popup (unless a modal)
8780     while (popup_idx > 0)
8781     {
8782         ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;
8783         ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
8784         bool close_parent = false;
8785         if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
8786             if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal))
8787                 close_parent = true;
8788         if (!close_parent)
8789             break;
8790         popup_idx--;
8791     }
8792     IMGUI_DEBUG_LOG_POPUP("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx);
8793     ClosePopupToLevel(popup_idx, true);
8794 
8795     // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
8796     // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
8797     // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
8798     if (ImGuiWindow* window = g.NavWindow)
8799         window->DC.NavHideHighlightOneFrame = true;
8800 }
8801 
8802 // Attention! BeginPopup() adds default flags which BeginPopupEx()!
BeginPopupEx(ImGuiID id,ImGuiWindowFlags flags)8803 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags)
8804 {
8805     ImGuiContext& g = *GImGui;
8806     if (!IsPopupOpen(id, ImGuiPopupFlags_None))
8807     {
8808         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
8809         return false;
8810     }
8811 
8812     char name[20];
8813     if (flags & ImGuiWindowFlags_ChildMenu)
8814         ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth
8815     else
8816         ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
8817 
8818     flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoDocking;
8819     bool is_open = Begin(name, NULL, flags);
8820     if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
8821         EndPopup();
8822 
8823     return is_open;
8824 }
8825 
BeginPopup(const char * str_id,ImGuiWindowFlags flags)8826 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
8827 {
8828     ImGuiContext& g = *GImGui;
8829     if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
8830     {
8831         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
8832         return false;
8833     }
8834     flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
8835     return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags);
8836 }
8837 
8838 // If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
8839 // 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)8840 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
8841 {
8842     ImGuiContext& g = *GImGui;
8843     ImGuiWindow* window = g.CurrentWindow;
8844     const ImGuiID id = window->GetID(name);
8845     if (!IsPopupOpen(id, ImGuiPopupFlags_None))
8846     {
8847         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
8848         return false;
8849     }
8850 
8851     // Center modal windows by default for increased visibility
8852     // (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves)
8853     // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
8854     if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
8855     {
8856         ImGuiViewportP* viewport = window->WasActive ? window->Viewport : (ImGuiViewportP*)GetMainViewport(); // FIXME-VIEWPORT: What may be our reference viewport?
8857         SetNextWindowPos(viewport->GetMainRect().GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
8858     }
8859 
8860     flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking;
8861     const bool is_open = Begin(name, p_open, flags);
8862     if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
8863     {
8864         EndPopup();
8865         if (is_open)
8866             ClosePopupToLevel(g.BeginPopupStack.Size, true);
8867         return false;
8868     }
8869     return is_open;
8870 }
8871 
EndPopup()8872 void ImGui::EndPopup()
8873 {
8874     ImGuiContext& g = *GImGui;
8875     ImGuiWindow* window = g.CurrentWindow;
8876     IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup);  // Mismatched BeginPopup()/EndPopup() calls
8877     IM_ASSERT(g.BeginPopupStack.Size > 0);
8878 
8879     // Make all menus and popups wrap around for now, may need to expose that policy.
8880     if (g.NavWindow == window)
8881         NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
8882 
8883     // Child-popups don't need to be laid out
8884     IM_ASSERT(g.WithinEndChild == false);
8885     if (window->Flags & ImGuiWindowFlags_ChildWindow)
8886         g.WithinEndChild = true;
8887     End();
8888     g.WithinEndChild = false;
8889 }
8890 
8891 // Helper to open a popup if mouse button is released over the item
8892 // - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup()
OpenPopupOnItemClick(const char * str_id,ImGuiPopupFlags popup_flags)8893 void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags)
8894 {
8895     ImGuiWindow* window = GImGui->CurrentWindow;
8896     int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8897     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
8898     {
8899         ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
8900         IM_ASSERT(id != 0);                                                  // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
8901         OpenPopupEx(id, popup_flags);
8902     }
8903 }
8904 
8905 // This is a helper to handle the simplest case of associating one named popup to one given widget.
8906 // - You can pass a NULL str_id to use the identifier of the last item.
8907 // - You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
8908 // - This is essentially the same as calling OpenPopupOnItemClick() + BeginPopup() but written to avoid
8909 //   computing the ID twice because BeginPopupContextXXX functions may be called very frequently.
BeginPopupContextItem(const char * str_id,ImGuiPopupFlags popup_flags)8910 bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags)
8911 {
8912     ImGuiWindow* window = GImGui->CurrentWindow;
8913     if (window->SkipItems)
8914         return false;
8915     ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
8916     IM_ASSERT(id != 0);                                                  // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
8917     int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8918     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
8919         OpenPopupEx(id, popup_flags);
8920     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
8921 }
8922 
BeginPopupContextWindow(const char * str_id,ImGuiPopupFlags popup_flags)8923 bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags)
8924 {
8925     ImGuiWindow* window = GImGui->CurrentWindow;
8926     if (!str_id)
8927         str_id = "window_context";
8928     ImGuiID id = window->GetID(str_id);
8929     int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8930     if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
8931         if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered())
8932             OpenPopupEx(id, popup_flags);
8933     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
8934 }
8935 
BeginPopupContextVoid(const char * str_id,ImGuiPopupFlags popup_flags)8936 bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags)
8937 {
8938     ImGuiWindow* window = GImGui->CurrentWindow;
8939     if (!str_id)
8940         str_id = "void_context";
8941     ImGuiID id = window->GetID(str_id);
8942     int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8943     if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
8944         if (GetTopMostPopupModal() == NULL)
8945             OpenPopupEx(id, popup_flags);
8946     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
8947 }
8948 
8949 // 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.)
8950 // 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.
8951 // (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor
8952 //  information are available, it may represent the entire platform monitor from the frame of reference of the current viewport.
8953 //  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)8954 ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
8955 {
8956     ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
8957     //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
8958     //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
8959 
8960     // Combo Box policy (we want a connecting edge)
8961     if (policy == ImGuiPopupPositionPolicy_ComboBox)
8962     {
8963         const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
8964         for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
8965         {
8966             const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
8967             if (n != -1 && dir == *last_dir) // Already tried this direction?
8968                 continue;
8969             ImVec2 pos;
8970             if (dir == ImGuiDir_Down)  pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y);          // Below, Toward Right (default)
8971             if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
8972             if (dir == ImGuiDir_Left)  pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
8973             if (dir == ImGuiDir_Up)    pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
8974             if (!r_outer.Contains(ImRect(pos, pos + size)))
8975                 continue;
8976             *last_dir = dir;
8977             return pos;
8978         }
8979     }
8980 
8981     // Tooltip and Default popup policy
8982     // (Always first try the direction we used on the last frame, if any)
8983     if (policy == ImGuiPopupPositionPolicy_Tooltip || policy == ImGuiPopupPositionPolicy_Default)
8984     {
8985         const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
8986         for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
8987         {
8988             const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
8989             if (n != -1 && dir == *last_dir) // Already tried this direction?
8990                 continue;
8991 
8992             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);
8993             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);
8994 
8995             // 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)
8996             if (avail_w < size.x && (dir == ImGuiDir_Left || dir == ImGuiDir_Right))
8997                 continue;
8998             if (avail_h < size.y && (dir == ImGuiDir_Up || dir == ImGuiDir_Down))
8999                 continue;
9000 
9001             ImVec2 pos;
9002             pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
9003             pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
9004 
9005             // Clamp top-left corner of popup
9006             pos.x = ImMax(pos.x, r_outer.Min.x);
9007             pos.y = ImMax(pos.y, r_outer.Min.y);
9008 
9009             *last_dir = dir;
9010             return pos;
9011         }
9012     }
9013 
9014     // Fallback when not enough room:
9015     *last_dir = ImGuiDir_None;
9016 
9017     // For tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
9018     if (policy == ImGuiPopupPositionPolicy_Tooltip)
9019         return ref_pos + ImVec2(2, 2);
9020 
9021     // Otherwise try to keep within display
9022     ImVec2 pos = ref_pos;
9023     pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
9024     pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
9025     return pos;
9026 }
9027 
9028 // Note that this is used for popups, which can overlap the non work-area of individual viewports.
GetWindowAllowedExtentRect(ImGuiWindow * window)9029 ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window)
9030 {
9031     ImGuiContext& g = *GImGui;
9032     ImRect r_screen;
9033     if (window->ViewportAllowPlatformMonitorExtend >= 0)
9034     {
9035         // Extent with be in the frame of reference of the given viewport (so Min is likely to be negative here)
9036         const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->ViewportAllowPlatformMonitorExtend];
9037         r_screen.Min = monitor.WorkPos;
9038         r_screen.Max = monitor.WorkPos + monitor.WorkSize;
9039     }
9040     else
9041     {
9042         // Use the full viewport area (not work area) for popups
9043         r_screen.Min = window->Viewport->Pos;
9044         r_screen.Max = window->Viewport->Pos + window->Viewport->Size;
9045     }
9046     ImVec2 padding = g.Style.DisplaySafeAreaPadding;
9047     r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
9048     return r_screen;
9049 }
9050 
FindBestWindowPosForPopup(ImGuiWindow * window)9051 ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
9052 {
9053     ImGuiContext& g = *GImGui;
9054     if (window->Flags & ImGuiWindowFlags_ChildMenu)
9055     {
9056         // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
9057         // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
9058         ImGuiWindow* parent_window = window->ParentWindow;
9059         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).
9060         ImRect r_outer = GetWindowAllowedExtentRect(window);
9061         ImRect r_avoid;
9062         if (parent_window->DC.MenuBarAppending)
9063             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
9064         else
9065             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);
9066         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default);
9067     }
9068     if (window->Flags & ImGuiWindowFlags_Popup)
9069     {
9070         ImRect r_outer = GetWindowAllowedExtentRect(window);
9071         ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
9072         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default);
9073     }
9074     if (window->Flags & ImGuiWindowFlags_Tooltip)
9075     {
9076         // Position tooltip (always follows mouse)
9077         float sc = g.Style.MouseCursorScale;
9078         ImVec2 ref_pos = NavCalcPreferredRefPos();
9079         ImRect r_outer = GetWindowAllowedExtentRect(window);
9080         ImRect r_avoid;
9081         if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
9082             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
9083         else
9084             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.
9085         return FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip);
9086     }
9087     IM_ASSERT(0);
9088     return window->Pos;
9089 }
9090 
9091 //-----------------------------------------------------------------------------
9092 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
9093 //-----------------------------------------------------------------------------
9094 
9095 // FIXME-NAV: The existence of SetNavID vs SetNavIDWithRectRel vs SetFocusID is incredibly messy and confusing,
9096 // and needs some explanation or serious refactoring.
SetNavID(ImGuiID id,int nav_layer,ImGuiID focus_scope_id)9097 void ImGui::SetNavID(ImGuiID id, int nav_layer, ImGuiID focus_scope_id)
9098 {
9099     ImGuiContext& g = *GImGui;
9100     IM_ASSERT(g.NavWindow);
9101     IM_ASSERT(nav_layer == 0 || nav_layer == 1);
9102     g.NavId = id;
9103     g.NavFocusScopeId = focus_scope_id;
9104     g.NavWindow->NavLastIds[nav_layer] = id;
9105 }
9106 
SetNavIDWithRectRel(ImGuiID id,int nav_layer,ImGuiID focus_scope_id,const ImRect & rect_rel)9107 void ImGui::SetNavIDWithRectRel(ImGuiID id, int nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel)
9108 {
9109     ImGuiContext& g = *GImGui;
9110     SetNavID(id, nav_layer, focus_scope_id);
9111     g.NavWindow->NavRectRel[nav_layer] = rect_rel;
9112     g.NavMousePosDirty = true;
9113     g.NavDisableHighlight = false;
9114     g.NavDisableMouseHover = true;
9115 }
9116 
SetFocusID(ImGuiID id,ImGuiWindow * window)9117 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
9118 {
9119     ImGuiContext& g = *GImGui;
9120     IM_ASSERT(id != 0);
9121 
9122     // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and window->DC.NavFocusScopeIdCurrent are valid.
9123     // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text)
9124     const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
9125     if (g.NavWindow != window)
9126         g.NavInitRequest = false;
9127     g.NavWindow = window;
9128     g.NavId = id;
9129     g.NavLayer = nav_layer;
9130     g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
9131     window->NavLastIds[nav_layer] = id;
9132     if (window->DC.LastItemId == id)
9133         window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos);
9134 
9135     if (g.ActiveIdSource == ImGuiInputSource_Nav)
9136         g.NavDisableMouseHover = true;
9137     else
9138         g.NavDisableHighlight = true;
9139 }
9140 
ImGetDirQuadrantFromDelta(float dx,float dy)9141 ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
9142 {
9143     if (ImFabs(dx) > ImFabs(dy))
9144         return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
9145     return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
9146 }
9147 
NavScoreItemDistInterval(float a0,float a1,float b0,float b1)9148 static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
9149 {
9150     if (a1 < b0)
9151         return a1 - b0;
9152     if (b1 < a0)
9153         return a0 - b1;
9154     return 0.0f;
9155 }
9156 
NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir,ImRect & r,const ImRect & clip_rect)9157 static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect)
9158 {
9159     if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
9160     {
9161         r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y);
9162         r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y);
9163     }
9164     else
9165     {
9166         r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x);
9167         r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x);
9168     }
9169 }
9170 
9171 // Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057
NavScoreItem(ImGuiNavMoveResult * result,ImRect cand)9172 static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
9173 {
9174     ImGuiContext& g = *GImGui;
9175     ImGuiWindow* window = g.CurrentWindow;
9176     if (g.NavLayer != window->DC.NavLayerCurrent)
9177         return false;
9178 
9179     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)
9180     g.NavScoringCount++;
9181 
9182     // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
9183     if (window->ParentWindow == g.NavWindow)
9184     {
9185         IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened);
9186         if (!window->ClipRect.Overlaps(cand))
9187             return false;
9188         cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
9189     }
9190 
9191     // 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)
9192     // For example, this ensure that items in one column are not reached when moving vertically from items in another column.
9193     NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect);
9194 
9195     // Compute distance between boxes
9196     // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
9197     float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
9198     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
9199     if (dby != 0.0f && dbx != 0.0f)
9200         dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
9201     float dist_box = ImFabs(dbx) + ImFabs(dby);
9202 
9203     // 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)
9204     float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
9205     float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
9206     float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
9207 
9208     // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
9209     ImGuiDir quadrant;
9210     float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
9211     if (dbx != 0.0f || dby != 0.0f)
9212     {
9213         // For non-overlapping boxes, use distance between boxes
9214         dax = dbx;
9215         day = dby;
9216         dist_axial = dist_box;
9217         quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
9218     }
9219     else if (dcx != 0.0f || dcy != 0.0f)
9220     {
9221         // For overlapping boxes with different centers, use distance between centers
9222         dax = dcx;
9223         day = dcy;
9224         dist_axial = dist_center;
9225         quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
9226     }
9227     else
9228     {
9229         // 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)
9230         quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
9231     }
9232 
9233 #if IMGUI_DEBUG_NAV_SCORING
9234     char buf[128];
9235     if (IsMouseHoveringRect(cand.Min, cand.Max))
9236     {
9237         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]);
9238         ImDrawList* draw_list = GetForegroundDrawList(window);
9239         draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
9240         draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
9241         draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40,0,0,150));
9242         draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
9243     }
9244     else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
9245     {
9246         if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; }
9247         if (quadrant == g.NavMoveDir)
9248         {
9249             ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
9250             ImDrawList* draw_list = GetForegroundDrawList(window);
9251             draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
9252             draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
9253         }
9254     }
9255 #endif
9256 
9257     // Is it in the quadrant we're interesting in moving to?
9258     bool new_best = false;
9259     if (quadrant == g.NavMoveDir)
9260     {
9261         // Does it beat the current best candidate?
9262         if (dist_box < result->DistBox)
9263         {
9264             result->DistBox = dist_box;
9265             result->DistCenter = dist_center;
9266             return true;
9267         }
9268         if (dist_box == result->DistBox)
9269         {
9270             // Try using distance between center points to break ties
9271             if (dist_center < result->DistCenter)
9272             {
9273                 result->DistCenter = dist_center;
9274                 new_best = true;
9275             }
9276             else if (dist_center == result->DistCenter)
9277             {
9278                 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
9279                 // (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),
9280                 // 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.
9281                 if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
9282                     new_best = true;
9283             }
9284         }
9285     }
9286 
9287     // 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
9288     // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
9289     // 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.
9290     // 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.
9291     // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
9292     if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial)  // Check axial match
9293         if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
9294             if ((g.NavMoveDir == ImGuiDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiDir_Down && day > 0.0f))
9295             {
9296                 result->DistAxial = dist_axial;
9297                 new_best = true;
9298             }
9299 
9300     return new_best;
9301 }
9302 
NavApplyItemToResult(ImGuiNavMoveResult * result,ImGuiWindow * window,ImGuiID id,const ImRect & nav_bb_rel)9303 static void ImGui::NavApplyItemToResult(ImGuiNavMoveResult* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel)
9304 {
9305     result->Window = window;
9306     result->ID = id;
9307     result->FocusScopeId = window->DC.NavFocusScopeIdCurrent;
9308     result->RectRel = nav_bb_rel;
9309 }
9310 
9311 // 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)9312 static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
9313 {
9314     ImGuiContext& g = *GImGui;
9315     //if (!g.IO.NavActive)  // [2017/10/06] Removed this possibly redundant test but I am not sure of all the side-effects yet. Some of the feature here will need to work regardless of using a _NoNavInputs flag.
9316     //    return;
9317 
9318     const ImGuiItemFlags item_flags = window->DC.ItemFlags;
9319     const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
9320 
9321     // Process Init Request
9322     if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
9323     {
9324         // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
9325         if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0)
9326         {
9327             g.NavInitResultId = id;
9328             g.NavInitResultRectRel = nav_bb_rel;
9329         }
9330         if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus))
9331         {
9332             g.NavInitRequest = false; // Found a match, clear request
9333             NavUpdateAnyRequestFlag();
9334         }
9335     }
9336 
9337     // Process Move Request (scoring for navigation)
9338     // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
9339     if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav)))
9340     {
9341         ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
9342 #if IMGUI_DEBUG_NAV_SCORING
9343         // [DEBUG] Score all items in NavWindow at all times
9344         if (!g.NavMoveRequest)
9345             g.NavMoveDir = g.NavMoveDirLast;
9346         bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest;
9347 #else
9348         bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb);
9349 #endif
9350         if (new_best)
9351             NavApplyItemToResult(result, window, id, nav_bb_rel);
9352 
9353         // Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
9354         const float VISIBLE_RATIO = 0.70f;
9355         if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
9356             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)
9357                 if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb))
9358                     NavApplyItemToResult(&g.NavMoveResultLocalVisibleSet, window, id, nav_bb_rel);
9359     }
9360 
9361     // Update window-relative bounding box of navigated item
9362     if (g.NavId == id)
9363     {
9364         g.NavWindow = window;                                           // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
9365         g.NavLayer = window->DC.NavLayerCurrent;
9366         g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
9367         g.NavIdIsAlive = true;
9368         g.NavIdTabCounter = window->DC.FocusCounterTabStop;
9369         window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel;    // Store item bounding box (relative to window position)
9370     }
9371 }
9372 
NavMoveRequestButNoResultYet()9373 bool ImGui::NavMoveRequestButNoResultYet()
9374 {
9375     ImGuiContext& g = *GImGui;
9376     return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
9377 }
9378 
NavMoveRequestCancel()9379 void ImGui::NavMoveRequestCancel()
9380 {
9381     ImGuiContext& g = *GImGui;
9382     g.NavMoveRequest = false;
9383     NavUpdateAnyRequestFlag();
9384 }
9385 
NavMoveRequestForward(ImGuiDir move_dir,ImGuiDir clip_dir,const ImRect & bb_rel,ImGuiNavMoveFlags move_flags)9386 void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags)
9387 {
9388     ImGuiContext& g = *GImGui;
9389     IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None);
9390     NavMoveRequestCancel();
9391     g.NavMoveDir = move_dir;
9392     g.NavMoveClipDir = clip_dir;
9393     g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
9394     g.NavMoveRequestFlags = move_flags;
9395     g.NavWindow->NavRectRel[g.NavLayer] = bb_rel;
9396 }
9397 
NavMoveRequestTryWrapping(ImGuiWindow * window,ImGuiNavMoveFlags move_flags)9398 void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
9399 {
9400     ImGuiContext& g = *GImGui;
9401 
9402     // Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
9403     // popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.
9404     g.NavWrapRequestWindow = window;
9405     g.NavWrapRequestFlags = move_flags;
9406 }
9407 
9408 // FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
9409 // This way we could find the last focused window among our children. It would be much less confusing this way?
NavSaveLastChildNavWindowIntoParent(ImGuiWindow * nav_window)9410 static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window)
9411 {
9412     ImGuiWindow* parent = nav_window;
9413     while (parent && parent->RootWindowDockStop != parent && (parent->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
9414         parent = parent->ParentWindow;
9415     if (parent && parent != nav_window)
9416         parent->NavLastChildNavWindow = nav_window;
9417 }
9418 
9419 // Restore the last focused child.
9420 // Call when we are expected to land on the Main Layer (0) after FocusWindow()
NavRestoreLastChildNavWindow(ImGuiWindow * window)9421 static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
9422 {
9423     if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive)
9424         return window->NavLastChildNavWindow;
9425     if (window->DockNodeAsHost && window->DockNodeAsHost->TabBar)
9426         if (ImGuiTabItem* tab = TabBarFindMostRecentlySelectedTabForActiveWindow(window->DockNodeAsHost->TabBar))
9427             return tab->Window;
9428     return window;
9429 }
9430 
NavRestoreLayer(ImGuiNavLayer layer)9431 static void NavRestoreLayer(ImGuiNavLayer layer)
9432 {
9433     ImGuiContext& g = *GImGui;
9434     g.NavLayer = layer;
9435     if (layer == 0)
9436         g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow);
9437     ImGuiWindow* window = g.NavWindow;
9438     if (window->NavLastIds[layer] != 0)
9439         ImGui::SetNavIDWithRectRel(window->NavLastIds[layer], layer, 0, g.NavWindow->NavRectRel[layer]);
9440     else
9441         ImGui::NavInitWindow(window, true);
9442 }
9443 
NavUpdateAnyRequestFlag()9444 static inline void ImGui::NavUpdateAnyRequestFlag()
9445 {
9446     ImGuiContext& g = *GImGui;
9447     g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
9448     if (g.NavAnyRequest)
9449         IM_ASSERT(g.NavWindow != NULL);
9450 }
9451 
9452 // This needs to be called before we submit any widget (aka in or before Begin)
NavInitWindow(ImGuiWindow * window,bool force_reinit)9453 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
9454 {
9455     ImGuiContext& g = *GImGui;
9456     IM_ASSERT(window == g.NavWindow);
9457     bool init_for_nav = false;
9458     if (!(window->Flags & ImGuiWindowFlags_NoNavInputs))
9459         if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
9460             init_for_nav = true;
9461     IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer);
9462     if (init_for_nav)
9463     {
9464         SetNavID(0, g.NavLayer, 0);
9465         g.NavInitRequest = true;
9466         g.NavInitRequestFromMove = false;
9467         g.NavInitResultId = 0;
9468         g.NavInitResultRectRel = ImRect();
9469         NavUpdateAnyRequestFlag();
9470     }
9471     else
9472     {
9473         g.NavId = window->NavLastIds[0];
9474         g.NavFocusScopeId = 0;
9475     }
9476 }
9477 
NavCalcPreferredRefPos()9478 static ImVec2 ImGui::NavCalcPreferredRefPos()
9479 {
9480     ImGuiContext& g = *GImGui;
9481     if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow)
9482     {
9483         // Mouse (we need a fallback in case the mouse becomes invalid after being used)
9484         if (IsMousePosValid(&g.IO.MousePos))
9485             return g.IO.MousePos;
9486         return g.LastValidMousePos;
9487     }
9488     else
9489     {
9490         // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item.
9491         const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
9492         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()));
9493         ImRect visible_rect = g.NavWindow->Viewport->GetMainRect();
9494         return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max));   // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta.
9495     }
9496 }
9497 
GetNavInputAmount(ImGuiNavInput n,ImGuiInputReadMode mode)9498 float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode)
9499 {
9500     ImGuiContext& g = *GImGui;
9501     if (mode == ImGuiInputReadMode_Down)
9502         return g.IO.NavInputs[n];                         // Instant, read analog input (0.0f..1.0f, as provided by user)
9503 
9504     const float t = g.IO.NavInputsDownDuration[n];
9505     if (t < 0.0f && mode == ImGuiInputReadMode_Released)  // Return 1.0f when just released, no repeat, ignore analog input.
9506         return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
9507     if (t < 0.0f)
9508         return 0.0f;
9509     if (mode == ImGuiInputReadMode_Pressed)               // Return 1.0f when just pressed, no repeat, ignore analog input.
9510         return (t == 0.0f) ? 1.0f : 0.0f;
9511     if (mode == ImGuiInputReadMode_Repeat)
9512         return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.80f);
9513     if (mode == ImGuiInputReadMode_RepeatSlow)
9514         return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 1.25f, g.IO.KeyRepeatRate * 2.00f);
9515     if (mode == ImGuiInputReadMode_RepeatFast)
9516         return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.30f);
9517     return 0.0f;
9518 }
9519 
GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources,ImGuiInputReadMode mode,float slow_factor,float fast_factor)9520 ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor)
9521 {
9522     ImVec2 delta(0.0f, 0.0f);
9523     if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
9524         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode)   - GetNavInputAmount(ImGuiNavInput_KeyLeft_,   mode), GetNavInputAmount(ImGuiNavInput_KeyDown_,   mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_,   mode));
9525     if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
9526         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode)   - GetNavInputAmount(ImGuiNavInput_DpadLeft,   mode), GetNavInputAmount(ImGuiNavInput_DpadDown,   mode) - GetNavInputAmount(ImGuiNavInput_DpadUp,   mode));
9527     if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
9528         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode));
9529     if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow))
9530         delta *= slow_factor;
9531     if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast))
9532         delta *= fast_factor;
9533     return delta;
9534 }
9535 
NavUpdate()9536 static void ImGui::NavUpdate()
9537 {
9538     ImGuiContext& g = *GImGui;
9539     ImGuiIO& io = g.IO;
9540 
9541     io.WantSetMousePos = false;
9542     g.NavWrapRequestWindow = NULL;
9543     g.NavWrapRequestFlags = ImGuiNavMoveFlags_None;
9544 #if 0
9545     if (g.NavScoringCount > 0) IMGUI_DEBUG_LOG("NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest);
9546 #endif
9547 
9548     // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard)
9549     // (do it before we map Keyboard input!)
9550     bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
9551     bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
9552     if (nav_gamepad_active && g.NavInputSource != ImGuiInputSource_NavGamepad)
9553     {
9554         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
9555             || io.NavInputs[ImGuiNavInput_DpadLeft] > 0.0f || io.NavInputs[ImGuiNavInput_DpadRight] > 0.0f || io.NavInputs[ImGuiNavInput_DpadUp] > 0.0f || io.NavInputs[ImGuiNavInput_DpadDown] > 0.0f)
9556             g.NavInputSource = ImGuiInputSource_NavGamepad;
9557     }
9558 
9559     // Update Keyboard->Nav inputs mapping
9560     if (nav_keyboard_active)
9561     {
9562         #define NAV_MAP_KEY(_KEY, _NAV_INPUT)  do { if (IsKeyDown(io.KeyMap[_KEY])) { io.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; } } while (0)
9563         NAV_MAP_KEY(ImGuiKey_Space,     ImGuiNavInput_Activate );
9564         NAV_MAP_KEY(ImGuiKey_Enter,     ImGuiNavInput_Input    );
9565         NAV_MAP_KEY(ImGuiKey_Escape,    ImGuiNavInput_Cancel   );
9566         NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ );
9567         NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
9568         NAV_MAP_KEY(ImGuiKey_UpArrow,   ImGuiNavInput_KeyUp_   );
9569         NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
9570         if (io.KeyCtrl)
9571             io.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
9572         if (io.KeyShift)
9573             io.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
9574         if (io.KeyAlt && !io.KeyCtrl) // AltGR is Alt+Ctrl, also even on keyboards without AltGR we don't want Alt+Ctrl to open menu.
9575             io.NavInputs[ImGuiNavInput_KeyMenu_]  = 1.0f;
9576         #undef NAV_MAP_KEY
9577     }
9578     memcpy(io.NavInputsDownDurationPrev, io.NavInputsDownDuration, sizeof(io.NavInputsDownDuration));
9579     for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++)
9580         io.NavInputsDownDuration[i] = (io.NavInputs[i] > 0.0f) ? (io.NavInputsDownDuration[i] < 0.0f ? 0.0f : io.NavInputsDownDuration[i] + io.DeltaTime) : -1.0f;
9581 
9582     // Process navigation init request (select first/default focus)
9583     if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove))
9584         NavUpdateInitResult();
9585     g.NavInitRequest = false;
9586     g.NavInitRequestFromMove = false;
9587     g.NavInitResultId = 0;
9588     g.NavJustMovedToId = 0;
9589 
9590     // Process navigation move request
9591     if (g.NavMoveRequest)
9592         NavUpdateMoveResult();
9593 
9594     // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
9595     if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
9596     {
9597         IM_ASSERT(g.NavMoveRequest);
9598         if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
9599             g.NavDisableHighlight = false;
9600         g.NavMoveRequestForward = ImGuiNavForward_None;
9601     }
9602 
9603     // Apply application mouse position movement, after we had a chance to process move request result.
9604     if (g.NavMousePosDirty && g.NavIdIsAlive)
9605     {
9606         // Set mouse position given our knowledge of the navigated item position from last frame
9607         if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
9608         {
9609             if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
9610             {
9611                 io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos();
9612                 io.WantSetMousePos = true;
9613             }
9614         }
9615         g.NavMousePosDirty = false;
9616     }
9617     g.NavIdIsAlive = false;
9618     g.NavJustTabbedId = 0;
9619     IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
9620 
9621     // Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0
9622     if (g.NavWindow)
9623         NavSaveLastChildNavWindowIntoParent(g.NavWindow);
9624     if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main)
9625         g.NavWindow->NavLastChildNavWindow = NULL;
9626 
9627     // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
9628     NavUpdateWindowing();
9629 
9630     // Set output flags for user application
9631     io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
9632     io.NavVisible = (io.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL);
9633 
9634     // Process NavCancel input (to close a popup, get back to parent, clear focus)
9635     if (IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
9636     {
9637         IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n");
9638         if (g.ActiveId != 0)
9639         {
9640             if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel))
9641                 ClearActiveID();
9642         }
9643         else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow && g.NavWindow != g.NavWindow->RootWindowDockStop)
9644         {
9645             // Exit child window
9646             ImGuiWindow* child_window = g.NavWindow;
9647             ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
9648             IM_ASSERT(child_window->ChildId != 0);
9649             FocusWindow(parent_window);
9650             SetNavID(child_window->ChildId, 0, 0);
9651             // Reassigning with same value, we're being explicit here.
9652             g.NavIdIsAlive = false;     // -V1048
9653             if (g.NavDisableMouseHover)
9654                 g.NavMousePosDirty = true;
9655         }
9656         else if (g.OpenPopupStack.Size > 0)
9657         {
9658             // Close open popup/menu
9659             if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
9660                 ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
9661         }
9662         else if (g.NavLayer != ImGuiNavLayer_Main)
9663         {
9664             // Leave the "menu" layer
9665             NavRestoreLayer(ImGuiNavLayer_Main);
9666         }
9667         else
9668         {
9669             // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
9670             if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
9671                 g.NavWindow->NavLastIds[0] = 0;
9672             g.NavId = g.NavFocusScopeId = 0;
9673         }
9674     }
9675 
9676     // Process manual activation request
9677     g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
9678     if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
9679     {
9680         bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
9681         bool activate_pressed = activate_down && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
9682         if (g.ActiveId == 0 && activate_pressed)
9683             g.NavActivateId = g.NavId;
9684         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
9685             g.NavActivateDownId = g.NavId;
9686         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
9687             g.NavActivatePressedId = g.NavId;
9688         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
9689             g.NavInputId = g.NavId;
9690     }
9691     if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
9692         g.NavDisableHighlight = true;
9693     if (g.NavActivateId != 0)
9694         IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
9695     g.NavMoveRequest = false;
9696 
9697     // Process programmatic activation request
9698     if (g.NavNextActivateId != 0)
9699         g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
9700     g.NavNextActivateId = 0;
9701 
9702     // Initiate directional inputs request
9703     if (g.NavMoveRequestForward == ImGuiNavForward_None)
9704     {
9705         g.NavMoveDir = ImGuiDir_None;
9706         g.NavMoveRequestFlags = ImGuiNavMoveFlags_None;
9707         if (g.NavWindow && !g.NavWindowingTarget && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
9708         {
9709             const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat;
9710             if (!IsActiveIdUsingNavDir(ImGuiDir_Left)  && (IsNavInputTest(ImGuiNavInput_DpadLeft,  read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_,  read_mode))) { g.NavMoveDir = ImGuiDir_Left; }
9711             if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; }
9712             if (!IsActiveIdUsingNavDir(ImGuiDir_Up)    && (IsNavInputTest(ImGuiNavInput_DpadUp,    read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_,    read_mode))) { g.NavMoveDir = ImGuiDir_Up; }
9713             if (!IsActiveIdUsingNavDir(ImGuiDir_Down)  && (IsNavInputTest(ImGuiNavInput_DpadDown,  read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_,  read_mode))) { g.NavMoveDir = ImGuiDir_Down; }
9714         }
9715         g.NavMoveClipDir = g.NavMoveDir;
9716     }
9717     else
9718     {
9719         // 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)
9720         // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function)
9721         IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
9722         IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
9723         IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir);
9724         g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
9725     }
9726 
9727     // Update PageUp/PageDown/Home/End scroll
9728     // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
9729     float nav_scoring_rect_offset_y = 0.0f;
9730     if (nav_keyboard_active)
9731         nav_scoring_rect_offset_y = NavUpdatePageUpPageDown();
9732 
9733     // If we initiate a movement request and have no current NavId, we initiate a InitDefautRequest that will be used as a fallback if the direction fails to find a match
9734     if (g.NavMoveDir != ImGuiDir_None)
9735     {
9736         g.NavMoveRequest = true;
9737         g.NavMoveRequestKeyMods = io.KeyMods;
9738         g.NavMoveDirLast = g.NavMoveDir;
9739     }
9740     if (g.NavMoveRequest && g.NavId == 0)
9741     {
9742         IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer);
9743         g.NavInitRequest = g.NavInitRequestFromMove = true;
9744         // Reassigning with same value, we're being explicit here.
9745         g.NavInitResultId = 0;     // -V1048
9746         g.NavDisableHighlight = false;
9747     }
9748     NavUpdateAnyRequestFlag();
9749 
9750     // Scrolling
9751     if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
9752     {
9753         // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
9754         ImGuiWindow* window = g.NavWindow;
9755         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.
9756         if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
9757         {
9758             if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
9759                 SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
9760             if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
9761                 SetScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
9762         }
9763 
9764         // *Normal* Manual scroll with NavScrollXXX keys
9765         // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
9766         ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f / 10.0f, 10.0f);
9767         if (scroll_dir.x != 0.0f && window->ScrollbarX)
9768             SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
9769         if (scroll_dir.y != 0.0f)
9770             SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
9771     }
9772 
9773     // Reset search results
9774     g.NavMoveResultLocal.Clear();
9775     g.NavMoveResultLocalVisibleSet.Clear();
9776     g.NavMoveResultOther.Clear();
9777 
9778     // When using gamepad, we project the reference nav bounding box into window visible area.
9779     // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad every movements are relative
9780     // (can't focus a visible object like we can with the mouse).
9781     if (g.NavMoveRequest && g.NavInputSource == ImGuiInputSource_NavGamepad && g.NavLayer == ImGuiNavLayer_Main)
9782     {
9783         ImGuiWindow* window = g.NavWindow;
9784         ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1));
9785         if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
9786         {
9787             IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel\n");
9788             float pad = window->CalcFontSize() * 0.5f;
9789             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
9790             window->NavRectRel[g.NavLayer].ClipWithFull(window_rect_rel);
9791             g.NavId = g.NavFocusScopeId = 0;
9792         }
9793     }
9794 
9795     // 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)
9796     ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);
9797     g.NavScoringRect = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : ImRect(0, 0, 0, 0);
9798     g.NavScoringRect.TranslateY(nav_scoring_rect_offset_y);
9799     g.NavScoringRect.Min.x = ImMin(g.NavScoringRect.Min.x + 1.0f, g.NavScoringRect.Max.x);
9800     g.NavScoringRect.Max.x = g.NavScoringRect.Min.x;
9801     IM_ASSERT(!g.NavScoringRect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem().
9802     //GetForegroundDrawList()->AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
9803     g.NavScoringCount = 0;
9804 #if IMGUI_DEBUG_NAV_RECTS
9805     if (g.NavWindow)
9806     {
9807         ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow);
9808         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]
9809         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); }
9810     }
9811 #endif
9812 }
9813 
NavUpdateInitResult()9814 static void ImGui::NavUpdateInitResult()
9815 {
9816     // 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)
9817     ImGuiContext& g = *GImGui;
9818     if (!g.NavWindow)
9819         return;
9820 
9821     // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
9822     IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name);
9823     if (g.NavInitRequestFromMove)
9824         SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel);
9825     else
9826         SetNavID(g.NavInitResultId, g.NavLayer, 0);
9827     g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel;
9828 }
9829 
9830 // Apply result from previous frame navigation directional move request
NavUpdateMoveResult()9831 static void ImGui::NavUpdateMoveResult()
9832 {
9833     ImGuiContext& g = *GImGui;
9834     if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
9835     {
9836         // 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)
9837         if (g.NavId != 0)
9838         {
9839             g.NavDisableHighlight = false;
9840             g.NavDisableMouseHover = true;
9841         }
9842         return;
9843     }
9844 
9845     // Select which result to use
9846     ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
9847 
9848     // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
9849     if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
9850         if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId)
9851             result = &g.NavMoveResultLocalVisibleSet;
9852 
9853     // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
9854     if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
9855         if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
9856             result = &g.NavMoveResultOther;
9857     IM_ASSERT(g.NavWindow && result->Window);
9858 
9859     // Scroll to keep newly navigated item fully into view.
9860     if (g.NavLayer == ImGuiNavLayer_Main)
9861     {
9862         ImVec2 delta_scroll;
9863         if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_ScrollToEdge)
9864         {
9865             float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
9866             delta_scroll.y = result->Window->Scroll.y - scroll_target;
9867             SetScrollY(result->Window, scroll_target);
9868         }
9869         else
9870         {
9871             ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos);
9872             delta_scroll = ScrollToBringRectIntoView(result->Window, rect_abs);
9873         }
9874 
9875         // Offset our result position so mouse position can be applied immediately after in NavUpdate()
9876         result->RectRel.TranslateX(-delta_scroll.x);
9877         result->RectRel.TranslateY(-delta_scroll.y);
9878     }
9879 
9880     ClearActiveID();
9881     g.NavWindow = result->Window;
9882     if (g.NavId != result->ID)
9883     {
9884         // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
9885         g.NavJustMovedToId = result->ID;
9886         g.NavJustMovedToFocusScopeId = result->FocusScopeId;
9887         g.NavJustMovedToKeyMods = g.NavMoveRequestKeyMods;
9888     }
9889     IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
9890     SetNavIDWithRectRel(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
9891 }
9892 
9893 // Handle PageUp/PageDown/Home/End keys
NavUpdatePageUpPageDown()9894 static float ImGui::NavUpdatePageUpPageDown()
9895 {
9896     ImGuiContext& g = *GImGui;
9897     ImGuiIO& io = g.IO;
9898 
9899     if (g.NavMoveDir != ImGuiDir_None || g.NavWindow == NULL)
9900         return 0.0f;
9901     if ((g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != ImGuiNavLayer_Main)
9902         return 0.0f;
9903 
9904     ImGuiWindow* window = g.NavWindow;
9905     const bool page_up_held = IsKeyDown(io.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp);
9906     const bool page_down_held = IsKeyDown(io.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown);
9907     const bool home_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home);
9908     const bool end_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End);
9909     if (page_up_held != page_down_held || home_pressed != end_pressed) // If either (not both) are pressed
9910     {
9911         if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll)
9912         {
9913             // Fallback manual-scroll when window has no navigable item
9914             if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
9915                 SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
9916             else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
9917                 SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
9918             else if (home_pressed)
9919                 SetScrollY(window, 0.0f);
9920             else if (end_pressed)
9921                 SetScrollY(window, window->ScrollMax.y);
9922         }
9923         else
9924         {
9925             ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
9926             const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
9927             float nav_scoring_rect_offset_y = 0.0f;
9928             if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
9929             {
9930                 nav_scoring_rect_offset_y = -page_offset_y;
9931                 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)
9932                 g.NavMoveClipDir = ImGuiDir_Up;
9933                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
9934             }
9935             else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
9936             {
9937                 nav_scoring_rect_offset_y = +page_offset_y;
9938                 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)
9939                 g.NavMoveClipDir = ImGuiDir_Down;
9940                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
9941             }
9942             else if (home_pressed)
9943             {
9944                 // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
9945                 // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result.
9946                 // Preserve current horizontal position if we have any.
9947                 nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y;
9948                 if (nav_rect_rel.IsInverted())
9949                     nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
9950                 g.NavMoveDir = ImGuiDir_Down;
9951                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
9952             }
9953             else if (end_pressed)
9954             {
9955                 nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y;
9956                 if (nav_rect_rel.IsInverted())
9957                     nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
9958                 g.NavMoveDir = ImGuiDir_Up;
9959                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
9960             }
9961             return nav_scoring_rect_offset_y;
9962         }
9963     }
9964     return 0.0f;
9965 }
9966 
NavEndFrame()9967 static void ImGui::NavEndFrame()
9968 {
9969     ImGuiContext& g = *GImGui;
9970 
9971     // Show CTRL+TAB list window
9972     if (g.NavWindowingTarget != NULL)
9973         NavUpdateWindowingOverlay();
9974 
9975     // Perform wrap-around in menus
9976     ImGuiWindow* window = g.NavWrapRequestWindow;
9977     ImGuiNavMoveFlags move_flags = g.NavWrapRequestFlags;
9978     if (window != NULL && g.NavWindow == window && NavMoveRequestButNoResultYet() && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == ImGuiNavLayer_Main)
9979     {
9980         IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
9981         ImRect bb_rel = window->NavRectRel[0];
9982 
9983         ImGuiDir clip_dir = g.NavMoveDir;
9984         if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
9985         {
9986             bb_rel.Min.x = bb_rel.Max.x =
9987                 ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x;
9988             if (move_flags & ImGuiNavMoveFlags_WrapX)
9989             {
9990                 bb_rel.TranslateY(-bb_rel.GetHeight());
9991                 clip_dir = ImGuiDir_Up;
9992             }
9993             NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
9994         }
9995         if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
9996         {
9997             bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
9998             if (move_flags & ImGuiNavMoveFlags_WrapX)
9999             {
10000                 bb_rel.TranslateY(+bb_rel.GetHeight());
10001                 clip_dir = ImGuiDir_Down;
10002             }
10003             NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
10004         }
10005         if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
10006         {
10007             bb_rel.Min.y = bb_rel.Max.y =
10008                 ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y;
10009             if (move_flags & ImGuiNavMoveFlags_WrapY)
10010             {
10011                 bb_rel.TranslateX(-bb_rel.GetWidth());
10012                 clip_dir = ImGuiDir_Left;
10013             }
10014             NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
10015         }
10016         if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
10017         {
10018             bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
10019             if (move_flags & ImGuiNavMoveFlags_WrapY)
10020             {
10021                 bb_rel.TranslateX(+bb_rel.GetWidth());
10022                 clip_dir = ImGuiDir_Right;
10023             }
10024             NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
10025         }
10026     }
10027 }
10028 
FindWindowFocusIndex(ImGuiWindow * window)10029 static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N)
10030 {
10031     ImGuiContext& g = *GImGui;
10032     for (int i = g.WindowsFocusOrder.Size - 1; i >= 0; i--)
10033         if (g.WindowsFocusOrder[i] == window)
10034             return i;
10035     return -1;
10036 }
10037 
FindWindowNavFocusable(int i_start,int i_stop,int dir)10038 static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
10039 {
10040     ImGuiContext& g = *GImGui;
10041     for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
10042         if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
10043             return g.WindowsFocusOrder[i];
10044     return NULL;
10045 }
10046 
NavUpdateWindowingHighlightWindow(int focus_change_dir)10047 static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
10048 {
10049     ImGuiContext& g = *GImGui;
10050     IM_ASSERT(g.NavWindowingTarget);
10051     if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
10052         return;
10053 
10054     const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget);
10055     ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
10056     if (!window_target)
10057         window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
10058     if (window_target) // Don't reset windowing target if there's a single window in the list
10059         g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
10060     g.NavWindowingToggleLayer = false;
10061 }
10062 
10063 // Windowing management mode
10064 // Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer)
10065 // Gamepad:  Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)
NavUpdateWindowing()10066 static void ImGui::NavUpdateWindowing()
10067 {
10068     ImGuiContext& g = *GImGui;
10069     ImGuiWindow* apply_focus_window = NULL;
10070     bool apply_toggle_layer = false;
10071 
10072     ImGuiWindow* modal_window = GetTopMostPopupModal();
10073     bool allow_windowing = (modal_window == NULL);
10074     if (!allow_windowing)
10075         g.NavWindowingTarget = NULL;
10076 
10077     // Fade out
10078     if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
10079     {
10080         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f);
10081         if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
10082             g.NavWindowingTargetAnim = NULL;
10083     }
10084 
10085     // Start CTRL-TAB or Square+L/R window selection
10086     bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
10087     bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
10088     if (start_windowing_with_gamepad || start_windowing_with_keyboard)
10089         if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
10090         {
10091             g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindowDockStop;
10092             g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
10093             g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true;
10094             g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad;
10095         }
10096 
10097     // Gamepad update
10098     g.NavWindowingTimer += g.IO.DeltaTime;
10099     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad)
10100     {
10101         // 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
10102         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
10103 
10104         // Select window to focus
10105         const int focus_change_dir = (int)IsNavInputTest(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputTest(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow);
10106         if (focus_change_dir != 0)
10107         {
10108             NavUpdateWindowingHighlightWindow(focus_change_dir);
10109             g.NavWindowingHighlightAlpha = 1.0f;
10110         }
10111 
10112         // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)
10113         if (!IsNavInputDown(ImGuiNavInput_Menu))
10114         {
10115             g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
10116             if (g.NavWindowingToggleLayer && g.NavWindow)
10117                 apply_toggle_layer = true;
10118             else if (!g.NavWindowingToggleLayer)
10119                 apply_focus_window = g.NavWindowingTarget;
10120             g.NavWindowingTarget = NULL;
10121         }
10122     }
10123 
10124     // Keyboard: Focus
10125     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard)
10126     {
10127         // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
10128         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
10129         if (IsKeyPressedMap(ImGuiKey_Tab, true))
10130             NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1);
10131         if (!g.IO.KeyCtrl)
10132             apply_focus_window = g.NavWindowingTarget;
10133     }
10134 
10135     // Keyboard: Press and Release ALT to toggle menu layer
10136     // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of backend clearing releases all keys on ALT-TAB
10137     if (IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Pressed))
10138         g.NavWindowingToggleLayer = true;
10139     if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && g.NavWindowingToggleLayer && IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released))
10140         if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev))
10141             apply_toggle_layer = true;
10142 
10143     // Move window
10144     if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
10145     {
10146         ImVec2 move_delta;
10147         if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift)
10148             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
10149         if (g.NavInputSource == ImGuiInputSource_NavGamepad)
10150             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down);
10151         if (move_delta.x != 0.0f || move_delta.y != 0.0f)
10152         {
10153             const float NAV_MOVE_SPEED = 800.0f;
10154             const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't handle variable framerate very well
10155             ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow;
10156             SetWindowPos(moving_window, moving_window->Pos + move_delta * move_speed, ImGuiCond_Always);
10157             MarkIniSettingsDirty(moving_window);
10158             g.NavDisableMouseHover = true;
10159         }
10160     }
10161 
10162     // Apply final focus
10163     if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindowDockStop))
10164     {
10165         ImGuiViewport* previous_viewport = g.NavWindow ? g.NavWindow->Viewport : NULL;
10166         ClearActiveID();
10167         g.NavDisableHighlight = false;
10168         g.NavDisableMouseHover = true;
10169         apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
10170         ClosePopupsOverWindow(apply_focus_window, false);
10171         FocusWindow(apply_focus_window);
10172         if (apply_focus_window->NavLastIds[0] == 0)
10173             NavInitWindow(apply_focus_window, false);
10174 
10175         // If the window only has a menu layer, select it directly
10176         if (apply_focus_window->DC.NavLayerActiveMask == (1 << ImGuiNavLayer_Menu))
10177             g.NavLayer = ImGuiNavLayer_Menu;
10178 
10179         // Request OS level focus
10180         if (apply_focus_window->Viewport != previous_viewport && g.PlatformIO.Platform_SetWindowFocus)
10181             g.PlatformIO.Platform_SetWindowFocus(apply_focus_window->Viewport);
10182     }
10183     if (apply_focus_window)
10184         g.NavWindowingTarget = NULL;
10185 
10186     // Apply menu/layer toggle
10187     if (apply_toggle_layer && g.NavWindow)
10188     {
10189         // Move to parent menu if necessary
10190         ImGuiWindow* new_nav_window = g.NavWindow;
10191         while (new_nav_window->ParentWindow
10192             && (new_nav_window->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) == 0
10193             && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
10194             && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
10195             new_nav_window = new_nav_window->ParentWindow;
10196         if (new_nav_window != g.NavWindow)
10197         {
10198             ImGuiWindow* old_nav_window = g.NavWindow;
10199             FocusWindow(new_nav_window);
10200             new_nav_window->NavLastChildNavWindow = old_nav_window;
10201         }
10202         g.NavDisableHighlight = false;
10203         g.NavDisableMouseHover = true;
10204 
10205         // When entering a regular menu bar with the Alt key, we always reinitialize the navigation ID. It however persist on docking tab tabs.
10206         const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;
10207         const bool preserve_layer_1_nav_id = (new_nav_window->DockNodeAsHost != NULL);
10208         if (new_nav_layer == ImGuiNavLayer_Menu && !preserve_layer_1_nav_id)
10209             g.NavWindow->NavLastIds[ImGuiNavLayer_Menu] = 0;
10210         NavRestoreLayer(new_nav_layer);
10211     }
10212 }
10213 
10214 // Window has already passed the IsWindowNavFocusable()
GetFallbackWindowNameForWindowingList(ImGuiWindow * window)10215 static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
10216 {
10217     if (window->Flags & ImGuiWindowFlags_Popup)
10218         return "(Popup)";
10219     if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
10220         return "(Main menu bar)";
10221     if (window->DockNodeAsHost)
10222         return "(Dock node)";
10223     return "(Untitled)";
10224 }
10225 
10226 // Overlay displayed when using CTRL+TAB. Called by EndFrame().
NavUpdateWindowingOverlay()10227 void ImGui::NavUpdateWindowingOverlay()
10228 {
10229     ImGuiContext& g = *GImGui;
10230     IM_ASSERT(g.NavWindowingTarget != NULL);
10231 
10232     if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
10233         return;
10234 
10235     if (g.NavWindowingListWindow == NULL)
10236         g.NavWindowingListWindow = FindWindowByName("###NavWindowingList");
10237     ImGuiViewportP* viewport = /*g.NavWindow ? g.NavWindow->Viewport :*/ (ImGuiViewportP*)GetMainViewport();
10238     SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
10239     SetNextWindowPos(viewport->Pos + viewport->Size * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
10240     PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
10241     Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
10242     for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
10243     {
10244         ImGuiWindow* window = g.WindowsFocusOrder[n];
10245         if (!IsWindowNavFocusable(window))
10246             continue;
10247         const char* label = window->Name;
10248         if (label == FindRenderedTextEnd(label))
10249             label = GetFallbackWindowNameForWindowingList(window);
10250         Selectable(label, g.NavWindowingTarget == window);
10251     }
10252     End();
10253     PopStyleVar();
10254 }
10255 
10256 
10257 //-----------------------------------------------------------------------------
10258 // [SECTION] DRAG AND DROP
10259 //-----------------------------------------------------------------------------
10260 
ClearDragDrop()10261 void ImGui::ClearDragDrop()
10262 {
10263     ImGuiContext& g = *GImGui;
10264     g.DragDropActive = false;
10265     g.DragDropPayload.Clear();
10266     g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
10267     g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
10268     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
10269     g.DragDropAcceptFrameCount = -1;
10270 
10271     g.DragDropPayloadBufHeap.clear();
10272     memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
10273 }
10274 
10275 // Call when current ID is active.
10276 // When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
BeginDragDropSource(ImGuiDragDropFlags flags)10277 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
10278 {
10279     ImGuiContext& g = *GImGui;
10280     ImGuiWindow* window = g.CurrentWindow;
10281 
10282     bool source_drag_active = false;
10283     ImGuiID source_id = 0;
10284     ImGuiID source_parent_id = 0;
10285     ImGuiMouseButton mouse_button = ImGuiMouseButton_Left;
10286     if (!(flags & ImGuiDragDropFlags_SourceExtern))
10287     {
10288         source_id = window->DC.LastItemId;
10289         if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case
10290             return false;
10291         if (g.IO.MouseDown[mouse_button] == false)
10292             return false;
10293 
10294         if (source_id == 0)
10295         {
10296             // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
10297             // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
10298             if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
10299             {
10300                 IM_ASSERT(0);
10301                 return false;
10302             }
10303 
10304             // Early out
10305             if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
10306                 return false;
10307 
10308             // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
10309             // We build a throwaway ID based on current ID stack + relative AABB of items in window.
10310             // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
10311             // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
10312             source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect);
10313             bool is_hovered = ItemHoverable(window->DC.LastItemRect, source_id);
10314             if (is_hovered && g.IO.MouseClicked[mouse_button])
10315             {
10316                 SetActiveID(source_id, window);
10317                 FocusWindow(window);
10318             }
10319             if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
10320                 g.ActiveIdAllowOverlap = is_hovered;
10321         }
10322         else
10323         {
10324             g.ActiveIdAllowOverlap = false;
10325         }
10326         if (g.ActiveId != source_id)
10327             return false;
10328         source_parent_id = window->IDStack.back();
10329         source_drag_active = IsMouseDragging(mouse_button);
10330 
10331         // Disable navigation and key inputs while dragging
10332         g.ActiveIdUsingNavDirMask = ~(ImU32)0;
10333         g.ActiveIdUsingNavInputMask = ~(ImU32)0;
10334         g.ActiveIdUsingKeyInputMask = ~(ImU64)0;
10335     }
10336     else
10337     {
10338         window = NULL;
10339         source_id = ImHashStr("#SourceExtern");
10340         source_drag_active = true;
10341     }
10342 
10343     if (source_drag_active)
10344     {
10345         if (!g.DragDropActive)
10346         {
10347             IM_ASSERT(source_id != 0);
10348             ClearDragDrop();
10349             ImGuiPayload& payload = g.DragDropPayload;
10350             payload.SourceId = source_id;
10351             payload.SourceParentId = source_parent_id;
10352             g.DragDropActive = true;
10353             g.DragDropSourceFlags = flags;
10354             g.DragDropMouseButton = mouse_button;
10355             if (payload.SourceId == g.ActiveId)
10356                 g.ActiveIdNoClearOnFocusLoss = true;
10357         }
10358         g.DragDropSourceFrameCount = g.FrameCount;
10359         g.DragDropWithinSource = true;
10360 
10361         if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
10362         {
10363             // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
10364             // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
10365             BeginTooltip();
10366             if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
10367             {
10368                 ImGuiWindow* tooltip_window = g.CurrentWindow;
10369                 tooltip_window->SkipItems = true;
10370                 tooltip_window->HiddenFramesCanSkipItems = 1;
10371             }
10372         }
10373 
10374         if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
10375             window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
10376 
10377         return true;
10378     }
10379     return false;
10380 }
10381 
EndDragDropSource()10382 void ImGui::EndDragDropSource()
10383 {
10384     ImGuiContext& g = *GImGui;
10385     IM_ASSERT(g.DragDropActive);
10386     IM_ASSERT(g.DragDropWithinSource && "Not after a BeginDragDropSource()?");
10387 
10388     if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
10389         EndTooltip();
10390 
10391     // Discard the drag if have not called SetDragDropPayload()
10392     if (g.DragDropPayload.DataFrameCount == -1)
10393         ClearDragDrop();
10394     g.DragDropWithinSource = false;
10395 }
10396 
10397 // 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)10398 bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
10399 {
10400     ImGuiContext& g = *GImGui;
10401     ImGuiPayload& payload = g.DragDropPayload;
10402     if (cond == 0)
10403         cond = ImGuiCond_Always;
10404 
10405     IM_ASSERT(type != NULL);
10406     IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
10407     IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
10408     IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
10409     IM_ASSERT(payload.SourceId != 0);                               // Not called between BeginDragDropSource() and EndDragDropSource()
10410 
10411     if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
10412     {
10413         // Copy payload
10414         ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
10415         g.DragDropPayloadBufHeap.resize(0);
10416         if (data_size > sizeof(g.DragDropPayloadBufLocal))
10417         {
10418             // Store in heap
10419             g.DragDropPayloadBufHeap.resize((int)data_size);
10420             payload.Data = g.DragDropPayloadBufHeap.Data;
10421             memcpy(payload.Data, data, data_size);
10422         }
10423         else if (data_size > 0)
10424         {
10425             // Store locally
10426             memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
10427             payload.Data = g.DragDropPayloadBufLocal;
10428             memcpy(payload.Data, data, data_size);
10429         }
10430         else
10431         {
10432             payload.Data = NULL;
10433         }
10434         payload.DataSize = (int)data_size;
10435     }
10436     payload.DataFrameCount = g.FrameCount;
10437 
10438     return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
10439 }
10440 
BeginDragDropTargetCustom(const ImRect & bb,ImGuiID id)10441 bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
10442 {
10443     ImGuiContext& g = *GImGui;
10444     if (!g.DragDropActive)
10445         return false;
10446 
10447     ImGuiWindow* window = g.CurrentWindow;
10448     ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
10449     if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow)
10450         return false;
10451     IM_ASSERT(id != 0);
10452     if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
10453         return false;
10454     if (window->SkipItems)
10455         return false;
10456 
10457     IM_ASSERT(g.DragDropWithinTarget == false);
10458     g.DragDropTargetRect = bb;
10459     g.DragDropTargetId = id;
10460     g.DragDropWithinTarget = true;
10461     return true;
10462 }
10463 
10464 // We don't use BeginDragDropTargetCustom() and duplicate its code because:
10465 // 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
10466 // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
10467 // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
BeginDragDropTarget()10468 bool ImGui::BeginDragDropTarget()
10469 {
10470     ImGuiContext& g = *GImGui;
10471     if (!g.DragDropActive)
10472         return false;
10473 
10474     ImGuiWindow* window = g.CurrentWindow;
10475     if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
10476         return false;
10477     ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
10478     if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow)
10479         return false;
10480 
10481     const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect;
10482     ImGuiID id = window->DC.LastItemId;
10483     if (id == 0)
10484         id = window->GetIDFromRectangle(display_rect);
10485     if (g.DragDropPayload.SourceId == id)
10486         return false;
10487 
10488     IM_ASSERT(g.DragDropWithinTarget == false);
10489     g.DragDropTargetRect = display_rect;
10490     g.DragDropTargetId = id;
10491     g.DragDropWithinTarget = true;
10492     return true;
10493 }
10494 
IsDragDropPayloadBeingAccepted()10495 bool ImGui::IsDragDropPayloadBeingAccepted()
10496 {
10497     ImGuiContext& g = *GImGui;
10498     return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
10499 }
10500 
AcceptDragDropPayload(const char * type,ImGuiDragDropFlags flags)10501 const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
10502 {
10503     ImGuiContext& g = *GImGui;
10504     ImGuiWindow* window = g.CurrentWindow;
10505     ImGuiPayload& payload = g.DragDropPayload;
10506     IM_ASSERT(g.DragDropActive);                        // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
10507     IM_ASSERT(payload.DataFrameCount != -1);            // Forgot to call EndDragDropTarget() ?
10508     if (type != NULL && !payload.IsDataType(type))
10509         return NULL;
10510 
10511     // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
10512     // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
10513     const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
10514     ImRect r = g.DragDropTargetRect;
10515     float r_surface = r.GetWidth() * r.GetHeight();
10516     if (r_surface <= g.DragDropAcceptIdCurrRectSurface)
10517     {
10518         g.DragDropAcceptFlags = flags;
10519         g.DragDropAcceptIdCurr = g.DragDropTargetId;
10520         g.DragDropAcceptIdCurrRectSurface = r_surface;
10521     }
10522 
10523     // Render default drop visuals
10524     payload.Preview = was_accepted_previously;
10525     flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
10526     if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
10527     {
10528         // FIXME-DRAG: Settle on a proper default visuals for drop target.
10529         r.Expand(3.5f);
10530         bool push_clip_rect = !window->ClipRect.Contains(r);
10531         if (push_clip_rect) window->DrawList->PushClipRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1));
10532         window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f);
10533         if (push_clip_rect) window->DrawList->PopClipRect();
10534     }
10535 
10536     g.DragDropAcceptFrameCount = g.FrameCount;
10537     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()
10538     if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
10539         return NULL;
10540 
10541     return &payload;
10542 }
10543 
GetDragDropPayload()10544 const ImGuiPayload* ImGui::GetDragDropPayload()
10545 {
10546     ImGuiContext& g = *GImGui;
10547     return g.DragDropActive ? &g.DragDropPayload : NULL;
10548 }
10549 
10550 // We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
EndDragDropTarget()10551 void ImGui::EndDragDropTarget()
10552 {
10553     ImGuiContext& g = *GImGui;
10554     IM_ASSERT(g.DragDropActive);
10555     IM_ASSERT(g.DragDropWithinTarget);
10556     g.DragDropWithinTarget = false;
10557 }
10558 
10559 //-----------------------------------------------------------------------------
10560 // [SECTION] LOGGING/CAPTURING
10561 //-----------------------------------------------------------------------------
10562 // All text output from the interface can be captured into tty/file/clipboard.
10563 // By default, tree nodes are automatically opened during logging.
10564 //-----------------------------------------------------------------------------
10565 
10566 // Pass text data straight to log (without being displayed)
LogText(const char * fmt,...)10567 void ImGui::LogText(const char* fmt, ...)
10568 {
10569     ImGuiContext& g = *GImGui;
10570     if (!g.LogEnabled)
10571         return;
10572 
10573     va_list args;
10574     va_start(args, fmt);
10575     if (g.LogFile)
10576     {
10577         g.LogBuffer.Buf.resize(0);
10578         g.LogBuffer.appendfv(fmt, args);
10579         ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile);
10580     }
10581     else
10582     {
10583         g.LogBuffer.appendfv(fmt, args);
10584     }
10585     va_end(args);
10586 }
10587 
10588 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
10589 // We split text into individual lines to add current tree level padding
LogRenderedText(const ImVec2 * ref_pos,const char * text,const char * text_end)10590 void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
10591 {
10592     ImGuiContext& g = *GImGui;
10593     ImGuiWindow* window = g.CurrentWindow;
10594 
10595     if (!text_end)
10596         text_end = FindRenderedTextEnd(text, text_end);
10597 
10598     const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + 1);
10599     if (ref_pos)
10600         g.LogLinePosY = ref_pos->y;
10601     if (log_new_line)
10602         g.LogLineFirstItem = true;
10603 
10604     const char* text_remaining = text;
10605     if (g.LogDepthRef > window->DC.TreeDepth)  // Re-adjust padding if we have popped out of our starting depth
10606         g.LogDepthRef = window->DC.TreeDepth;
10607     const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);
10608     for (;;)
10609     {
10610         // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
10611         // We don't add a trailing \n to allow a subsequent item on the same line to be captured.
10612         const char* line_start = text_remaining;
10613         const char* line_end = ImStreolRange(line_start, text_end);
10614         const bool is_first_line = (line_start == text);
10615         const bool is_last_line = (line_end == text_end);
10616         if (!is_last_line || (line_start != line_end))
10617         {
10618             const int char_count = (int)(line_end - line_start);
10619             if (log_new_line || !is_first_line)
10620                 LogText(IM_NEWLINE "%*s%.*s", tree_depth * 4, "", char_count, line_start);
10621             else if (g.LogLineFirstItem)
10622                 LogText("%*s%.*s", tree_depth * 4, "", char_count, line_start);
10623             else
10624                 LogText(" %.*s", char_count, line_start);
10625             g.LogLineFirstItem = false;
10626         }
10627         else if (log_new_line)
10628         {
10629             // An empty "" string at a different Y position should output a carriage return.
10630             LogText(IM_NEWLINE);
10631             break;
10632         }
10633 
10634         if (is_last_line)
10635             break;
10636         text_remaining = line_end + 1;
10637     }
10638 }
10639 
10640 // Start logging/capturing text output
LogBegin(ImGuiLogType type,int auto_open_depth)10641 void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth)
10642 {
10643     ImGuiContext& g = *GImGui;
10644     ImGuiWindow* window = g.CurrentWindow;
10645     IM_ASSERT(g.LogEnabled == false);
10646     IM_ASSERT(g.LogFile == NULL);
10647     IM_ASSERT(g.LogBuffer.empty());
10648     g.LogEnabled = true;
10649     g.LogType = type;
10650     g.LogDepthRef = window->DC.TreeDepth;
10651     g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);
10652     g.LogLinePosY = FLT_MAX;
10653     g.LogLineFirstItem = true;
10654 }
10655 
LogToTTY(int auto_open_depth)10656 void ImGui::LogToTTY(int auto_open_depth)
10657 {
10658     ImGuiContext& g = *GImGui;
10659     if (g.LogEnabled)
10660         return;
10661     IM_UNUSED(auto_open_depth);
10662 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
10663     LogBegin(ImGuiLogType_TTY, auto_open_depth);
10664     g.LogFile = stdout;
10665 #endif
10666 }
10667 
10668 // Start logging/capturing text output to given file
LogToFile(int auto_open_depth,const char * filename)10669 void ImGui::LogToFile(int auto_open_depth, const char* filename)
10670 {
10671     ImGuiContext& g = *GImGui;
10672     if (g.LogEnabled)
10673         return;
10674 
10675     // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still
10676     // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE.
10677     // By opening the file in binary mode "ab" we have consistent output everywhere.
10678     if (!filename)
10679         filename = g.IO.LogFilename;
10680     if (!filename || !filename[0])
10681         return;
10682     ImFileHandle f = ImFileOpen(filename, "ab");
10683     if (!f)
10684     {
10685         IM_ASSERT(0);
10686         return;
10687     }
10688 
10689     LogBegin(ImGuiLogType_File, auto_open_depth);
10690     g.LogFile = f;
10691 }
10692 
10693 // Start logging/capturing text output to clipboard
LogToClipboard(int auto_open_depth)10694 void ImGui::LogToClipboard(int auto_open_depth)
10695 {
10696     ImGuiContext& g = *GImGui;
10697     if (g.LogEnabled)
10698         return;
10699     LogBegin(ImGuiLogType_Clipboard, auto_open_depth);
10700 }
10701 
LogToBuffer(int auto_open_depth)10702 void ImGui::LogToBuffer(int auto_open_depth)
10703 {
10704     ImGuiContext& g = *GImGui;
10705     if (g.LogEnabled)
10706         return;
10707     LogBegin(ImGuiLogType_Buffer, auto_open_depth);
10708 }
10709 
LogFinish()10710 void ImGui::LogFinish()
10711 {
10712     ImGuiContext& g = *GImGui;
10713     if (!g.LogEnabled)
10714         return;
10715 
10716     LogText(IM_NEWLINE);
10717     switch (g.LogType)
10718     {
10719     case ImGuiLogType_TTY:
10720 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
10721         fflush(g.LogFile);
10722 #endif
10723         break;
10724     case ImGuiLogType_File:
10725         ImFileClose(g.LogFile);
10726         break;
10727     case ImGuiLogType_Buffer:
10728         break;
10729     case ImGuiLogType_Clipboard:
10730         if (!g.LogBuffer.empty())
10731             SetClipboardText(g.LogBuffer.begin());
10732         break;
10733     case ImGuiLogType_None:
10734         IM_ASSERT(0);
10735         break;
10736     }
10737 
10738     g.LogEnabled = false;
10739     g.LogType = ImGuiLogType_None;
10740     g.LogFile = NULL;
10741     g.LogBuffer.clear();
10742 }
10743 
10744 // Helper to display logging buttons
10745 // FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!)
LogButtons()10746 void ImGui::LogButtons()
10747 {
10748     ImGuiContext& g = *GImGui;
10749 
10750     PushID("LogButtons");
10751 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
10752     const bool log_to_tty = Button("Log To TTY"); SameLine();
10753 #else
10754     const bool log_to_tty = false;
10755 #endif
10756     const bool log_to_file = Button("Log To File"); SameLine();
10757     const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
10758     PushAllowKeyboardFocus(false);
10759     SetNextItemWidth(80.0f);
10760     SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL);
10761     PopAllowKeyboardFocus();
10762     PopID();
10763 
10764     // Start logging at the end of the function so that the buttons don't appear in the log
10765     if (log_to_tty)
10766         LogToTTY();
10767     if (log_to_file)
10768         LogToFile();
10769     if (log_to_clipboard)
10770         LogToClipboard();
10771 }
10772 
10773 
10774 //-----------------------------------------------------------------------------
10775 // [SECTION] SETTINGS
10776 //-----------------------------------------------------------------------------
10777 // - UpdateSettings() [Internal]
10778 // - MarkIniSettingsDirty() [Internal]
10779 // - CreateNewWindowSettings() [Internal]
10780 // - FindWindowSettings() [Internal]
10781 // - FindOrCreateWindowSettings() [Internal]
10782 // - FindSettingsHandler() [Internal]
10783 // - ClearIniSettings() [Internal]
10784 // - LoadIniSettingsFromDisk()
10785 // - LoadIniSettingsFromMemory()
10786 // - SaveIniSettingsToDisk()
10787 // - SaveIniSettingsToMemory()
10788 // - WindowSettingsHandler_***() [Internal]
10789 //-----------------------------------------------------------------------------
10790 
10791 // Called by NewFrame()
UpdateSettings()10792 void ImGui::UpdateSettings()
10793 {
10794     // Load settings on first frame (if not explicitly loaded manually before)
10795     ImGuiContext& g = *GImGui;
10796     if (!g.SettingsLoaded)
10797     {
10798         IM_ASSERT(g.SettingsWindows.empty());
10799         if (g.IO.IniFilename)
10800             LoadIniSettingsFromDisk(g.IO.IniFilename);
10801         g.SettingsLoaded = true;
10802     }
10803 
10804     // Save settings (with a delay after the last modification, so we don't spam disk too much)
10805     if (g.SettingsDirtyTimer > 0.0f)
10806     {
10807         g.SettingsDirtyTimer -= g.IO.DeltaTime;
10808         if (g.SettingsDirtyTimer <= 0.0f)
10809         {
10810             if (g.IO.IniFilename != NULL)
10811                 SaveIniSettingsToDisk(g.IO.IniFilename);
10812             else
10813                 g.IO.WantSaveIniSettings = true;  // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
10814             g.SettingsDirtyTimer = 0.0f;
10815         }
10816     }
10817 }
10818 
MarkIniSettingsDirty()10819 void ImGui::MarkIniSettingsDirty()
10820 {
10821     ImGuiContext& g = *GImGui;
10822     if (g.SettingsDirtyTimer <= 0.0f)
10823         g.SettingsDirtyTimer = g.IO.IniSavingRate;
10824 }
10825 
MarkIniSettingsDirty(ImGuiWindow * window)10826 void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
10827 {
10828     ImGuiContext& g = *GImGui;
10829     if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
10830         if (g.SettingsDirtyTimer <= 0.0f)
10831             g.SettingsDirtyTimer = g.IO.IniSavingRate;
10832 }
10833 
CreateNewWindowSettings(const char * name)10834 ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
10835 {
10836     ImGuiContext& g = *GImGui;
10837 
10838 #if !IMGUI_DEBUG_INI_SETTINGS
10839     // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
10840     // Preserve the full string when IMGUI_DEBUG_INI_SETTINGS is set to make .ini inspection easier.
10841     if (const char* p = strstr(name, "###"))
10842         name = p;
10843 #endif
10844     const size_t name_len = strlen(name);
10845 
10846     // Allocate chunk
10847     const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1;
10848     ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size);
10849     IM_PLACEMENT_NEW(settings) ImGuiWindowSettings();
10850     settings->ID = ImHashStr(name, name_len);
10851     memcpy(settings->GetName(), name, name_len + 1);   // Store with zero terminator
10852 
10853     return settings;
10854 }
10855 
FindWindowSettings(ImGuiID id)10856 ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
10857 {
10858     ImGuiContext& g = *GImGui;
10859     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
10860         if (settings->ID == id)
10861             return settings;
10862     return NULL;
10863 }
10864 
FindOrCreateWindowSettings(const char * name)10865 ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name)
10866 {
10867     if (ImGuiWindowSettings* settings = FindWindowSettings(ImHashStr(name)))
10868         return settings;
10869     return CreateNewWindowSettings(name);
10870 }
10871 
FindSettingsHandler(const char * type_name)10872 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
10873 {
10874     ImGuiContext& g = *GImGui;
10875     const ImGuiID type_hash = ImHashStr(type_name);
10876     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10877         if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
10878             return &g.SettingsHandlers[handler_n];
10879     return NULL;
10880 }
10881 
ClearIniSettings()10882 void ImGui::ClearIniSettings()
10883 {
10884     ImGuiContext& g = *GImGui;
10885     g.SettingsIniData.clear();
10886     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10887         if (g.SettingsHandlers[handler_n].ClearAllFn)
10888             g.SettingsHandlers[handler_n].ClearAllFn(&g, &g.SettingsHandlers[handler_n]);
10889 }
10890 
LoadIniSettingsFromDisk(const char * ini_filename)10891 void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
10892 {
10893     size_t file_data_size = 0;
10894     char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
10895     if (!file_data)
10896         return;
10897     LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
10898     IM_FREE(file_data);
10899 }
10900 
10901 // Zero-tolerance, no error reporting, cheap .ini parsing
LoadIniSettingsFromMemory(const char * ini_data,size_t ini_size)10902 void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
10903 {
10904     ImGuiContext& g = *GImGui;
10905     IM_ASSERT(g.Initialized);
10906     //IM_ASSERT(!g.WithinFrameScope && "Cannot be called between NewFrame() and EndFrame()");
10907     //IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
10908 
10909     // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
10910     // 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..
10911     if (ini_size == 0)
10912         ini_size = strlen(ini_data);
10913     g.SettingsIniData.Buf.resize((int)ini_size + 1);
10914     char* const buf = g.SettingsIniData.Buf.Data;
10915     char* const buf_end = buf + ini_size;
10916     memcpy(buf, ini_data, ini_size);
10917     buf_end[0] = 0;
10918 
10919     // Call pre-read handlers
10920     // Some types will clear their data (e.g. dock information) some types will allow merge/override (window)
10921     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10922         if (g.SettingsHandlers[handler_n].ReadInitFn)
10923             g.SettingsHandlers[handler_n].ReadInitFn(&g, &g.SettingsHandlers[handler_n]);
10924 
10925     void* entry_data = NULL;
10926     ImGuiSettingsHandler* entry_handler = NULL;
10927 
10928     char* line_end = NULL;
10929     for (char* line = buf; line < buf_end; line = line_end + 1)
10930     {
10931         // Skip new lines markers, then find end of the line
10932         while (*line == '\n' || *line == '\r')
10933             line++;
10934         line_end = line;
10935         while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
10936             line_end++;
10937         line_end[0] = 0;
10938         if (line[0] == ';')
10939             continue;
10940         if (line[0] == '[' && line_end > line && line_end[-1] == ']')
10941         {
10942             // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
10943             line_end[-1] = 0;
10944             const char* name_end = line_end - 1;
10945             const char* type_start = line + 1;
10946             char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']');
10947             const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
10948             if (!type_end || !name_start)
10949                 continue;
10950             *type_end = 0; // Overwrite first ']'
10951             name_start++;  // Skip second '['
10952             entry_handler = FindSettingsHandler(type_start);
10953             entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
10954         }
10955         else if (entry_handler != NULL && entry_data != NULL)
10956         {
10957             // Let type handler parse the line
10958             entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
10959         }
10960     }
10961     g.SettingsLoaded = true;
10962 
10963     // [DEBUG] Restore untouched copy so it can be browsed in Metrics (not strictly necessary)
10964     memcpy(buf, ini_data, ini_size);
10965 
10966     // Call post-read handlers
10967     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10968         if (g.SettingsHandlers[handler_n].ApplyAllFn)
10969             g.SettingsHandlers[handler_n].ApplyAllFn(&g, &g.SettingsHandlers[handler_n]);
10970 }
10971 
SaveIniSettingsToDisk(const char * ini_filename)10972 void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
10973 {
10974     ImGuiContext& g = *GImGui;
10975     g.SettingsDirtyTimer = 0.0f;
10976     if (!ini_filename)
10977         return;
10978 
10979     size_t ini_data_size = 0;
10980     const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
10981     ImFileHandle f = ImFileOpen(ini_filename, "wt");
10982     if (!f)
10983         return;
10984     ImFileWrite(ini_data, sizeof(char), ini_data_size, f);
10985     ImFileClose(f);
10986 }
10987 
10988 // Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
SaveIniSettingsToMemory(size_t * out_size)10989 const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
10990 {
10991     ImGuiContext& g = *GImGui;
10992     g.SettingsDirtyTimer = 0.0f;
10993     g.SettingsIniData.Buf.resize(0);
10994     g.SettingsIniData.Buf.push_back(0);
10995     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10996     {
10997         ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
10998         handler->WriteAllFn(&g, handler, &g.SettingsIniData);
10999     }
11000     if (out_size)
11001         *out_size = (size_t)g.SettingsIniData.size();
11002     return g.SettingsIniData.c_str();
11003 }
11004 
WindowSettingsHandler_ClearAll(ImGuiContext * ctx,ImGuiSettingsHandler *)11005 static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
11006 {
11007     ImGuiContext& g = *ctx;
11008     for (int i = 0; i != g.Windows.Size; i++)
11009         g.Windows[i]->SettingsOffset = -1;
11010     g.SettingsWindows.clear();
11011 }
11012 
WindowSettingsHandler_ReadOpen(ImGuiContext *,ImGuiSettingsHandler *,const char * name)11013 static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
11014 {
11015     ImGuiWindowSettings* settings = ImGui::FindOrCreateWindowSettings(name);
11016     ImGuiID id = settings->ID;
11017     *settings = ImGuiWindowSettings(); // Clear existing if recycling previous entry
11018     settings->ID = id;
11019     settings->WantApply = true;
11020     return (void*)settings;
11021 }
11022 
WindowSettingsHandler_ReadLine(ImGuiContext *,ImGuiSettingsHandler *,void * entry,const char * line)11023 static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
11024 {
11025     ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
11026     int x, y;
11027     int i;
11028     ImU32 u1;
11029     if (sscanf(line, "Pos=%i,%i", &x, &y) == 2)             { settings->Pos = ImVec2ih((short)x, (short)y); }
11030     else if (sscanf(line, "Size=%i,%i", &x, &y) == 2)       { settings->Size = ImVec2ih((short)x, (short)y); }
11031     else if (sscanf(line, "ViewportId=0x%08X", &u1) == 1)   { settings->ViewportId = u1; }
11032     else if (sscanf(line, "ViewportPos=%i,%i", &x, &y) == 2){ settings->ViewportPos = ImVec2ih((short)x, (short)y); }
11033     else if (sscanf(line, "Collapsed=%d", &i) == 1)         { settings->Collapsed = (i != 0); }
11034     else if (sscanf(line, "DockId=0x%X,%d", &u1, &i) == 2)  { settings->DockId = u1; settings->DockOrder = (short)i; }
11035     else if (sscanf(line, "DockId=0x%X", &u1) == 1)         { settings->DockId = u1; settings->DockOrder = -1; }
11036     else if (sscanf(line, "ClassId=0x%X", &u1) == 1)        { settings->ClassId = u1; }
11037 }
11038 
11039 // Apply to existing windows (if any)
WindowSettingsHandler_ApplyAll(ImGuiContext * ctx,ImGuiSettingsHandler *)11040 static void WindowSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
11041 {
11042     ImGuiContext& g = *ctx;
11043     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
11044         if (settings->WantApply)
11045         {
11046             if (ImGuiWindow* window = ImGui::FindWindowByID(settings->ID))
11047                 ApplyWindowSettings(window, settings);
11048             settings->WantApply = false;
11049         }
11050 }
11051 
WindowSettingsHandler_WriteAll(ImGuiContext * ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)11052 static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
11053 {
11054     // Gather data from windows that were active during this session
11055     // (if a window wasn't opened in this session we preserve its settings)
11056     ImGuiContext& g = *ctx;
11057     for (int i = 0; i != g.Windows.Size; i++)
11058     {
11059         ImGuiWindow* window = g.Windows[i];
11060         if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
11061             continue;
11062 
11063         ImGuiWindowSettings* settings = (window->SettingsOffset != -1) ? g.SettingsWindows.ptr_from_offset(window->SettingsOffset) : ImGui::FindWindowSettings(window->ID);
11064         if (!settings)
11065         {
11066             settings = ImGui::CreateNewWindowSettings(window->Name);
11067             window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
11068         }
11069         IM_ASSERT(settings->ID == window->ID);
11070         settings->Pos = ImVec2ih(window->Pos - window->ViewportPos);
11071         settings->Size = ImVec2ih(window->SizeFull);
11072         settings->ViewportId = window->ViewportId;
11073         settings->ViewportPos = ImVec2ih(window->ViewportPos);
11074         IM_ASSERT(window->DockNode == NULL || window->DockNode->ID == window->DockId);
11075         settings->DockId = window->DockId;
11076         settings->ClassId = window->WindowClass.ClassId;
11077         settings->DockOrder = window->DockOrder;
11078         settings->Collapsed = window->Collapsed;
11079     }
11080 
11081     // Write to text buffer
11082     buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve
11083     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
11084     {
11085         const char* settings_name = settings->GetName();
11086         buf->appendf("[%s][%s]\n", handler->TypeName, settings_name);
11087         if (settings->ViewportId != 0 && settings->ViewportId != ImGui::IMGUI_VIEWPORT_DEFAULT_ID)
11088         {
11089             buf->appendf("ViewportPos=%d,%d\n", settings->ViewportPos.x, settings->ViewportPos.y);
11090             buf->appendf("ViewportId=0x%08X\n", settings->ViewportId);
11091         }
11092         if (settings->Pos.x != 0 || settings->Pos.y != 0 || settings->ViewportId == ImGui::IMGUI_VIEWPORT_DEFAULT_ID)
11093             buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
11094         if (settings->Size.x != 0 || settings->Size.y != 0)
11095             buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
11096         buf->appendf("Collapsed=%d\n", settings->Collapsed);
11097         if (settings->DockId != 0)
11098         {
11099             // Write DockId as 4 digits if possible. Automatic DockId are small numbers, but full explicit DockSpace() are full ImGuiID range.
11100             if (settings->DockOrder == -1)
11101                 buf->appendf("DockId=0x%08X\n", settings->DockId);
11102             else
11103                 buf->appendf("DockId=0x%08X,%d\n", settings->DockId, settings->DockOrder);
11104             if (settings->ClassId != 0)
11105                 buf->appendf("ClassId=0x%08X\n", settings->ClassId);
11106         }
11107         buf->append("\n");
11108     }
11109 }
11110 
11111 
11112 //-----------------------------------------------------------------------------
11113 // [SECTION] VIEWPORTS, PLATFORM WINDOWS
11114 //-----------------------------------------------------------------------------
11115 // - GetMainViewport()
11116 // - FindViewportByID()
11117 // - FindViewportByPlatformHandle()
11118 // - SetCurrentViewport() [Internal]
11119 // - SetWindowViewport() [Internal]
11120 // - GetWindowAlwaysWantOwnViewport() [Internal]
11121 // - UpdateTryMergeWindowIntoHostViewport() [Internal]
11122 // - UpdateTryMergeWindowIntoHostViewports() [Internal]
11123 // - TranslateWindowsInViewport() [Internal]
11124 // - ScaleWindowsInViewport() [Internal]
11125 // - FindHoveredViewportFromPlatformWindowStack() [Internal]
11126 // - UpdateViewportsNewFrame() [Internal]
11127 // - UpdateViewportsEndFrame() [Internal]
11128 // - AddUpdateViewport() [Internal]
11129 // - UpdateSelectWindowViewport() [Internal]
11130 // - UpdatePlatformWindows()
11131 // - RenderPlatformWindowsDefault()
11132 // - FindPlatformMonitorForPos() [Internal]
11133 // - FindPlatformMonitorForRect() [Internal]
11134 // - UpdateViewportPlatformMonitor() [Internal]
11135 // - DestroyPlatformWindow() [Internal]
11136 // - DestroyPlatformWindows()
11137 //-----------------------------------------------------------------------------
11138 
GetMainViewport()11139 ImGuiViewport* ImGui::GetMainViewport()
11140 {
11141     ImGuiContext& g = *GImGui;
11142     return g.Viewports[0];
11143 }
11144 
FindViewportByID(ImGuiID id)11145 ImGuiViewport* ImGui::FindViewportByID(ImGuiID id)
11146 {
11147     ImGuiContext& g = *GImGui;
11148     for (int n = 0; n < g.Viewports.Size; n++)
11149         if (g.Viewports[n]->ID == id)
11150             return g.Viewports[n];
11151     return NULL;
11152 }
11153 
FindViewportByPlatformHandle(void * platform_handle)11154 ImGuiViewport* ImGui::FindViewportByPlatformHandle(void* platform_handle)
11155 {
11156     ImGuiContext& g = *GImGui;
11157     for (int i = 0; i != g.Viewports.Size; i++)
11158         if (g.Viewports[i]->PlatformHandle == platform_handle)
11159             return g.Viewports[i];
11160     return NULL;
11161 }
11162 
SetCurrentViewport(ImGuiWindow * current_window,ImGuiViewportP * viewport)11163 void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* viewport)
11164 {
11165     ImGuiContext& g = *GImGui;
11166     (void)current_window;
11167 
11168     if (viewport)
11169         viewport->LastFrameActive = g.FrameCount;
11170     if (g.CurrentViewport == viewport)
11171         return;
11172     g.CurrentDpiScale = viewport ? viewport->DpiScale : 1.0f;
11173     g.CurrentViewport = viewport;
11174     //IMGUI_DEBUG_LOG_VIEWPORT("SetCurrentViewport changed '%s' 0x%08X\n", current_window ? current_window->Name : NULL, viewport ? viewport->ID : 0);
11175 
11176     // Notify platform layer of viewport changes
11177     // FIXME-DPI: This is only currently used for experimenting with handling of multiple DPI
11178     if (g.CurrentViewport && g.PlatformIO.Platform_OnChangedViewport)
11179         g.PlatformIO.Platform_OnChangedViewport(g.CurrentViewport);
11180 }
11181 
SetWindowViewport(ImGuiWindow * window,ImGuiViewportP * viewport)11182 static void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport)
11183 {
11184     window->Viewport = viewport;
11185     window->ViewportId = viewport->ID;
11186     window->ViewportOwned = (viewport->Window == window);
11187 }
11188 
GetWindowAlwaysWantOwnViewport(ImGuiWindow * window)11189 static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window)
11190 {
11191     // Tooltips and menus are not automatically forced into their own viewport when the NoMerge flag is set, however the multiplication of viewports makes them more likely to protrude and create their own.
11192     ImGuiContext& g = *GImGui;
11193     if (g.IO.ConfigViewportsNoAutoMerge || (window->WindowClass.ViewportFlagsOverrideSet & ImGuiViewportFlags_NoAutoMerge))
11194         if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)
11195             if (!window->DockIsActive)
11196                 if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip)) == 0)
11197                     if ((window->Flags & ImGuiWindowFlags_Popup) == 0 || (window->Flags & ImGuiWindowFlags_Modal) != 0)
11198                         return true;
11199     return false;
11200 }
11201 
UpdateTryMergeWindowIntoHostViewport(ImGuiWindow * window,ImGuiViewportP * viewport)11202 static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport)
11203 {
11204     ImGuiContext& g = *GImGui;
11205     if (window->Viewport == viewport)
11206         return false;
11207     if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) == 0)
11208         return false;
11209     if ((viewport->Flags & ImGuiViewportFlags_Minimized) != 0)
11210         return false;
11211     if (!viewport->GetMainRect().Contains(window->Rect()))
11212         return false;
11213     if (GetWindowAlwaysWantOwnViewport(window))
11214         return false;
11215 
11216     for (int n = 0; n < g.Windows.Size; n++)
11217     {
11218         ImGuiWindow* window_behind = g.Windows[n];
11219         if (window_behind == window)
11220             break;
11221         if (window_behind->WasActive && window_behind->ViewportOwned && !(window_behind->Flags & ImGuiWindowFlags_ChildWindow))
11222             if (window_behind->Viewport->GetMainRect().Overlaps(window->Rect()))
11223                 return false;
11224     }
11225 
11226     // Move to the existing viewport, Move child/hosted windows as well (FIXME-OPT: iterate child)
11227     ImGuiViewportP* old_viewport = window->Viewport;
11228     if (window->ViewportOwned)
11229         for (int n = 0; n < g.Windows.Size; n++)
11230             if (g.Windows[n]->Viewport == old_viewport)
11231                 SetWindowViewport(g.Windows[n], viewport);
11232     SetWindowViewport(window, viewport);
11233     BringWindowToDisplayFront(window);
11234 
11235     return true;
11236 }
11237 
UpdateTryMergeWindowIntoHostViewports(ImGuiWindow * window)11238 static bool ImGui::UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window)
11239 {
11240     ImGuiContext& g = *GImGui;
11241     return UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]);
11242 }
11243 
11244 // Translate imgui windows when a Host Viewport has been moved
11245 // (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!)
TranslateWindowsInViewport(ImGuiViewportP * viewport,const ImVec2 & old_pos,const ImVec2 & new_pos)11246 void ImGui::TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos)
11247 {
11248     ImGuiContext& g = *GImGui;
11249     IM_ASSERT(viewport->Window == NULL && (viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows));
11250 
11251     // 1) We test if ImGuiConfigFlags_ViewportsEnable was just toggled, which allows us to conveniently
11252     // translate imgui windows from OS-window-local to absolute coordinates or vice-versa.
11253     // 2) If it's not going to fit into the new size, keep it at same absolute position.
11254     // One problem with this is that most Win32 applications doesn't update their render while dragging,
11255     // and so the window will appear to teleport when releasing the mouse.
11256     const bool translate_all_windows = (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) != (g.ConfigFlagsLastFrame & ImGuiConfigFlags_ViewportsEnable);
11257     ImRect test_still_fit_rect(old_pos, old_pos + viewport->Size);
11258     ImVec2 delta_pos = new_pos - old_pos;
11259     for (int window_n = 0; window_n < g.Windows.Size; window_n++) // FIXME-OPT
11260         if (translate_all_windows || (g.Windows[window_n]->Viewport == viewport && test_still_fit_rect.Contains(g.Windows[window_n]->Rect())))
11261             TranslateWindow(g.Windows[window_n], delta_pos);
11262 }
11263 
11264 // Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!)
ScaleWindowsInViewport(ImGuiViewportP * viewport,float scale)11265 void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale)
11266 {
11267     ImGuiContext& g = *GImGui;
11268     if (viewport->Window)
11269     {
11270         ScaleWindow(viewport->Window, scale);
11271     }
11272     else
11273     {
11274         for (int i = 0; i != g.Windows.Size; i++)
11275             if (g.Windows[i]->Viewport == viewport)
11276                 ScaleWindow(g.Windows[i], scale);
11277     }
11278 }
11279 
11280 // If the backend doesn't set MouseLastHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs, we do a search ourselves.
11281 // A) It won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window.
11282 // B) It requires Platform_GetWindowFocus to be implemented by backend.
FindHoveredViewportFromPlatformWindowStack(const ImVec2 mouse_platform_pos)11283 static ImGuiViewportP* FindHoveredViewportFromPlatformWindowStack(const ImVec2 mouse_platform_pos)
11284 {
11285     ImGuiContext& g = *GImGui;
11286     ImGuiViewportP* best_candidate = NULL;
11287     for (int n = 0; n < g.Viewports.Size; n++)
11288     {
11289         ImGuiViewportP* viewport = g.Viewports[n];
11290         if (!(viewport->Flags & (ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_Minimized)) && viewport->GetMainRect().Contains(mouse_platform_pos))
11291             if (best_candidate == NULL || best_candidate->LastFrontMostStampCount < viewport->LastFrontMostStampCount)
11292                 best_candidate = viewport;
11293     }
11294     return best_candidate;
11295 }
11296 
11297 // Update viewports and monitor infos
11298 // Note that this is running even if 'ImGuiConfigFlags_ViewportsEnable' is not set, in order to clear unused viewports (if any) and update monitor info.
UpdateViewportsNewFrame()11299 static void ImGui::UpdateViewportsNewFrame()
11300 {
11301     ImGuiContext& g = *GImGui;
11302     IM_ASSERT(g.PlatformIO.Viewports.Size <= g.Viewports.Size);
11303 
11304     // Update Minimized status (we need it first in order to decide if we'll apply Pos/Size of the main viewport)
11305     const bool viewports_enabled = (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) != 0;
11306     if (viewports_enabled)
11307     {
11308         for (int n = 0; n < g.Viewports.Size; n++)
11309         {
11310             ImGuiViewportP* viewport = g.Viewports[n];
11311             const bool platform_funcs_available = viewport->PlatformWindowCreated;
11312             if (g.PlatformIO.Platform_GetWindowMinimized && platform_funcs_available)
11313             {
11314                 bool minimized = g.PlatformIO.Platform_GetWindowMinimized(viewport);
11315                 if (minimized)
11316                     viewport->Flags |= ImGuiViewportFlags_Minimized;
11317                 else
11318                     viewport->Flags &= ~ImGuiViewportFlags_Minimized;
11319             }
11320         }
11321     }
11322 
11323     // Create/update main viewport with current platform position.
11324     // FIXME-VIEWPORT: Size is driven by backend/user code for backward-compatibility but we should aim to make this more consistent.
11325     ImGuiViewportP* main_viewport = g.Viewports[0];
11326     IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID);
11327     IM_ASSERT(main_viewport->Window == NULL);
11328     ImVec2 main_viewport_pos = viewports_enabled ? g.PlatformIO.Platform_GetWindowPos(main_viewport) : ImVec2(0.0f, 0.0f);
11329     ImVec2 main_viewport_size = g.IO.DisplaySize;
11330     if (viewports_enabled && (main_viewport->Flags & ImGuiViewportFlags_Minimized))
11331     {
11332         main_viewport_pos = main_viewport->Pos;    // Preserve last pos/size when minimized (FIXME: We don't do the same for Size outside of the viewport path)
11333         main_viewport_size = main_viewport->Size;
11334     }
11335     AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_pos, main_viewport_size, ImGuiViewportFlags_CanHostOtherWindows);
11336 
11337     g.CurrentDpiScale = 0.0f;
11338     g.CurrentViewport = NULL;
11339     g.MouseViewport = NULL;
11340     for (int n = 0; n < g.Viewports.Size; n++)
11341     {
11342         ImGuiViewportP* viewport = g.Viewports[n];
11343         viewport->Idx = n;
11344 
11345         // Erase unused viewports
11346         if (n > 0 && viewport->LastFrameActive < g.FrameCount - 2)
11347         {
11348             // Clear references to this viewport in windows (window->ViewportId becomes the master data)
11349             for (int window_n = 0; window_n < g.Windows.Size; window_n++)
11350                 if (g.Windows[window_n]->Viewport == viewport)
11351                 {
11352                     g.Windows[window_n]->Viewport = NULL;
11353                     g.Windows[window_n]->ViewportOwned = false;
11354                 }
11355             if (viewport == g.MouseLastHoveredViewport)
11356                 g.MouseLastHoveredViewport = NULL;
11357             g.Viewports.erase(g.Viewports.Data + n);
11358 
11359             // Destroy
11360             IMGUI_DEBUG_LOG_VIEWPORT("Delete Viewport %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a");
11361             DestroyPlatformWindow(viewport); // In most circumstances the platform window will already be destroyed here.
11362             IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false);
11363             IM_DELETE(viewport);
11364             n--;
11365             continue;
11366         }
11367 
11368         const bool platform_funcs_available = viewport->PlatformWindowCreated;
11369         if (viewports_enabled)
11370         {
11371             // Update Position and Size (from Platform Window to ImGui) if requested.
11372             // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities.
11373             if (!(viewport->Flags & ImGuiViewportFlags_Minimized) && platform_funcs_available)
11374             {
11375                 if (viewport->PlatformRequestMove)
11376                     viewport->Pos = viewport->LastPlatformPos = g.PlatformIO.Platform_GetWindowPos(viewport);
11377                 if (viewport->PlatformRequestResize)
11378                     viewport->Size = viewport->LastPlatformSize = g.PlatformIO.Platform_GetWindowSize(viewport);
11379             }
11380         }
11381 
11382         // Update/copy monitor info
11383         UpdateViewportPlatformMonitor(viewport);
11384 
11385         // Lock down space taken by menu bars and status bars, reset the offset for fucntions like BeginMainMenuBar() to alter them again.
11386         viewport->WorkOffsetMin = viewport->CurrWorkOffsetMin;
11387         viewport->WorkOffsetMax = viewport->CurrWorkOffsetMax;
11388         viewport->CurrWorkOffsetMin = viewport->CurrWorkOffsetMax = ImVec2(0.0f, 0.0f);
11389 
11390         // Reset alpha every frame. Users of transparency (docking) needs to request a lower alpha back.
11391         viewport->Alpha = 1.0f;
11392 
11393         // Translate imgui windows when a Host Viewport has been moved
11394         // (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!)
11395         const ImVec2 viewport_delta_pos = viewport->Pos - viewport->LastPos;
11396         if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (viewport_delta_pos.x != 0.0f || viewport_delta_pos.y != 0.0f))
11397             TranslateWindowsInViewport(viewport, viewport->LastPos, viewport->Pos);
11398 
11399         // Update DPI scale
11400         float new_dpi_scale;
11401         if (g.PlatformIO.Platform_GetWindowDpiScale && platform_funcs_available)
11402             new_dpi_scale = g.PlatformIO.Platform_GetWindowDpiScale(viewport);
11403         else if (viewport->PlatformMonitor != -1)
11404             new_dpi_scale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale;
11405         else
11406             new_dpi_scale = (viewport->DpiScale != 0.0f) ? viewport->DpiScale : 1.0f;
11407         if (viewport->DpiScale != 0.0f && new_dpi_scale != viewport->DpiScale)
11408         {
11409             float scale_factor = new_dpi_scale / viewport->DpiScale;
11410             if (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports)
11411                 ScaleWindowsInViewport(viewport, scale_factor);
11412             //if (viewport == GetMainViewport())
11413             //    g.PlatformInterface.SetWindowSize(viewport, viewport->Size * scale_factor);
11414 
11415             // Scale our window moving pivot so that the window will rescale roughly around the mouse position.
11416             // FIXME-VIEWPORT: This currently creates a resizing feedback loop when a window is straddling a DPI transition border.
11417             // (Minor: since our sizes do not perfectly linearly scale, deferring the click offset scale until we know the actual window scale ratio may get us slightly more precise mouse positioning.)
11418             //if (g.MovingWindow != NULL && g.MovingWindow->Viewport == viewport)
11419             //    g.ActiveIdClickOffset = ImFloor(g.ActiveIdClickOffset * scale_factor);
11420         }
11421         viewport->DpiScale = new_dpi_scale;
11422     }
11423 
11424     if (!viewports_enabled)
11425     {
11426         g.MouseViewport = main_viewport;
11427         return;
11428     }
11429 
11430     // Mouse handling: decide on the actual mouse viewport for this frame between the active/focused viewport and the hovered viewport.
11431     // Note that 'viewport_hovered' should skip over any viewport that has the ImGuiViewportFlags_NoInputs flags set.
11432     ImGuiViewportP* viewport_hovered = NULL;
11433     if (g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport)
11434     {
11435         viewport_hovered = g.IO.MouseHoveredViewport ? (ImGuiViewportP*)FindViewportByID(g.IO.MouseHoveredViewport) : NULL;
11436         if (viewport_hovered && (viewport_hovered->Flags & ImGuiViewportFlags_NoInputs))
11437         {
11438             // Backend failed at honoring its contract if it returned a viewport with the _NoInputs flag.
11439             IM_ASSERT(0);
11440             viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos);
11441         }
11442     }
11443     else
11444     {
11445         // If the backend doesn't know how to honor ImGuiViewportFlags_NoInputs, we do a search ourselves. Note that this search:
11446         // A) won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window.
11447         // B) uses LastFrameAsRefViewport as a flawed replacement for the last time a window was focused (we could/should fix that by introducing Focus functions in PlatformIO)
11448         viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos);
11449     }
11450     if (viewport_hovered != NULL)
11451         g.MouseLastHoveredViewport = viewport_hovered;
11452     else if (g.MouseLastHoveredViewport == NULL)
11453         g.MouseLastHoveredViewport = g.Viewports[0];
11454 
11455     // Update mouse reference viewport
11456     // (when moving a window we aim at its viewport, but this will be overwritten below if we go in drag and drop mode)
11457     if (g.MovingWindow)
11458         g.MouseViewport = g.MovingWindow->Viewport;
11459     else
11460         g.MouseViewport = g.MouseLastHoveredViewport;
11461 
11462     // When dragging something, always refer to the last hovered viewport.
11463     // - when releasing a moving window we will revert to aiming behind (at viewport_hovered)
11464     // - when we are between viewports, our dragged preview will tend to show in the last viewport _even_ if we don't have tooltips in their viewports (when lacking monitor info)
11465     // - consider the case of holding on a menu item to browse child menus: even thou a mouse button is held, there's no active id because menu items only react on mouse release.
11466     const bool is_mouse_dragging_with_an_expected_destination = g.DragDropActive;
11467     if (is_mouse_dragging_with_an_expected_destination && viewport_hovered == NULL)
11468         viewport_hovered = g.MouseLastHoveredViewport;
11469     if (is_mouse_dragging_with_an_expected_destination || g.ActiveId == 0 || !IsAnyMouseDown())
11470         if (viewport_hovered != NULL && viewport_hovered != g.MouseViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs))
11471             g.MouseViewport = viewport_hovered;
11472 
11473     IM_ASSERT(g.MouseViewport != NULL);
11474 }
11475 
11476 // Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some)
UpdateViewportsEndFrame()11477 static void ImGui::UpdateViewportsEndFrame()
11478 {
11479     ImGuiContext& g = *GImGui;
11480     g.PlatformIO.MainViewport = g.Viewports[0];
11481     g.PlatformIO.Viewports.resize(0);
11482     for (int i = 0; i < g.Viewports.Size; i++)
11483     {
11484         ImGuiViewportP* viewport = g.Viewports[i];
11485         viewport->LastPos = viewport->Pos;
11486         if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0.0f || viewport->Size.y <= 0.0f)
11487             if (i > 0) // Always include main viewport in the list
11488                 continue;
11489         if (viewport->Window && !IsWindowActiveAndVisible(viewport->Window))
11490             continue;
11491         if (i > 0)
11492             IM_ASSERT(viewport->Window != NULL);
11493         g.PlatformIO.Viewports.push_back(viewport);
11494     }
11495     g.Viewports[0]->ClearRequestFlags(); // Clear main viewport flags because UpdatePlatformWindows() won't do it and may not even be called
11496 }
11497 
11498 // FIXME: We should ideally refactor the system to call this every frame (we currently don't)
AddUpdateViewport(ImGuiWindow * window,ImGuiID id,const ImVec2 & pos,const ImVec2 & size,ImGuiViewportFlags flags)11499 ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& pos, const ImVec2& size, ImGuiViewportFlags flags)
11500 {
11501     ImGuiContext& g = *GImGui;
11502     IM_ASSERT(id != 0);
11503 
11504     if (window != NULL)
11505     {
11506         if (g.MovingWindow && g.MovingWindow->RootWindow == window)
11507             flags |= ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_NoFocusOnAppearing;
11508         if ((window->Flags & ImGuiWindowFlags_NoMouseInputs) && (window->Flags & ImGuiWindowFlags_NoNavInputs))
11509             flags |= ImGuiViewportFlags_NoInputs;
11510         if (window->Flags & ImGuiWindowFlags_NoFocusOnAppearing)
11511             flags |= ImGuiViewportFlags_NoFocusOnAppearing;
11512     }
11513 
11514     ImGuiViewportP* viewport = (ImGuiViewportP*)FindViewportByID(id);
11515     if (viewport)
11516     {
11517         if (!viewport->PlatformRequestMove)
11518             viewport->Pos = pos;
11519         if (!viewport->PlatformRequestResize)
11520             viewport->Size = size;
11521         viewport->Flags = flags | (viewport->Flags & ImGuiViewportFlags_Minimized); // Preserve existing flags
11522     }
11523     else
11524     {
11525         // New viewport
11526         viewport = IM_NEW(ImGuiViewportP)();
11527         viewport->ID = id;
11528         viewport->Idx = g.Viewports.Size;
11529         viewport->Pos = viewport->LastPos = pos;
11530         viewport->Size = size;
11531         viewport->Flags = flags;
11532         UpdateViewportPlatformMonitor(viewport);
11533         g.Viewports.push_back(viewport);
11534         IMGUI_DEBUG_LOG_VIEWPORT("Add Viewport %08X (%s)\n", id, window->Name);
11535 
11536         // We normally setup for all viewports in NewFrame() but here need to handle the mid-frame creation of a new viewport.
11537         // We need to extend the fullscreen clip rect so the OverlayDrawList clip is correct for that the first frame
11538         g.DrawListSharedData.ClipRectFullscreen.x = ImMin(g.DrawListSharedData.ClipRectFullscreen.x, viewport->Pos.x);
11539         g.DrawListSharedData.ClipRectFullscreen.y = ImMin(g.DrawListSharedData.ClipRectFullscreen.y, viewport->Pos.y);
11540         g.DrawListSharedData.ClipRectFullscreen.z = ImMax(g.DrawListSharedData.ClipRectFullscreen.z, viewport->Pos.x + viewport->Size.x);
11541         g.DrawListSharedData.ClipRectFullscreen.w = ImMax(g.DrawListSharedData.ClipRectFullscreen.w, viewport->Pos.y + viewport->Size.y);
11542 
11543         // Store initial DpiScale before the OS platform window creation, based on expected monitor data.
11544         // This is so we can select an appropriate font size on the first frame of our window lifetime
11545         if (viewport->PlatformMonitor != -1)
11546             viewport->DpiScale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale;
11547     }
11548 
11549     viewport->Window = window;
11550     viewport->LastFrameActive = g.FrameCount;
11551     IM_ASSERT(window == NULL || viewport->ID == window->ID);
11552 
11553     if (window != NULL)
11554         window->ViewportOwned = true;
11555 
11556     return viewport;
11557 }
11558 
11559 // FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten.
UpdateSelectWindowViewport(ImGuiWindow * window)11560 static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window)
11561 {
11562     ImGuiContext& g = *GImGui;
11563     ImGuiWindowFlags flags = window->Flags;
11564     window->ViewportAllowPlatformMonitorExtend = -1;
11565 
11566     // Restore main viewport if multi-viewport is not supported by the backend
11567     ImGuiViewportP* main_viewport = g.Viewports[0];
11568     if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable))
11569     {
11570         SetWindowViewport(window, main_viewport);
11571         return;
11572     }
11573     window->ViewportOwned = false;
11574 
11575     // Appearing popups reset their viewport so they can inherit again
11576     if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && window->Appearing)
11577     {
11578         window->Viewport = NULL;
11579         window->ViewportId = 0;
11580     }
11581 
11582     if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasViewport) == 0)
11583     {
11584         // By default inherit from parent window
11585         if (window->Viewport == NULL && window->ParentWindow && !window->ParentWindow->IsFallbackWindow)
11586             window->Viewport = window->ParentWindow->Viewport;
11587 
11588         // Attempt to restore saved viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportPos' restored from .ini file
11589         if (window->Viewport == NULL && window->ViewportId != 0)
11590         {
11591             window->Viewport = (ImGuiViewportP*)FindViewportByID(window->ViewportId);
11592             if (window->Viewport == NULL && window->ViewportPos.x != FLT_MAX && window->ViewportPos.y != FLT_MAX)
11593                 window->Viewport = AddUpdateViewport(window, window->ID, window->ViewportPos, window->Size, ImGuiViewportFlags_None);
11594         }
11595     }
11596 
11597     bool lock_viewport = false;
11598     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasViewport)
11599     {
11600         // Code explicitly request a viewport
11601         window->Viewport = (ImGuiViewportP*)FindViewportByID(g.NextWindowData.ViewportId);
11602         window->ViewportId = g.NextWindowData.ViewportId; // Store ID even if Viewport isn't resolved yet.
11603         lock_viewport = true;
11604     }
11605     else if ((flags & ImGuiWindowFlags_ChildWindow) || (flags & ImGuiWindowFlags_ChildMenu))
11606     {
11607         // Always inherit viewport from parent window
11608         window->Viewport = window->ParentWindow->Viewport;
11609     }
11610     else if (flags & ImGuiWindowFlags_Tooltip)
11611     {
11612         window->Viewport = g.MouseViewport;
11613     }
11614     else if (GetWindowAlwaysWantOwnViewport(window))
11615     {
11616         window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None);
11617     }
11618     else if (g.MovingWindow && g.MovingWindow->RootWindow == window && IsMousePosValid())
11619     {
11620         if (window->Viewport != NULL && window->Viewport->Window == window)
11621             window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None);
11622     }
11623     else
11624     {
11625         // Merge into host viewport?
11626         // We cannot test window->ViewportOwned as it set lower in the function.
11627         bool try_to_merge_into_host_viewport = (window->Viewport && window == window->Viewport->Window && g.ActiveId == 0);
11628         if (try_to_merge_into_host_viewport)
11629             UpdateTryMergeWindowIntoHostViewports(window);
11630     }
11631 
11632     // Fallback: merge in default viewport if z-order matches, otherwise create a new viewport
11633     if (window->Viewport == NULL)
11634         if (!UpdateTryMergeWindowIntoHostViewport(window, main_viewport))
11635             window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None);
11636 
11637     // Mark window as allowed to protrude outside of its viewport and into the current monitor
11638     if (!lock_viewport)
11639     {
11640         if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
11641         {
11642             // We need to take account of the possibility that mouse may become invalid.
11643             // Popups/Tooltip always set ViewportAllowPlatformMonitorExtend so GetWindowAllowedExtentRect() will return full monitor bounds.
11644             ImVec2 mouse_ref = (flags & ImGuiWindowFlags_Tooltip) ? g.IO.MousePos : g.BeginPopupStack.back().OpenMousePos;
11645             bool use_mouse_ref = (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow);
11646             bool mouse_valid = IsMousePosValid(&mouse_ref);
11647             if ((window->Appearing || (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_ChildMenu))) && (!use_mouse_ref || mouse_valid))
11648                 window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos((use_mouse_ref && mouse_valid) ? mouse_ref : NavCalcPreferredRefPos());
11649             else
11650                 window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor;
11651         }
11652         else if (window->Viewport && window != window->Viewport->Window && window->Viewport->Window && !(flags & ImGuiWindowFlags_ChildWindow))
11653         {
11654             // When called from Begin() we don't have access to a proper version of the Hidden flag yet, so we replicate this code.
11655             const bool will_be_visible = (window->DockIsActive && !window->DockTabIsVisible) ? false : true;
11656             if ((window->Flags & ImGuiWindowFlags_DockNodeHost) && window->Viewport->LastFrameActive < g.FrameCount && will_be_visible)
11657             {
11658                 // Steal/transfer ownership
11659                 IMGUI_DEBUG_LOG_VIEWPORT("Window '%s' steal Viewport %08X from Window '%s'\n", window->Name, window->Viewport->ID, window->Viewport->Window->Name);
11660                 window->Viewport->Window = window;
11661                 window->Viewport->ID = window->ID;
11662                 window->Viewport->LastNameHash = 0;
11663             }
11664             else if (!UpdateTryMergeWindowIntoHostViewports(window)) // Merge?
11665             {
11666                 // New viewport
11667                 window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing);
11668             }
11669         }
11670         else if (window->ViewportAllowPlatformMonitorExtend < 0 && (flags & ImGuiWindowFlags_ChildWindow) == 0)
11671         {
11672             // Regular (non-child, non-popup) windows by default are also allowed to protrude
11673             // Child windows are kept contained within their parent.
11674             window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor;
11675         }
11676     }
11677 
11678     // Update flags
11679     window->ViewportOwned = (window == window->Viewport->Window);
11680     window->ViewportId = window->Viewport->ID;
11681 
11682     // If the OS window has a title bar, hide our imgui title bar
11683     //if (window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration))
11684     //    window->Flags |= ImGuiWindowFlags_NoTitleBar;
11685 }
11686 
11687 // Called by user at the end of the main loop, after EndFrame()
11688 // This will handle the creation/update of all OS windows via function defined in the ImGuiPlatformIO api.
UpdatePlatformWindows()11689 void ImGui::UpdatePlatformWindows()
11690 {
11691     ImGuiContext& g = *GImGui;
11692     IM_ASSERT(g.FrameCountEnded == g.FrameCount && "Forgot to call Render() or EndFrame() before UpdatePlatformWindows()?");
11693     IM_ASSERT(g.FrameCountPlatformEnded < g.FrameCount);
11694     g.FrameCountPlatformEnded = g.FrameCount;
11695     if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable))
11696         return;
11697 
11698     // Create/resize/destroy platform windows to match each active viewport.
11699     // Skip the main viewport (index 0), which is always fully handled by the application!
11700     for (int i = 1; i < g.Viewports.Size; i++)
11701     {
11702         ImGuiViewportP* viewport = g.Viewports[i];
11703 
11704         // Destroy platform window if the viewport hasn't been submitted or if it is hosting a hidden window
11705         // (the implicit/fallback Debug##Default window will be registering its viewport then be disabled, causing a dummy DestroyPlatformWindow to be made each frame)
11706         bool destroy_platform_window = false;
11707         destroy_platform_window |= (viewport->LastFrameActive < g.FrameCount - 1);
11708         destroy_platform_window |= (viewport->Window && !IsWindowActiveAndVisible(viewport->Window));
11709         if (destroy_platform_window)
11710         {
11711             DestroyPlatformWindow(viewport);
11712             continue;
11713         }
11714 
11715         // New windows that appears directly in a new viewport won't always have a size on their first frame
11716         if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0 || viewport->Size.y <= 0)
11717             continue;
11718 
11719         // Create window
11720         bool is_new_platform_window = (viewport->PlatformWindowCreated == false);
11721         if (is_new_platform_window)
11722         {
11723             IMGUI_DEBUG_LOG_VIEWPORT("Create Platform Window %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a");
11724             g.PlatformIO.Platform_CreateWindow(viewport);
11725             if (g.PlatformIO.Renderer_CreateWindow != NULL)
11726                 g.PlatformIO.Renderer_CreateWindow(viewport);
11727             viewport->LastNameHash = 0;
11728             viewport->LastPlatformPos = viewport->LastPlatformSize = ImVec2(FLT_MAX, FLT_MAX); // By clearing those we'll enforce a call to Platform_SetWindowPos/Size below, before Platform_ShowWindow (FIXME: Is that necessary?)
11729             viewport->LastRendererSize = viewport->Size;                                       // We don't need to call Renderer_SetWindowSize() as it is expected Renderer_CreateWindow() already did it.
11730             viewport->PlatformWindowCreated = true;
11731         }
11732 
11733         // Apply Position and Size (from ImGui to Platform/Renderer backends)
11734         if ((viewport->LastPlatformPos.x != viewport->Pos.x || viewport->LastPlatformPos.y != viewport->Pos.y) && !viewport->PlatformRequestMove)
11735             g.PlatformIO.Platform_SetWindowPos(viewport, viewport->Pos);
11736         if ((viewport->LastPlatformSize.x != viewport->Size.x || viewport->LastPlatformSize.y != viewport->Size.y) && !viewport->PlatformRequestResize)
11737             g.PlatformIO.Platform_SetWindowSize(viewport, viewport->Size);
11738         if ((viewport->LastRendererSize.x != viewport->Size.x || viewport->LastRendererSize.y != viewport->Size.y) && g.PlatformIO.Renderer_SetWindowSize)
11739             g.PlatformIO.Renderer_SetWindowSize(viewport, viewport->Size);
11740         viewport->LastPlatformPos = viewport->Pos;
11741         viewport->LastPlatformSize = viewport->LastRendererSize = viewport->Size;
11742 
11743         // Update title bar (if it changed)
11744         if (ImGuiWindow* window_for_title = GetWindowForTitleDisplay(viewport->Window))
11745         {
11746             const char* title_begin = window_for_title->Name;
11747             char* title_end = (char*)(intptr_t)FindRenderedTextEnd(title_begin);
11748             const ImGuiID title_hash = ImHashStr(title_begin, title_end - title_begin);
11749             if (viewport->LastNameHash != title_hash)
11750             {
11751                 char title_end_backup_c = *title_end;
11752                 *title_end = 0; // Cut existing buffer short instead of doing an alloc/free, no small gain.
11753                 g.PlatformIO.Platform_SetWindowTitle(viewport, title_begin);
11754                 *title_end = title_end_backup_c;
11755                 viewport->LastNameHash = title_hash;
11756             }
11757         }
11758 
11759         // Update alpha (if it changed)
11760         if (viewport->LastAlpha != viewport->Alpha && g.PlatformIO.Platform_SetWindowAlpha)
11761             g.PlatformIO.Platform_SetWindowAlpha(viewport, viewport->Alpha);
11762         viewport->LastAlpha = viewport->Alpha;
11763 
11764         // Optional, general purpose call to allow the backend to perform general book-keeping even if things haven't changed.
11765         if (g.PlatformIO.Platform_UpdateWindow)
11766             g.PlatformIO.Platform_UpdateWindow(viewport);
11767 
11768         if (is_new_platform_window)
11769         {
11770             // On startup ensure new platform window don't steal focus (give it a few frames, as nested contents may lead to viewport being created a few frames late)
11771             if (g.FrameCount < 3)
11772                 viewport->Flags |= ImGuiViewportFlags_NoFocusOnAppearing;
11773 
11774             // Show window
11775             g.PlatformIO.Platform_ShowWindow(viewport);
11776 
11777             // Even without focus, we assume the window becomes front-most.
11778             // This is useful for our platform z-order heuristic when io.MouseHoveredViewport is not available.
11779             if (viewport->LastFrontMostStampCount != g.ViewportFrontMostStampCount)
11780                 viewport->LastFrontMostStampCount = ++g.ViewportFrontMostStampCount;
11781         }
11782 
11783         // Clear request flags
11784         viewport->ClearRequestFlags();
11785     }
11786 
11787     // Update our implicit z-order knowledge of platform windows, which is used when the backend cannot provide io.MouseHoveredViewport.
11788     // When setting Platform_GetWindowFocus, it is expected that the platform backend can handle calls without crashing if it doesn't have data stored.
11789     // FIXME-VIEWPORT: We should use this information to also set dear imgui-side focus, allowing us to handle os-level alt+tab.
11790     if (g.PlatformIO.Platform_GetWindowFocus != NULL)
11791     {
11792         ImGuiViewportP* focused_viewport = NULL;
11793         for (int n = 0; n < g.Viewports.Size && focused_viewport == NULL; n++)
11794         {
11795             ImGuiViewportP* viewport = g.Viewports[n];
11796             if (viewport->PlatformWindowCreated)
11797                 if (g.PlatformIO.Platform_GetWindowFocus(viewport))
11798                     focused_viewport = viewport;
11799         }
11800 
11801         // Store a tag so we can infer z-order easily from all our windows
11802         if (focused_viewport && focused_viewport->LastFrontMostStampCount != g.ViewportFrontMostStampCount)
11803             focused_viewport->LastFrontMostStampCount = ++g.ViewportFrontMostStampCount;
11804     }
11805 }
11806 
11807 // This is a default/basic function for performing the rendering/swap of multiple Platform Windows.
11808 // Custom renderers may prefer to not call this function at all, and instead iterate the publicly exposed platform data and handle rendering/sync themselves.
11809 // The Render/Swap functions stored in ImGuiPlatformIO are merely here to allow for this helper to exist, but you can do it yourself:
11810 //
11811 //    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
11812 //    for (int i = 1; i < platform_io.Viewports.Size; i++)
11813 //        if ((platform_io.Viewports[i]->Flags & ImGuiViewportFlags_Minimized) == 0)
11814 //            MyRenderFunction(platform_io.Viewports[i], my_args);
11815 //    for (int i = 1; i < platform_io.Viewports.Size; i++)
11816 //        if ((platform_io.Viewports[i]->Flags & ImGuiViewportFlags_Minimized) == 0)
11817 //            MySwapBufferFunction(platform_io.Viewports[i], my_args);
11818 //
RenderPlatformWindowsDefault(void * platform_render_arg,void * renderer_render_arg)11819 void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* renderer_render_arg)
11820 {
11821     // Skip the main viewport (index 0), which is always fully handled by the application!
11822     ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
11823     for (int i = 1; i < platform_io.Viewports.Size; i++)
11824     {
11825         ImGuiViewport* viewport = platform_io.Viewports[i];
11826         if (viewport->Flags & ImGuiViewportFlags_Minimized)
11827             continue;
11828         if (platform_io.Platform_RenderWindow) platform_io.Platform_RenderWindow(viewport, platform_render_arg);
11829         if (platform_io.Renderer_RenderWindow) platform_io.Renderer_RenderWindow(viewport, renderer_render_arg);
11830     }
11831     for (int i = 1; i < platform_io.Viewports.Size; i++)
11832     {
11833         ImGuiViewport* viewport = platform_io.Viewports[i];
11834         if (viewport->Flags & ImGuiViewportFlags_Minimized)
11835             continue;
11836         if (platform_io.Platform_SwapBuffers) platform_io.Platform_SwapBuffers(viewport, platform_render_arg);
11837         if (platform_io.Renderer_SwapBuffers) platform_io.Renderer_SwapBuffers(viewport, renderer_render_arg);
11838     }
11839 }
11840 
FindPlatformMonitorForPos(const ImVec2 & pos)11841 static int ImGui::FindPlatformMonitorForPos(const ImVec2& pos)
11842 {
11843     ImGuiContext& g = *GImGui;
11844     for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++)
11845     {
11846         const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n];
11847         if (ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize).Contains(pos))
11848             return monitor_n;
11849     }
11850     return -1;
11851 }
11852 
11853 // Search for the monitor with the largest intersection area with the given rectangle
11854 // We generally try to avoid searching loops but the monitor count should be very small here
11855 // FIXME-OPT: We could test the last monitor used for that viewport first, and early
FindPlatformMonitorForRect(const ImRect & rect)11856 static int ImGui::FindPlatformMonitorForRect(const ImRect& rect)
11857 {
11858     ImGuiContext& g = *GImGui;
11859 
11860     const int monitor_count = g.PlatformIO.Monitors.Size;
11861     if (monitor_count <= 1)
11862         return monitor_count - 1;
11863 
11864     // Use a minimum threshold of 1.0f so a zero-sized rect won't false positive, and will still find the correct monitor given its position.
11865     // This is necessary for tooltips which always resize down to zero at first.
11866     const float surface_threshold = ImMax(rect.GetWidth() * rect.GetHeight() * 0.5f, 1.0f);
11867     int best_monitor_n = -1;
11868     float best_monitor_surface = 0.001f;
11869 
11870     for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size && best_monitor_surface < surface_threshold; monitor_n++)
11871     {
11872         const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n];
11873         const ImRect monitor_rect = ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize);
11874         if (monitor_rect.Contains(rect))
11875             return monitor_n;
11876         ImRect overlapping_rect = rect;
11877         overlapping_rect.ClipWithFull(monitor_rect);
11878         float overlapping_surface = overlapping_rect.GetWidth() * overlapping_rect.GetHeight();
11879         if (overlapping_surface < best_monitor_surface)
11880             continue;
11881         best_monitor_surface = overlapping_surface;
11882         best_monitor_n = monitor_n;
11883     }
11884     return best_monitor_n;
11885 }
11886 
11887 // Update monitor from viewport rectangle (we'll use this info to clamp windows and save windows lost in a removed monitor)
UpdateViewportPlatformMonitor(ImGuiViewportP * viewport)11888 static void ImGui::UpdateViewportPlatformMonitor(ImGuiViewportP* viewport)
11889 {
11890     viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetMainRect());
11891 }
11892 
DestroyPlatformWindow(ImGuiViewportP * viewport)11893 void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport)
11894 {
11895     ImGuiContext& g = *GImGui;
11896     if (viewport->PlatformWindowCreated)
11897     {
11898         if (g.PlatformIO.Renderer_DestroyWindow)
11899             g.PlatformIO.Renderer_DestroyWindow(viewport);
11900         if (g.PlatformIO.Platform_DestroyWindow)
11901             g.PlatformIO.Platform_DestroyWindow(viewport);
11902         IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL);
11903 
11904         // Don't clear PlatformWindowCreated for the main viewport, as we initially set that up to true in Initialize()
11905         // The righter way may be to leave it to the backend to set this flag all-together, and made the flag public.
11906         if (viewport->ID != IMGUI_VIEWPORT_DEFAULT_ID)
11907             viewport->PlatformWindowCreated = false;
11908     }
11909     else
11910     {
11911         IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL);
11912     }
11913     viewport->RendererUserData = viewport->PlatformUserData = viewport->PlatformHandle = NULL;
11914     viewport->ClearRequestFlags();
11915 }
11916 
DestroyPlatformWindows()11917 void ImGui::DestroyPlatformWindows()
11918 {
11919     // We call the destroy window on every viewport (including the main viewport, index 0) to give a chance to the backend
11920     // to clear any data they may have stored in e.g. PlatformUserData, RendererUserData.
11921     // It is convenient for the platform backend code to store something in the main viewport, in order for e.g. the mouse handling
11922     // code to operator a consistent manner.
11923     // It is expected that the backend can handle calls to Renderer_DestroyWindow/Platform_DestroyWindow without
11924     // crashing if it doesn't have data stored.
11925     ImGuiContext& g = *GImGui;
11926     for (int i = 0; i < g.Viewports.Size; i++)
11927         DestroyPlatformWindow(g.Viewports[i]);
11928 }
11929 
11930 
11931 //-----------------------------------------------------------------------------
11932 // [SECTION] DOCKING
11933 //-----------------------------------------------------------------------------
11934 // Docking: Internal Types
11935 // Docking: Forward Declarations
11936 // Docking: ImGuiDockContext
11937 // Docking: ImGuiDockContext Docking/Undocking functions
11938 // Docking: ImGuiDockNode
11939 // Docking: ImGuiDockNode Tree manipulation functions
11940 // Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport)
11941 // Docking: Builder Functions
11942 // Docking: Begin/End Support Functions (called from Begin/End)
11943 // Docking: Settings
11944 //-----------------------------------------------------------------------------
11945 
11946 //-----------------------------------------------------------------------------
11947 // Typical Docking call flow: (root level is generally public API):
11948 //-----------------------------------------------------------------------------
11949 // - NewFrame()                               new dear imgui frame
11950 //    | DockContextNewFrameUpdateUndocking()  - process queued undocking requests
11951 //    | - DockContextProcessUndockWindow()    - process one window undocking request
11952 //    | - DockContextProcessUndockNode()      - process one whole node undocking request
11953 //    | DockContextNewFrameUpdateUndocking()  - process queue docking requests, create floating dock nodes
11954 //    | - update g.HoveredDockNode            - [debug] update node hovered by mouse
11955 //    | - DockContextProcessDock()            - process one docking request
11956 //    | - DockNodeUpdate()
11957 //    |   - DockNodeUpdateForRootNode()
11958 //    |     - DockNodeUpdateVisibleFlagAndInactiveChilds()
11959 //    |     - DockNodeFindInfo()
11960 //    |   - destroy unused node or tab bar
11961 //    |   - create dock node host window
11962 //    |      - Begin() etc.
11963 //    |   - DockNodeStartMouseMovingWindow()
11964 //    |   - DockNodeTreeUpdatePosSize()
11965 //    |   - DockNodeTreeUpdateSplitter()
11966 //    |   - draw node background
11967 //    |   - DockNodeUpdateTabBar()            - create/update tab bar for a docking node
11968 //    |     - DockNodeAddTabBar()
11969 //    |     - DockNodeUpdateWindowMenu()
11970 //    |     - DockNodeCalcTabBarLayout()
11971 //    |     - BeginTabBarEx()
11972 //    |     - TabItemEx() calls
11973 //    |     - EndTabBar()
11974 //    |   - BeginDockableDragDropTarget()
11975 //    |      - DockNodeUpdate()               - recurse into child nodes...
11976 //-----------------------------------------------------------------------------
11977 // - DockSpace()                              user submit a dockspace into a window
11978 //    | Begin(Child)                          - create a child window
11979 //    | DockNodeUpdate()                      - call main dock node update function
11980 //    | End(Child)
11981 //    | ItemSize()
11982 //-----------------------------------------------------------------------------
11983 // - Begin()
11984 //    | BeginDocked()
11985 //    | BeginDockableDragDropSource()
11986 //    | BeginDockableDragDropTarget()
11987 //    | - DockNodePreviewDockRender()
11988 //-----------------------------------------------------------------------------
11989 
11990 //-----------------------------------------------------------------------------
11991 // Docking: Internal Types
11992 //-----------------------------------------------------------------------------
11993 // - ImGuiDockRequestType
11994 // - ImGuiDockRequest
11995 // - ImGuiDockPreviewData
11996 // - ImGuiDockNodeSettings
11997 // - ImGuiDockContext
11998 //-----------------------------------------------------------------------------
11999 
12000 enum ImGuiDockRequestType
12001 {
12002     ImGuiDockRequestType_None = 0,
12003     ImGuiDockRequestType_Dock,
12004     ImGuiDockRequestType_Undock,
12005     ImGuiDockRequestType_Split                  // Split is the same as Dock but without a DockPayload
12006 };
12007 
12008 struct ImGuiDockRequest
12009 {
12010     ImGuiDockRequestType    Type;
12011     ImGuiWindow*            DockTargetWindow;   // Destination/Target Window to dock into (may be a loose window or a DockNode, might be NULL in which case DockTargetNode cannot be NULL)
12012     ImGuiDockNode*          DockTargetNode;     // Destination/Target Node to dock into
12013     ImGuiWindow*            DockPayload;        // Source/Payload window to dock (may be a loose window or a DockNode), [Optional]
12014     ImGuiDir                DockSplitDir;
12015     float                   DockSplitRatio;
12016     bool                    DockSplitOuter;
12017     ImGuiWindow*            UndockTargetWindow;
12018     ImGuiDockNode*          UndockTargetNode;
12019 
ImGuiDockRequestImGuiDockRequest12020     ImGuiDockRequest()
12021     {
12022         Type = ImGuiDockRequestType_None;
12023         DockTargetWindow = DockPayload = UndockTargetWindow = NULL;
12024         DockTargetNode = UndockTargetNode = NULL;
12025         DockSplitDir = ImGuiDir_None;
12026         DockSplitRatio = 0.5f;
12027         DockSplitOuter = false;
12028     }
12029 };
12030 
12031 struct ImGuiDockPreviewData
12032 {
12033     ImGuiDockNode   FutureNode;
12034     bool            IsDropAllowed;
12035     bool            IsCenterAvailable;
12036     bool            IsSidesAvailable;           // Hold your breath, grammar freaks..
12037     bool            IsSplitDirExplicit;         // Set when hovered the drop rect (vs. implicit SplitDir==None when hovered the window)
12038     ImGuiDockNode*  SplitNode;
12039     ImGuiDir        SplitDir;
12040     float           SplitRatio;
12041     ImRect          DropRectsDraw[ImGuiDir_COUNT + 1];  // May be slightly different from hit-testing drop rects used in DockNodeCalcDropRects()
12042 
ImGuiDockPreviewDataImGuiDockPreviewData12043     ImGuiDockPreviewData() : FutureNode(0) { IsDropAllowed = IsCenterAvailable = IsSidesAvailable = IsSplitDirExplicit = false; SplitNode = NULL; SplitDir = ImGuiDir_None; SplitRatio = 0.f; for (int n = 0; n < IM_ARRAYSIZE(DropRectsDraw); n++) DropRectsDraw[n] = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); }
12044 };
12045 
12046 // Persistent Settings data, stored contiguously in SettingsNodes (sizeof() ~32 bytes)
12047 struct ImGuiDockNodeSettings
12048 {
12049     ImGuiID             ID;
12050     ImGuiID             ParentNodeId;
12051     ImGuiID             ParentWindowId;
12052     ImGuiID             SelectedWindowId;
12053     signed char         SplitAxis;
12054     char                Depth;
12055     ImGuiDockNodeFlags  Flags;                  // NB: We save individual flags one by one in ascii format (ImGuiDockNodeFlags_SavedFlagsMask_)
12056     ImVec2ih            Pos;
12057     ImVec2ih            Size;
12058     ImVec2ih            SizeRef;
ImGuiDockNodeSettingsImGuiDockNodeSettings12059     ImGuiDockNodeSettings() { ID = ParentNodeId = ParentWindowId = SelectedWindowId = 0; SplitAxis = ImGuiAxis_None; Depth = 0; Flags = ImGuiDockNodeFlags_None; }
12060 };
12061 
12062 //-----------------------------------------------------------------------------
12063 // Docking: Forward Declarations
12064 //-----------------------------------------------------------------------------
12065 
12066 namespace ImGui
12067 {
12068     // ImGuiDockContext
12069     static ImGuiDockNode*   DockContextAddNode(ImGuiContext* ctx, ImGuiID id);
12070     static void             DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node);
12071     static void             DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node);
12072     static void             DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req);
12073     static void             DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window, bool clear_persistent_docking_ref = true);
12074     static void             DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node);
12075     static void             DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx);
12076     static ImGuiDockNode*   DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id);
12077     static ImGuiDockNode*   DockContextBindNodeToWindow(ImGuiContext* ctx, ImGuiWindow* window);
12078     static void             DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count);
12079     static void             DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id);                            // Use root_id==0 to add all
12080 
12081     // ImGuiDockNode
12082     static void             DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, bool add_to_tab_bar);
12083     static void             DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node);
12084     static void             DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode* src_node);
12085     static ImGuiWindow*     DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id);
12086     static void             DockNodeApplyPosSizeToWindows(ImGuiDockNode* node);
12087     static void             DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id);
12088     static void             DockNodeHideHostWindow(ImGuiDockNode* node);
12089     static void             DockNodeUpdate(ImGuiDockNode* node);
12090     static void             DockNodeUpdateForRootNode(ImGuiDockNode* node);
12091     static void             DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node);
12092     static void             DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window);
12093     static void             DockNodeAddTabBar(ImGuiDockNode* node);
12094     static void             DockNodeRemoveTabBar(ImGuiDockNode* node);
12095     static ImGuiID          DockNodeUpdateWindowMenu(ImGuiDockNode* node, ImGuiTabBar* tab_bar);
12096     static void             DockNodeUpdateVisibleFlag(ImGuiDockNode* node);
12097     static void             DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window);
12098     static bool             DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* payload_window);
12099     static void             DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockPreviewData* preview_data, bool is_explicit_target, bool is_outer_docking);
12100     static void             DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, const ImGuiDockPreviewData* preview_data);
12101     static void             DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos);
12102     static void             DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired);
12103     static bool             DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_draw, bool outer_docking, ImVec2* test_mouse_pos);
DockNodeGetHostWindowTitle(ImGuiDockNode * node,char * buf,int buf_size)12104     static const char*      DockNodeGetHostWindowTitle(ImGuiDockNode* node, char* buf, int buf_size) { ImFormatString(buf, buf_size, "##DockNode_%02X", node->ID); return buf; }
12105     static int              DockNodeGetTabOrder(ImGuiWindow* window);
12106 
12107     // ImGuiDockNode tree manipulations
12108     static void             DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_first_child, float split_ratio, ImGuiDockNode* new_node);
12109     static void             DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child);
12110     static void             DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, bool only_write_to_marked_nodes = false);
12111     static void             DockNodeTreeUpdateSplitter(ImGuiDockNode* node);
12112     static ImGuiDockNode*   DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos);
12113     static ImGuiDockNode*   DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node);
12114 
12115     // Settings
12116     static void             DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id);
12117     static void             DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ids_count);
12118     static ImGuiDockNodeSettings*   DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID node_id);
12119     static void             DockSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
12120     static void             DockSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);
12121     static void*            DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
12122     static void             DockSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
12123     static void             DockSettingsHandler_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf);
12124 }
12125 
12126 //-----------------------------------------------------------------------------
12127 // Docking: ImGuiDockContext
12128 //-----------------------------------------------------------------------------
12129 // The lifetime model is different from the one of regular windows: we always create a ImGuiDockNode for each ImGuiDockNodeSettings,
12130 // or we always hold the entire docking node tree. Nodes are frequently hidden, e.g. if the window(s) or child nodes they host are not active.
12131 // At boot time only, we run a simple GC to remove nodes that have no references.
12132 // Because dock node settings (which are small, contiguous structures) are always mirrored by their corresponding dock nodes (more complete structures),
12133 // we can also very easily recreate the nodes from scratch given the settings data (this is what DockContextRebuild() does).
12134 // This is convenient as docking reconfiguration can be implemented by mostly poking at the simpler settings data.
12135 //-----------------------------------------------------------------------------
12136 // - DockContextInitialize()
12137 // - DockContextShutdown()
12138 // - DockContextClearNodes()
12139 // - DockContextRebuildNodes()
12140 // - DockContextNewFrameUpdateUndocking()
12141 // - DockContextNewFrameUpdateDocking()
12142 // - DockContextFindNodeByID()
12143 // - DockContextBindNodeToWindow()
12144 // - DockContextGenNodeID()
12145 // - DockContextAddNode()
12146 // - DockContextRemoveNode()
12147 // - ImGuiDockContextPruneNodeData
12148 // - DockContextPruneUnusedSettingsNodes()
12149 // - DockContextBuildNodesFromSettings()
12150 // - DockContextBuildAddWindowsToNodes()
12151 //-----------------------------------------------------------------------------
12152 
DockContextInitialize(ImGuiContext * ctx)12153 void ImGui::DockContextInitialize(ImGuiContext* ctx)
12154 {
12155     ImGuiContext& g = *ctx;
12156 
12157     // Add .ini handle for persistent docking data
12158     ImGuiSettingsHandler ini_handler;
12159     ini_handler.TypeName = "Docking";
12160     ini_handler.TypeHash = ImHashStr("Docking");
12161     ini_handler.ClearAllFn = DockSettingsHandler_ClearAll;
12162     ini_handler.ReadInitFn = DockSettingsHandler_ClearAll; // Also clear on read
12163     ini_handler.ReadOpenFn = DockSettingsHandler_ReadOpen;
12164     ini_handler.ReadLineFn = DockSettingsHandler_ReadLine;
12165     ini_handler.ApplyAllFn = DockSettingsHandler_ApplyAll;
12166     ini_handler.WriteAllFn = DockSettingsHandler_WriteAll;
12167     g.SettingsHandlers.push_back(ini_handler);
12168 }
12169 
DockContextShutdown(ImGuiContext * ctx)12170 void ImGui::DockContextShutdown(ImGuiContext* ctx)
12171 {
12172     ImGuiDockContext* dc  = &ctx->DockContext;
12173     for (int n = 0; n < dc->Nodes.Data.Size; n++)
12174         if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
12175             IM_DELETE(node);
12176 }
12177 
DockContextClearNodes(ImGuiContext * ctx,ImGuiID root_id,bool clear_settings_refs)12178 void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_settings_refs)
12179 {
12180     IM_UNUSED(ctx);
12181     IM_ASSERT(ctx == GImGui);
12182     DockBuilderRemoveNodeDockedWindows(root_id, clear_settings_refs);
12183     DockBuilderRemoveNodeChildNodes(root_id);
12184 }
12185 
12186 // [DEBUG] This function also acts as a defacto test to make sure we can rebuild from scratch without a glitch
12187 // (Different from DockSettingsHandler_ClearAll() + DockSettingsHandler_ApplyAll() because this reuses current settings!)
DockContextRebuildNodes(ImGuiContext * ctx)12188 void ImGui::DockContextRebuildNodes(ImGuiContext* ctx)
12189 {
12190     IMGUI_DEBUG_LOG_DOCKING("DockContextRebuild()\n");
12191     ImGuiDockContext* dc  = &ctx->DockContext;
12192     SaveIniSettingsToMemory();
12193     ImGuiID root_id = 0; // Rebuild all
12194     DockContextClearNodes(ctx, root_id, false);
12195     DockContextBuildNodesFromSettings(ctx, dc->NodesSettings.Data, dc->NodesSettings.Size);
12196     DockContextBuildAddWindowsToNodes(ctx, root_id);
12197 }
12198 
12199 // Docking context update function, called by NewFrame()
DockContextNewFrameUpdateUndocking(ImGuiContext * ctx)12200 void ImGui::DockContextNewFrameUpdateUndocking(ImGuiContext* ctx)
12201 {
12202     ImGuiContext& g = *ctx;
12203     ImGuiDockContext* dc  = &ctx->DockContext;
12204     if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
12205     {
12206         if (dc->Nodes.Data.Size > 0 || dc->Requests.Size > 0)
12207             DockContextClearNodes(ctx, 0, true);
12208         return;
12209     }
12210 
12211     // Setting NoSplit at runtime merges all nodes
12212     if (g.IO.ConfigDockingNoSplit)
12213         for (int n = 0; n < dc->Nodes.Data.Size; n++)
12214             if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
12215                 if (node->IsRootNode() && node->IsSplitNode())
12216                 {
12217                     DockBuilderRemoveNodeChildNodes(node->ID);
12218                     //dc->WantFullRebuild = true;
12219                 }
12220 
12221     // Process full rebuild
12222 #if 0
12223     if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C)))
12224         dc->WantFullRebuild = true;
12225 #endif
12226     if (dc->WantFullRebuild)
12227     {
12228         DockContextRebuildNodes(ctx);
12229         dc->WantFullRebuild = false;
12230     }
12231 
12232     // Process Undocking requests (we need to process them _before_ the UpdateMouseMovingWindowNewFrame call in NewFrame)
12233     for (int n = 0; n < dc->Requests.Size; n++)
12234     {
12235         ImGuiDockRequest* req = &dc->Requests[n];
12236         if (req->Type == ImGuiDockRequestType_Undock && req->UndockTargetWindow)
12237             DockContextProcessUndockWindow(ctx, req->UndockTargetWindow);
12238         else if (req->Type == ImGuiDockRequestType_Undock && req->UndockTargetNode)
12239             DockContextProcessUndockNode(ctx, req->UndockTargetNode);
12240     }
12241 }
12242 
12243 // Docking context update function, called by NewFrame()
DockContextNewFrameUpdateDocking(ImGuiContext * ctx)12244 void ImGui::DockContextNewFrameUpdateDocking(ImGuiContext* ctx)
12245 {
12246     ImGuiContext& g = *ctx;
12247     ImGuiDockContext* dc  = &ctx->DockContext;
12248     if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
12249         return;
12250 
12251     // [DEBUG] Store hovered dock node.
12252     // We could in theory use DockNodeTreeFindVisibleNodeByPos() on the root host dock node, but using ->DockNode is a good shortcut.
12253     // Note this is mostly a debug thing and isn't actually used for docking target, because docking involve more detailed filtering.
12254     g.HoveredDockNode = NULL;
12255     if (ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow)
12256     {
12257         if (hovered_window->DockNodeAsHost)
12258             g.HoveredDockNode = DockNodeTreeFindVisibleNodeByPos(hovered_window->DockNodeAsHost, g.IO.MousePos);
12259         else if (hovered_window->RootWindowDockStop->DockNode)
12260             g.HoveredDockNode = hovered_window->RootWindowDockStop->DockNode;
12261     }
12262 
12263     // Process Docking requests
12264     for (int n = 0; n < dc->Requests.Size; n++)
12265         if (dc->Requests[n].Type == ImGuiDockRequestType_Dock)
12266             DockContextProcessDock(ctx, &dc->Requests[n]);
12267     dc->Requests.resize(0);
12268 
12269     // Create windows for each automatic docking nodes
12270     // We can have NULL pointers when we delete nodes, but because ID are recycled this should amortize nicely (and our node count will never be very high)
12271     for (int n = 0; n < dc->Nodes.Data.Size; n++)
12272         if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
12273             if (node->IsFloatingNode())
12274                 DockNodeUpdate(node);
12275 }
12276 
DockContextFindNodeByID(ImGuiContext * ctx,ImGuiID id)12277 static ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id)
12278 {
12279     return (ImGuiDockNode*)ctx->DockContext.Nodes.GetVoidPtr(id);
12280 }
12281 
DockContextGenNodeID(ImGuiContext * ctx)12282 ImGuiID ImGui::DockContextGenNodeID(ImGuiContext* ctx)
12283 {
12284     // Generate an ID for new node (the exact ID value doesn't matter as long as it is not already used)
12285     // FIXME-OPT FIXME-DOCK: This is suboptimal, even if the node count is small enough not to be a worry. We should poke in ctx->Nodes to find a suitable ID faster.
12286     ImGuiID id = 0x0001;
12287     while (DockContextFindNodeByID(ctx, id) != NULL)
12288         id++;
12289     return id;
12290 }
12291 
DockContextAddNode(ImGuiContext * ctx,ImGuiID id)12292 static ImGuiDockNode* ImGui::DockContextAddNode(ImGuiContext* ctx, ImGuiID id)
12293 {
12294     // Generate an ID for the new node (the exact ID value doesn't matter as long as it is not already used) and add the first window.
12295     if (id == 0)
12296         id = DockContextGenNodeID(ctx);
12297     else
12298         IM_ASSERT(DockContextFindNodeByID(ctx, id) == NULL);
12299 
12300     // We don't set node->LastFrameAlive on construction. Nodes are always created at all time to reflect .ini settings!
12301     IMGUI_DEBUG_LOG_DOCKING("DockContextAddNode 0x%08X\n", id);
12302     ImGuiDockNode* node = IM_NEW(ImGuiDockNode)(id);
12303     ctx->DockContext.Nodes.SetVoidPtr(node->ID, node);
12304     return node;
12305 }
12306 
DockContextRemoveNode(ImGuiContext * ctx,ImGuiDockNode * node,bool merge_sibling_into_parent_node)12307 static void ImGui::DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node)
12308 {
12309     ImGuiContext& g = *ctx;
12310     ImGuiDockContext* dc  = &ctx->DockContext;
12311 
12312     IMGUI_DEBUG_LOG_DOCKING("DockContextRemoveNode 0x%08X\n", node->ID);
12313     IM_ASSERT(DockContextFindNodeByID(ctx, node->ID) == node);
12314     IM_ASSERT(node->ChildNodes[0] == NULL && node->ChildNodes[1] == NULL);
12315     IM_ASSERT(node->Windows.Size == 0);
12316 
12317     if (node->HostWindow)
12318         node->HostWindow->DockNodeAsHost = NULL;
12319 
12320     ImGuiDockNode* parent_node = node->ParentNode;
12321     const bool merge = (merge_sibling_into_parent_node && parent_node != NULL);
12322     if (merge)
12323     {
12324         IM_ASSERT(parent_node->ChildNodes[0] == node || parent_node->ChildNodes[1] == node);
12325         ImGuiDockNode* sibling_node = (parent_node->ChildNodes[0] == node ? parent_node->ChildNodes[1] : parent_node->ChildNodes[0]);
12326         DockNodeTreeMerge(&g, parent_node, sibling_node);
12327     }
12328     else
12329     {
12330         for (int n = 0; parent_node && n < IM_ARRAYSIZE(parent_node->ChildNodes); n++)
12331             if (parent_node->ChildNodes[n] == node)
12332                 node->ParentNode->ChildNodes[n] = NULL;
12333         dc->Nodes.SetVoidPtr(node->ID, NULL);
12334         IM_DELETE(node);
12335     }
12336 }
12337 
DockNodeComparerDepthMostFirst(const void * lhs,const void * rhs)12338 static int IMGUI_CDECL DockNodeComparerDepthMostFirst(const void* lhs, const void* rhs)
12339 {
12340     const ImGuiDockNode* a = *(const ImGuiDockNode* const*)lhs;
12341     const ImGuiDockNode* b = *(const ImGuiDockNode* const*)rhs;
12342     return ImGui::DockNodeGetDepth(b) - ImGui::DockNodeGetDepth(a);
12343 }
12344 
12345 // Pre C++0x doesn't allow us to use a function-local type (without linkage) as template parameter, so we moved this here.
12346 struct ImGuiDockContextPruneNodeData
12347 {
12348     int         CountWindows, CountChildWindows, CountChildNodes;
12349     ImGuiID     RootId;
ImGuiDockContextPruneNodeDataImGuiDockContextPruneNodeData12350     ImGuiDockContextPruneNodeData() { CountWindows = CountChildWindows = CountChildNodes = 0; RootId = 0; }
12351 };
12352 
12353 // Garbage collect unused nodes (run once at init time)
DockContextPruneUnusedSettingsNodes(ImGuiContext * ctx)12354 static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx)
12355 {
12356     ImGuiContext& g = *ctx;
12357     ImGuiDockContext* dc  = &ctx->DockContext;
12358     IM_ASSERT(g.Windows.Size == 0);
12359 
12360     ImPool<ImGuiDockContextPruneNodeData> pool;
12361     pool.Reserve(dc->NodesSettings.Size);
12362 
12363     // Count child nodes and compute RootID
12364     for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++)
12365     {
12366         ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n];
12367         ImGuiDockContextPruneNodeData* parent_data = settings->ParentNodeId ? pool.GetByKey(settings->ParentNodeId) : 0;
12368         pool.GetOrAddByKey(settings->ID)->RootId = parent_data ? parent_data->RootId : settings->ID;
12369         if (settings->ParentNodeId)
12370             pool.GetOrAddByKey(settings->ParentNodeId)->CountChildNodes++;
12371     }
12372 
12373     // Count reference to dock ids from dockspaces
12374     // We track the 'auto-DockNode <- manual-Window <- manual-DockSpace' in order to avoid 'auto-DockNode' being ditched by DockContextPruneUnusedSettingsNodes()
12375     for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++)
12376     {
12377         ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n];
12378         if (settings->ParentWindowId != 0)
12379             if (ImGuiWindowSettings* window_settings = FindWindowSettings(settings->ParentWindowId))
12380                 if (window_settings->DockId)
12381                     if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(window_settings->DockId))
12382                         data->CountChildNodes++;
12383     }
12384 
12385     // Count reference to dock ids from window settings
12386     // We guard against the possibility of an invalid .ini file (RootID may point to a missing node)
12387     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
12388         if (ImGuiID dock_id = settings->DockId)
12389             if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(dock_id))
12390             {
12391                 data->CountWindows++;
12392                 if (ImGuiDockContextPruneNodeData* data_root = (data->RootId == dock_id) ? data : pool.GetByKey(data->RootId))
12393                     data_root->CountChildWindows++;
12394             }
12395 
12396     // Prune
12397     for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++)
12398     {
12399         ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n];
12400         ImGuiDockContextPruneNodeData* data = pool.GetByKey(settings->ID);
12401         if (data->CountWindows > 1)
12402             continue;
12403         ImGuiDockContextPruneNodeData* data_root = (data->RootId == settings->ID) ? data : pool.GetByKey(data->RootId);
12404 
12405         bool remove = false;
12406         remove |= (data->CountWindows == 1 && settings->ParentNodeId == 0 && data->CountChildNodes == 0 && !(settings->Flags & ImGuiDockNodeFlags_CentralNode));  // Floating root node with only 1 window
12407         remove |= (data->CountWindows == 0 && settings->ParentNodeId == 0 && data->CountChildNodes == 0); // Leaf nodes with 0 window
12408         remove |= (data_root->CountChildWindows == 0);
12409         if (remove)
12410         {
12411             IMGUI_DEBUG_LOG_DOCKING("DockContextPruneUnusedSettingsNodes: Prune 0x%08X\n", settings->ID);
12412             DockSettingsRemoveNodeReferences(&settings->ID, 1);
12413             settings->ID = 0;
12414         }
12415     }
12416 }
12417 
DockContextBuildNodesFromSettings(ImGuiContext * ctx,ImGuiDockNodeSettings * node_settings_array,int node_settings_count)12418 static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count)
12419 {
12420     // Build nodes
12421     for (int node_n = 0; node_n < node_settings_count; node_n++)
12422     {
12423         ImGuiDockNodeSettings* settings = &node_settings_array[node_n];
12424         if (settings->ID == 0)
12425             continue;
12426         ImGuiDockNode* node = DockContextAddNode(ctx, settings->ID);
12427         node->ParentNode = settings->ParentNodeId ? DockContextFindNodeByID(ctx, settings->ParentNodeId) : NULL;
12428         node->Pos = ImVec2(settings->Pos.x, settings->Pos.y);
12429         node->Size = ImVec2(settings->Size.x, settings->Size.y);
12430         node->SizeRef = ImVec2(settings->SizeRef.x, settings->SizeRef.y);
12431         node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_DockNode;
12432         if (node->ParentNode && node->ParentNode->ChildNodes[0] == NULL)
12433             node->ParentNode->ChildNodes[0] = node;
12434         else if (node->ParentNode && node->ParentNode->ChildNodes[1] == NULL)
12435             node->ParentNode->ChildNodes[1] = node;
12436         node->SelectedTabId = settings->SelectedWindowId;
12437         node->SplitAxis = (ImGuiAxis)settings->SplitAxis;
12438         node->LocalFlags |= (settings->Flags & ImGuiDockNodeFlags_SavedFlagsMask_);
12439 
12440         // Bind host window immediately if it already exist (in case of a rebuild)
12441         // This is useful as the RootWindowForTitleBarHighlight links necessary to highlight the currently focused node requires node->HostWindow to be set.
12442         char host_window_title[20];
12443         ImGuiDockNode* root_node = DockNodeGetRootNode(node);
12444         node->HostWindow = FindWindowByName(DockNodeGetHostWindowTitle(root_node, host_window_title, IM_ARRAYSIZE(host_window_title)));
12445     }
12446 }
12447 
DockContextBuildAddWindowsToNodes(ImGuiContext * ctx,ImGuiID root_id)12448 void ImGui::DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id)
12449 {
12450     // Rebind all windows to nodes (they can also lazily rebind but we'll have a visible glitch during the first frame)
12451     ImGuiContext& g = *ctx;
12452     for (int n = 0; n < g.Windows.Size; n++)
12453     {
12454         ImGuiWindow* window = g.Windows[n];
12455         if (window->DockId == 0 || window->LastFrameActive < g.FrameCount - 1)
12456             continue;
12457         if (window->DockNode != NULL)
12458             continue;
12459 
12460         ImGuiDockNode* node = DockContextFindNodeByID(ctx, window->DockId);
12461         IM_ASSERT(node != NULL);   // This should have been called after DockContextBuildNodesFromSettings()
12462         if (root_id == 0 || DockNodeGetRootNode(node)->ID == root_id)
12463             DockNodeAddWindow(node, window, true);
12464     }
12465 }
12466 
12467 //-----------------------------------------------------------------------------
12468 // Docking: ImGuiDockContext Docking/Undocking functions
12469 //-----------------------------------------------------------------------------
12470 // - DockContextQueueDock()
12471 // - DockContextQueueUndockWindow()
12472 // - DockContextQueueUndockNode()
12473 // - DockContextQueueNotifyRemovedNode()
12474 // - DockContextProcessDock()
12475 // - DockContextProcessUndockWindow()
12476 // - DockContextProcessUndockNode()
12477 // - DockContextCalcDropPosForDocking()
12478 //-----------------------------------------------------------------------------
12479 
DockContextQueueDock(ImGuiContext * ctx,ImGuiWindow * target,ImGuiDockNode * target_node,ImGuiWindow * payload,ImGuiDir split_dir,float split_ratio,bool split_outer)12480 void ImGui::DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer)
12481 {
12482     IM_ASSERT(target != payload);
12483     ImGuiDockRequest req;
12484     req.Type = ImGuiDockRequestType_Dock;
12485     req.DockTargetWindow = target;
12486     req.DockTargetNode = target_node;
12487     req.DockPayload = payload;
12488     req.DockSplitDir = split_dir;
12489     req.DockSplitRatio = split_ratio;
12490     req.DockSplitOuter = split_outer;
12491     ctx->DockContext.Requests.push_back(req);
12492 }
12493 
DockContextQueueUndockWindow(ImGuiContext * ctx,ImGuiWindow * window)12494 void ImGui::DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window)
12495 {
12496     ImGuiDockRequest req;
12497     req.Type = ImGuiDockRequestType_Undock;
12498     req.UndockTargetWindow = window;
12499     ctx->DockContext.Requests.push_back(req);
12500 }
12501 
DockContextQueueUndockNode(ImGuiContext * ctx,ImGuiDockNode * node)12502 void ImGui::DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node)
12503 {
12504     ImGuiDockRequest req;
12505     req.Type = ImGuiDockRequestType_Undock;
12506     req.UndockTargetNode = node;
12507     ctx->DockContext.Requests.push_back(req);
12508 }
12509 
DockContextQueueNotifyRemovedNode(ImGuiContext * ctx,ImGuiDockNode * node)12510 void ImGui::DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node)
12511 {
12512     ImGuiDockContext* dc  = &ctx->DockContext;
12513     for (int n = 0; n < dc->Requests.Size; n++)
12514         if (dc->Requests[n].DockTargetNode == node)
12515             dc->Requests[n].Type = ImGuiDockRequestType_None;
12516 }
12517 
DockContextProcessDock(ImGuiContext * ctx,ImGuiDockRequest * req)12518 void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req)
12519 {
12520     IM_ASSERT((req->Type == ImGuiDockRequestType_Dock && req->DockPayload != NULL) || (req->Type == ImGuiDockRequestType_Split && req->DockPayload == NULL));
12521     IM_ASSERT(req->DockTargetWindow != NULL || req->DockTargetNode != NULL);
12522 
12523     ImGuiContext& g = *ctx;
12524     IM_UNUSED(g);
12525 
12526     ImGuiWindow* payload_window = req->DockPayload;     // Optional
12527     ImGuiWindow* target_window = req->DockTargetWindow;
12528     ImGuiDockNode* node = req->DockTargetNode;
12529     if (payload_window)
12530         IMGUI_DEBUG_LOG_DOCKING("DockContextProcessDock node 0x%08X target '%s' dock window '%s', split_dir %d\n", node ? node->ID : 0, target_window ? target_window->Name : "NULL", payload_window ? payload_window->Name : "NULL", req->DockSplitDir);
12531     else
12532         IMGUI_DEBUG_LOG_DOCKING("DockContextProcessDock node 0x%08X, split_dir %d\n", node ? node->ID : 0, req->DockSplitDir);
12533 
12534     // Decide which Tab will be selected at the end of the operation
12535     ImGuiID next_selected_id = 0;
12536     ImGuiDockNode* payload_node = NULL;
12537     if (payload_window)
12538     {
12539         payload_node = payload_window->DockNodeAsHost;
12540         payload_window->DockNodeAsHost = NULL; // Important to clear this as the node will have its life as a child which might be merged/deleted later.
12541         if (payload_node && payload_node->IsLeafNode())
12542             next_selected_id = payload_node->TabBar->NextSelectedTabId ? payload_node->TabBar->NextSelectedTabId : payload_node->TabBar->SelectedTabId;
12543         if (payload_node == NULL)
12544             next_selected_id = payload_window->ID;
12545     }
12546 
12547     // FIXME-DOCK: When we are trying to dock an existing single-window node into a loose window, transfer Node ID as well
12548     // When processing an interactive split, usually LastFrameAlive will be < g.FrameCount. But DockBuilder operations can make it ==.
12549     if (node)
12550         IM_ASSERT(node->LastFrameAlive <= g.FrameCount);
12551     if (node && target_window && node == target_window->DockNodeAsHost)
12552         IM_ASSERT(node->Windows.Size > 0 || node->IsSplitNode() || node->IsCentralNode());
12553 
12554     // Create new node and add existing window to it
12555     if (node == NULL)
12556     {
12557         node = DockContextAddNode(ctx, 0);
12558         node->Pos = target_window->Pos;
12559         node->Size = target_window->Size;
12560         if (target_window->DockNodeAsHost == NULL)
12561         {
12562             DockNodeAddWindow(node, target_window, true);
12563             node->TabBar->Tabs[0].Flags &= ~ImGuiTabItemFlags_Unsorted;
12564             target_window->DockIsActive = true;
12565         }
12566     }
12567 
12568     ImGuiDir split_dir = req->DockSplitDir;
12569     if (split_dir != ImGuiDir_None)
12570     {
12571         // Split into one, one side will be our payload node unless we are dropping a loose window
12572         const ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
12573         const int split_inheritor_child_idx = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0; // Current contents will be moved to the opposite side
12574         const float split_ratio = req->DockSplitRatio;
12575         DockNodeTreeSplit(ctx, node, split_axis, split_inheritor_child_idx, split_ratio, payload_node);  // payload_node may be NULL here!
12576         ImGuiDockNode* new_node = node->ChildNodes[split_inheritor_child_idx ^ 1];
12577         new_node->HostWindow = node->HostWindow;
12578         node = new_node;
12579     }
12580     node->LocalFlags &= ~ImGuiDockNodeFlags_HiddenTabBar;
12581 
12582     if (node != payload_node)
12583     {
12584         // Create tab bar before we call DockNodeMoveWindows (which would attempt to move the old tab-bar, which would lead us to payload tabs wrongly appearing before target tabs!)
12585         if (node->Windows.Size > 0 && node->TabBar == NULL)
12586         {
12587             DockNodeAddTabBar(node);
12588             for (int n = 0; n < node->Windows.Size; n++)
12589                 TabBarAddTab(node->TabBar, ImGuiTabItemFlags_None, node->Windows[n]);
12590         }
12591 
12592         if (payload_node != NULL)
12593         {
12594             // Transfer full payload node (with 1+ child windows or child nodes)
12595             if (payload_node->IsSplitNode())
12596             {
12597                 if (node->Windows.Size > 0)
12598                 {
12599                     // We can dock a split payload into a node that already has windows _only_ if our payload is a node tree with a single visible node.
12600                     // In this situation, we move the windows of the target node into the currently visible node of the payload.
12601                     // This allows us to preserve some of the underlying dock tree settings nicely.
12602                     IM_ASSERT(payload_node->OnlyNodeWithWindows != NULL); // The docking should have been blocked by DockNodePreviewDockSetup() early on and never submitted.
12603                     ImGuiDockNode* visible_node = payload_node->OnlyNodeWithWindows;
12604                     if (visible_node->TabBar)
12605                         IM_ASSERT(visible_node->TabBar->Tabs.Size > 0);
12606                     DockNodeMoveWindows(node, visible_node);
12607                     DockNodeMoveWindows(visible_node, node);
12608                     DockSettingsRenameNodeReferences(node->ID, visible_node->ID);
12609                 }
12610                 if (node->IsCentralNode())
12611                 {
12612                     // Central node property needs to be moved to a leaf node, pick the last focused one.
12613                     // FIXME-DOCK: If we had to transfer other flags here, what would the policy be?
12614                     ImGuiDockNode* last_focused_node = DockContextFindNodeByID(ctx, payload_node->LastFocusedNodeId);
12615                     IM_ASSERT(last_focused_node != NULL);
12616                     ImGuiDockNode* last_focused_root_node = DockNodeGetRootNode(last_focused_node);
12617                     IM_ASSERT(last_focused_root_node == DockNodeGetRootNode(payload_node));
12618                     last_focused_node->LocalFlags |= ImGuiDockNodeFlags_CentralNode;
12619                     node->LocalFlags &= ~ImGuiDockNodeFlags_CentralNode;
12620                     last_focused_root_node->CentralNode = last_focused_node;
12621                 }
12622 
12623                 IM_ASSERT(node->Windows.Size == 0);
12624                 DockNodeMoveChildNodes(node, payload_node);
12625             }
12626             else
12627             {
12628                 const ImGuiID payload_dock_id = payload_node->ID;
12629                 DockNodeMoveWindows(node, payload_node);
12630                 DockSettingsRenameNodeReferences(payload_dock_id, node->ID);
12631             }
12632             DockContextRemoveNode(ctx, payload_node, true);
12633         }
12634         else if (payload_window)
12635         {
12636             // Transfer single window
12637             const ImGuiID payload_dock_id = payload_window->DockId;
12638             node->VisibleWindow = payload_window;
12639             DockNodeAddWindow(node, payload_window, true);
12640             if (payload_dock_id != 0)
12641                 DockSettingsRenameNodeReferences(payload_dock_id, node->ID);
12642         }
12643     }
12644     else
12645     {
12646         // When docking a floating single window node we want to reevaluate auto-hiding of the tab bar
12647         node->WantHiddenTabBarUpdate = true;
12648     }
12649 
12650     // Update selection immediately
12651     if (ImGuiTabBar* tab_bar = node->TabBar)
12652         tab_bar->NextSelectedTabId = next_selected_id;
12653     MarkIniSettingsDirty();
12654 }
12655 
DockContextProcessUndockWindow(ImGuiContext * ctx,ImGuiWindow * window,bool clear_persistent_docking_ref)12656 void ImGui::DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window, bool clear_persistent_docking_ref)
12657 {
12658     IMGUI_DEBUG_LOG_DOCKING("DockContextProcessUndockWindow window '%s', clear_persistent_docking_ref = %d\n", window->Name, clear_persistent_docking_ref);
12659     IM_UNUSED(ctx);
12660     if (window->DockNode)
12661         DockNodeRemoveWindow(window->DockNode, window, clear_persistent_docking_ref ? 0 : window->DockId);
12662     else
12663         window->DockId = 0;
12664     window->Collapsed = false;
12665     window->DockIsActive = false;
12666     window->DockTabIsVisible = false;
12667     MarkIniSettingsDirty();
12668 }
12669 
DockContextProcessUndockNode(ImGuiContext * ctx,ImGuiDockNode * node)12670 void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node)
12671 {
12672     IMGUI_DEBUG_LOG_DOCKING("DockContextProcessUndockNode node %08X\n", node->ID);
12673     IM_ASSERT(node->IsLeafNode());
12674     IM_ASSERT(node->Windows.Size >= 1);
12675 
12676     if (node->IsRootNode() || node->IsCentralNode())
12677     {
12678         // In the case of a root node or central node, the node will have to stay in place. Create a new node to receive the payload.
12679         ImGuiDockNode* new_node = DockContextAddNode(ctx, 0);
12680         DockNodeMoveWindows(new_node, node);
12681         DockSettingsRenameNodeReferences(node->ID, new_node->ID);
12682         for (int n = 0; n < new_node->Windows.Size; n++)
12683             UpdateWindowParentAndRootLinks(new_node->Windows[n], new_node->Windows[n]->Flags, NULL);
12684         node = new_node;
12685     }
12686     else
12687     {
12688         // Otherwise extract our node and merging our sibling back into the parent node.
12689         IM_ASSERT(node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node);
12690         int index_in_parent = (node->ParentNode->ChildNodes[0] == node) ? 0 : 1;
12691         node->ParentNode->ChildNodes[index_in_parent] = NULL;
12692         DockNodeTreeMerge(ctx, node->ParentNode, node->ParentNode->ChildNodes[index_in_parent ^ 1]);
12693         node->ParentNode->AuthorityForViewport = ImGuiDataAuthority_Window; // The node that stays in place keeps the viewport, so our newly dragged out node will create a new viewport
12694         node->ParentNode = NULL;
12695     }
12696     node->AuthorityForPos = node->AuthorityForSize = ImGuiDataAuthority_Window;
12697     node->WantMouseMove = true;
12698     MarkIniSettingsDirty();
12699 }
12700 
12701 // This is mostly used for automation.
DockContextCalcDropPosForDocking(ImGuiWindow * target,ImGuiDockNode * target_node,ImGuiWindow * payload,ImGuiDir split_dir,bool split_outer,ImVec2 * out_pos)12702 bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos)
12703 {
12704     if (split_outer)
12705     {
12706         IM_ASSERT(0);
12707     }
12708     else
12709     {
12710         ImGuiDockPreviewData split_data;
12711         DockNodePreviewDockSetup(target, target_node, payload, &split_data, false, split_outer);
12712         if (split_data.DropRectsDraw[split_dir+1].IsInverted())
12713             return false;
12714         *out_pos = split_data.DropRectsDraw[split_dir+1].GetCenter();
12715         return true;
12716     }
12717     return false;
12718 }
12719 
12720 //-----------------------------------------------------------------------------
12721 // Docking: ImGuiDockNode
12722 //-----------------------------------------------------------------------------
12723 // - DockNodeGetTabOrder()
12724 // - DockNodeAddWindow()
12725 // - DockNodeRemoveWindow()
12726 // - DockNodeMoveChildNodes()
12727 // - DockNodeMoveWindows()
12728 // - DockNodeApplyPosSizeToWindows()
12729 // - DockNodeHideHostWindow()
12730 // - ImGuiDockNodeFindInfoResults
12731 // - DockNodeFindInfo()
12732 // - DockNodeFindWindowByID()
12733 // - DockNodeUpdateVisibleFlagAndInactiveChilds()
12734 // - DockNodeUpdateVisibleFlag()
12735 // - DockNodeStartMouseMovingWindow()
12736 // - DockNodeUpdate()
12737 // - DockNodeUpdateWindowMenu()
12738 // - DockNodeBeginAmendTabBar()
12739 // - DockNodeEndAmendTabBar()
12740 // - DockNodeUpdateTabBar()
12741 // - DockNodeAddTabBar()
12742 // - DockNodeRemoveTabBar()
12743 // - DockNodeIsDropAllowedOne()
12744 // - DockNodeIsDropAllowed()
12745 // - DockNodeCalcTabBarLayout()
12746 // - DockNodeCalcSplitRects()
12747 // - DockNodeCalcDropRectsAndTestMousePos()
12748 // - DockNodePreviewDockSetup()
12749 // - DockNodePreviewDockRender()
12750 //-----------------------------------------------------------------------------
12751 
ImGuiDockNode(ImGuiID id)12752 ImGuiDockNode::ImGuiDockNode(ImGuiID id)
12753 {
12754     ID = id;
12755     SharedFlags = LocalFlags = ImGuiDockNodeFlags_None;
12756     ParentNode = ChildNodes[0] = ChildNodes[1] = NULL;
12757     TabBar = NULL;
12758     SplitAxis = ImGuiAxis_None;
12759 
12760     State = ImGuiDockNodeState_Unknown;
12761     HostWindow = VisibleWindow = NULL;
12762     CentralNode = OnlyNodeWithWindows = NULL;
12763     LastFrameAlive = LastFrameActive = LastFrameFocused = -1;
12764     LastFocusedNodeId = 0;
12765     SelectedTabId = 0;
12766     WantCloseTabId = 0;
12767     AuthorityForPos = AuthorityForSize = ImGuiDataAuthority_DockNode;
12768     AuthorityForViewport = ImGuiDataAuthority_Auto;
12769     IsVisible = true;
12770     IsFocused = HasCloseButton = HasWindowMenuButton = EnableCloseButton = false;
12771     WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false;
12772     MarkedForPosSizeWrite = false;
12773 }
12774 
~ImGuiDockNode()12775 ImGuiDockNode::~ImGuiDockNode()
12776 {
12777     IM_DELETE(TabBar);
12778     TabBar = NULL;
12779     ChildNodes[0] = ChildNodes[1] = NULL;
12780 }
12781 
DockNodeGetTabOrder(ImGuiWindow * window)12782 int ImGui::DockNodeGetTabOrder(ImGuiWindow* window)
12783 {
12784     ImGuiTabBar* tab_bar = window->DockNode->TabBar;
12785     if (tab_bar == NULL)
12786         return -1;
12787     ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, window->ID);
12788     return tab ? tab_bar->GetTabOrder(tab) : -1;
12789 }
12790 
DockNodeAddWindow(ImGuiDockNode * node,ImGuiWindow * window,bool add_to_tab_bar)12791 static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, bool add_to_tab_bar)
12792 {
12793     ImGuiContext& g = *GImGui; (void)g;
12794     if (window->DockNode)
12795     {
12796         // Can overwrite an existing window->DockNode (e.g. pointing to a disabled DockSpace node)
12797         IM_ASSERT(window->DockNode->ID != node->ID);
12798         DockNodeRemoveWindow(window->DockNode, window, 0);
12799     }
12800     IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL);
12801     IMGUI_DEBUG_LOG_DOCKING("DockNodeAddWindow node 0x%08X window '%s'\n", node->ID, window->Name);
12802 
12803     node->Windows.push_back(window);
12804     node->WantHiddenTabBarUpdate = true;
12805     window->DockNode = node;
12806     window->DockId = node->ID;
12807     window->DockIsActive = (node->Windows.Size > 1);
12808     window->DockTabWantClose = false;
12809 
12810     // If more than 2 windows appeared on the same frame, we'll create a new hosting DockNode from the point of the second window submission.
12811     // Then we need to hide the first window (after its been output) otherwise it would be visible as a standalone window for one frame.
12812     if (node->HostWindow == NULL && node->Windows.Size == 2 && node->Windows[0]->WasActive == false)
12813     {
12814         node->Windows[0]->Hidden = true;
12815         node->Windows[0]->HiddenFramesCanSkipItems = 1;
12816     }
12817 
12818     // When reactivating a node with one or two loose window, the window pos/size/viewport are authoritative over the node storage.
12819     // In particular it is important we init the viewport from the first window so we don't create two viewports and drop one.
12820     if (node->HostWindow == NULL && node->IsFloatingNode())
12821     {
12822         if (node->AuthorityForPos == ImGuiDataAuthority_Auto)
12823             node->AuthorityForPos = ImGuiDataAuthority_Window;
12824         if (node->AuthorityForSize == ImGuiDataAuthority_Auto)
12825             node->AuthorityForSize = ImGuiDataAuthority_Window;
12826         if (node->AuthorityForViewport == ImGuiDataAuthority_Auto)
12827             node->AuthorityForViewport = ImGuiDataAuthority_Window;
12828     }
12829 
12830     // Add to tab bar if requested
12831     if (add_to_tab_bar)
12832     {
12833         if (node->TabBar == NULL)
12834         {
12835             DockNodeAddTabBar(node);
12836             node->TabBar->SelectedTabId = node->TabBar->NextSelectedTabId = node->SelectedTabId;
12837 
12838             // Add existing windows
12839             for (int n = 0; n < node->Windows.Size - 1; n++)
12840                 TabBarAddTab(node->TabBar, ImGuiTabItemFlags_None, node->Windows[n]);
12841         }
12842         TabBarAddTab(node->TabBar, ImGuiTabItemFlags_Unsorted, window);
12843     }
12844 
12845     DockNodeUpdateVisibleFlag(node);
12846 
12847     // Update this without waiting for the next time we Begin() in the window, so our host window will have the proper title bar color on its first frame.
12848     if (node->HostWindow)
12849         UpdateWindowParentAndRootLinks(window, window->Flags | ImGuiWindowFlags_ChildWindow, node->HostWindow);
12850 }
12851 
DockNodeRemoveWindow(ImGuiDockNode * node,ImGuiWindow * window,ImGuiID save_dock_id)12852 static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id)
12853 {
12854     ImGuiContext& g = *GImGui;
12855     IM_ASSERT(window->DockNode == node);
12856     //IM_ASSERT(window->RootWindow == node->HostWindow);
12857     //IM_ASSERT(window->LastFrameActive < g.FrameCount);    // We may call this from Begin()
12858     IM_ASSERT(save_dock_id == 0 || save_dock_id == node->ID);
12859     IMGUI_DEBUG_LOG_DOCKING("DockNodeRemoveWindow node 0x%08X window '%s'\n", node->ID, window->Name);
12860 
12861     window->DockNode = NULL;
12862     window->DockIsActive = window->DockTabWantClose = false;
12863     window->DockId = save_dock_id;
12864     UpdateWindowParentAndRootLinks(window, window->Flags & ~ImGuiWindowFlags_ChildWindow, NULL); // Update immediately
12865 
12866     // Remove window
12867     bool erased = false;
12868     for (int n = 0; n < node->Windows.Size; n++)
12869         if (node->Windows[n] == window)
12870         {
12871             node->Windows.erase(node->Windows.Data + n);
12872             erased = true;
12873             break;
12874         }
12875     IM_ASSERT(erased);
12876     if (node->VisibleWindow == window)
12877         node->VisibleWindow = NULL;
12878 
12879     // Remove tab and possibly tab bar
12880     node->WantHiddenTabBarUpdate = true;
12881     if (node->TabBar)
12882     {
12883         TabBarRemoveTab(node->TabBar, window->ID);
12884         const int tab_count_threshold_for_tab_bar = node->IsCentralNode() ? 1 : 2;
12885         if (node->Windows.Size < tab_count_threshold_for_tab_bar)
12886             DockNodeRemoveTabBar(node);
12887     }
12888 
12889     if (node->Windows.Size == 0 && !node->IsCentralNode() && !node->IsDockSpace() && window->DockId != node->ID)
12890     {
12891         // Automatic dock node delete themselves if they are not holding at least one tab
12892         DockContextRemoveNode(&g, node, true);
12893         return;
12894     }
12895 
12896     if (node->Windows.Size == 1 && !node->IsCentralNode() && node->HostWindow)
12897     {
12898         ImGuiWindow* remaining_window = node->Windows[0];
12899         if (node->HostWindow->ViewportOwned && node->IsRootNode())
12900         {
12901             // Transfer viewport back to the remaining loose window
12902             IM_ASSERT(node->HostWindow->Viewport->Window == node->HostWindow);
12903             node->HostWindow->Viewport->Window = remaining_window;
12904             node->HostWindow->Viewport->ID = remaining_window->ID;
12905         }
12906         remaining_window->Collapsed = node->HostWindow->Collapsed;
12907     }
12908 
12909     // Update visibility immediately is required so the DockNodeUpdateRemoveInactiveChilds() processing can reflect changes up the tree
12910     DockNodeUpdateVisibleFlag(node);
12911 }
12912 
DockNodeMoveChildNodes(ImGuiDockNode * dst_node,ImGuiDockNode * src_node)12913 static void ImGui::DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode* src_node)
12914 {
12915     IM_ASSERT(dst_node->Windows.Size == 0);
12916     dst_node->ChildNodes[0] = src_node->ChildNodes[0];
12917     dst_node->ChildNodes[1] = src_node->ChildNodes[1];
12918     if (dst_node->ChildNodes[0])
12919         dst_node->ChildNodes[0]->ParentNode = dst_node;
12920     if (dst_node->ChildNodes[1])
12921         dst_node->ChildNodes[1]->ParentNode = dst_node;
12922     dst_node->SplitAxis = src_node->SplitAxis;
12923     dst_node->SizeRef = src_node->SizeRef;
12924     src_node->ChildNodes[0] = src_node->ChildNodes[1] = NULL;
12925 }
12926 
DockNodeMoveWindows(ImGuiDockNode * dst_node,ImGuiDockNode * src_node)12927 static void ImGui::DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node)
12928 {
12929     // Insert tabs in the same orders as currently ordered (node->Windows isn't ordered)
12930     IM_ASSERT(src_node && dst_node && dst_node != src_node);
12931     ImGuiTabBar* src_tab_bar = src_node->TabBar;
12932     if (src_tab_bar != NULL)
12933         IM_ASSERT(src_node->Windows.Size <= src_node->TabBar->Tabs.Size);
12934 
12935     // If the dst_node is empty we can just move the entire tab bar (to preserve selection, scrolling, etc.)
12936     bool move_tab_bar = (src_tab_bar != NULL) && (dst_node->TabBar == NULL);
12937     if (move_tab_bar)
12938     {
12939         dst_node->TabBar = src_node->TabBar;
12940         src_node->TabBar = NULL;
12941     }
12942 
12943     for (int n = 0; n < src_node->Windows.Size; n++)
12944     {
12945         // DockNode's TabBar may have non-window Tabs manually appended by user
12946         if (ImGuiWindow* window = src_tab_bar ? src_tab_bar->Tabs[n].Window : src_node->Windows[n])
12947         {
12948             window->DockNode = NULL;
12949             window->DockIsActive = false;
12950             DockNodeAddWindow(dst_node, window, move_tab_bar ? false : true);
12951         }
12952     }
12953     src_node->Windows.clear();
12954 
12955     if (!move_tab_bar && src_node->TabBar)
12956     {
12957         if (dst_node->TabBar)
12958             dst_node->TabBar->SelectedTabId = src_node->TabBar->SelectedTabId;
12959         DockNodeRemoveTabBar(src_node);
12960     }
12961 }
12962 
DockNodeApplyPosSizeToWindows(ImGuiDockNode * node)12963 static void ImGui::DockNodeApplyPosSizeToWindows(ImGuiDockNode* node)
12964 {
12965     for (int n = 0; n < node->Windows.Size; n++)
12966     {
12967         SetWindowPos(node->Windows[n], node->Pos, ImGuiCond_Always); // We don't assign directly to Pos because it can break the calculation of SizeContents on next frame
12968         SetWindowSize(node->Windows[n], node->Size, ImGuiCond_Always);
12969     }
12970 }
12971 
DockNodeHideHostWindow(ImGuiDockNode * node)12972 static void ImGui::DockNodeHideHostWindow(ImGuiDockNode* node)
12973 {
12974     if (node->HostWindow)
12975     {
12976         if (node->HostWindow->DockNodeAsHost == node)
12977             node->HostWindow->DockNodeAsHost = NULL;
12978         node->HostWindow = NULL;
12979     }
12980 
12981     if (node->Windows.Size == 1)
12982     {
12983         node->VisibleWindow = node->Windows[0];
12984         node->Windows[0]->DockIsActive = false;
12985     }
12986 
12987     if (node->TabBar)
12988         DockNodeRemoveTabBar(node);
12989 }
12990 
12991 // Search function called once by root node in DockNodeUpdate()
12992 struct ImGuiDockNodeFindInfoResults
12993 {
12994     ImGuiDockNode*      CentralNode;
12995     ImGuiDockNode*      FirstNodeWithWindows;
12996     int                 CountNodesWithWindows;
12997     //ImGuiWindowClass  WindowClassForMerges;
12998 
ImGuiDockNodeFindInfoResultsImGuiDockNodeFindInfoResults12999     ImGuiDockNodeFindInfoResults() { CentralNode = FirstNodeWithWindows = NULL; CountNodesWithWindows = 0; }
13000 };
13001 
DockNodeFindInfo(ImGuiDockNode * node,ImGuiDockNodeFindInfoResults * results)13002 static void DockNodeFindInfo(ImGuiDockNode* node, ImGuiDockNodeFindInfoResults* results)
13003 {
13004     if (node->Windows.Size > 0)
13005     {
13006         if (results->FirstNodeWithWindows == NULL)
13007             results->FirstNodeWithWindows = node;
13008         results->CountNodesWithWindows++;
13009     }
13010     if (node->IsCentralNode())
13011     {
13012         IM_ASSERT(results->CentralNode == NULL); // Should be only one
13013         IM_ASSERT(node->IsLeafNode() && "If you get this assert: please submit .ini file + repro of actions leading to this.");
13014         results->CentralNode = node;
13015     }
13016     if (results->CountNodesWithWindows > 1 && results->CentralNode != NULL)
13017         return;
13018     if (node->ChildNodes[0])
13019         DockNodeFindInfo(node->ChildNodes[0], results);
13020     if (node->ChildNodes[1])
13021         DockNodeFindInfo(node->ChildNodes[1], results);
13022 }
13023 
DockNodeFindWindowByID(ImGuiDockNode * node,ImGuiID id)13024 static ImGuiWindow* ImGui::DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id)
13025 {
13026     IM_ASSERT(id != 0);
13027     for (int n = 0; n < node->Windows.Size; n++)
13028         if (node->Windows[n]->ID == id)
13029             return node->Windows[n];
13030     return NULL;
13031 }
13032 
13033 // - Remove inactive windows/nodes.
13034 // - Update visibility flag.
DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode * node)13035 static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node)
13036 {
13037     ImGuiContext& g = *GImGui;
13038     IM_ASSERT(node->ParentNode == NULL || node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node);
13039 
13040     // Inherit most flags
13041     if (node->ParentNode)
13042         node->SharedFlags = node->ParentNode->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_;
13043 
13044     // Recurse into children
13045     // There is the possibility that one of our child becoming empty will delete itself and moving its sibling contents into 'node'.
13046     // If 'node->ChildNode[0]' delete itself, then 'node->ChildNode[1]->Windows' will be moved into 'node'
13047     // If 'node->ChildNode[1]' delete itself, then 'node->ChildNode[0]->Windows' will be moved into 'node' and the "remove inactive windows" loop will have run twice on those windows (harmless)
13048     if (node->ChildNodes[0])
13049         DockNodeUpdateVisibleFlagAndInactiveChilds(node->ChildNodes[0]);
13050     if (node->ChildNodes[1])
13051         DockNodeUpdateVisibleFlagAndInactiveChilds(node->ChildNodes[1]);
13052 
13053     // Remove inactive windows
13054     // Merge node flags overrides stored in windows
13055     for (int window_n = 0; window_n < node->Windows.Size; window_n++)
13056     {
13057         ImGuiWindow* window = node->Windows[window_n];
13058         IM_ASSERT(window->DockNode == node);
13059 
13060         bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount);
13061         bool remove = false;
13062         remove |= node_was_active && (window->LastFrameActive + 1 < g.FrameCount);
13063         remove |= node_was_active && (node->WantCloseAll || node->WantCloseTabId == window->ID) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument);  // Submit all _expected_ closure from last frame
13064         remove |= (window->DockTabWantClose);
13065         if (remove)
13066         {
13067             window->DockTabWantClose = false;
13068             if (node->Windows.Size == 1 && !node->IsCentralNode())
13069             {
13070                 DockNodeHideHostWindow(node);
13071                 node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow;
13072                 DockNodeRemoveWindow(node, window, node->ID); // Will delete the node so it'll be invalid on return
13073                 return;
13074             }
13075             DockNodeRemoveWindow(node, window, node->ID);
13076             window_n--;
13077         }
13078         else
13079         {
13080             node->LocalFlags &= ~window->WindowClass.DockNodeFlagsOverrideClear;
13081             node->LocalFlags |= window->WindowClass.DockNodeFlagsOverrideSet;
13082         }
13083     }
13084 
13085     // Auto-hide tab bar option
13086     ImGuiDockNodeFlags node_flags = node->GetMergedFlags();
13087     if (node->WantHiddenTabBarUpdate && node->Windows.Size == 1 && (node_flags & ImGuiDockNodeFlags_AutoHideTabBar) && !node->IsHiddenTabBar())
13088         node->WantHiddenTabBarToggle = true;
13089     node->WantHiddenTabBarUpdate = false;
13090 
13091     // Cancel toggling if we know our tab bar is enforced to be hidden at all times
13092     if (node->WantHiddenTabBarToggle && node->VisibleWindow && (node->VisibleWindow->WindowClass.DockNodeFlagsOverrideSet & ImGuiDockNodeFlags_HiddenTabBar))
13093         node->WantHiddenTabBarToggle = false;
13094 
13095     // Apply toggles at a single point of the frame (here!)
13096     if (node->Windows.Size > 1)
13097         node->LocalFlags &= ~ImGuiDockNodeFlags_HiddenTabBar;
13098     else if (node->WantHiddenTabBarToggle)
13099         node->LocalFlags ^= ImGuiDockNodeFlags_HiddenTabBar;
13100     node->WantHiddenTabBarToggle = false;
13101 
13102     DockNodeUpdateVisibleFlag(node);
13103 }
13104 
DockNodeUpdateVisibleFlag(ImGuiDockNode * node)13105 static void ImGui::DockNodeUpdateVisibleFlag(ImGuiDockNode* node)
13106 {
13107     // Update visibility flag
13108     bool is_visible = (node->ParentNode == NULL) ? node->IsDockSpace() : node->IsCentralNode();
13109     is_visible |= (node->Windows.Size > 0);
13110     is_visible |= (node->ChildNodes[0] && node->ChildNodes[0]->IsVisible);
13111     is_visible |= (node->ChildNodes[1] && node->ChildNodes[1]->IsVisible);
13112     node->IsVisible = is_visible;
13113 }
13114 
DockNodeStartMouseMovingWindow(ImGuiDockNode * node,ImGuiWindow * window)13115 static void ImGui::DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window)
13116 {
13117     ImGuiContext& g = *GImGui;
13118     IM_ASSERT(node->WantMouseMove == true);
13119     StartMouseMovingWindow(window);
13120     g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - node->Pos;
13121     g.MovingWindow = window; // If we are docked into a non moveable root window, StartMouseMovingWindow() won't set g.MovingWindow. Override that decision.
13122     node->WantMouseMove = false;
13123 }
13124 
13125 // Update CentralNode, OnlyNodeWithWindows, LastFocusedNodeID. Copy window class.
DockNodeUpdateForRootNode(ImGuiDockNode * node)13126 static void ImGui::DockNodeUpdateForRootNode(ImGuiDockNode* node)
13127 {
13128     DockNodeUpdateVisibleFlagAndInactiveChilds(node);
13129 
13130     // FIXME-DOCK: Merge this scan into the one above.
13131     // - Setup central node pointers
13132     // - Find if there's only a single visible window in the hierarchy (in which case we need to display a regular title bar -> FIXME-DOCK: that last part is not done yet!)
13133     ImGuiDockNodeFindInfoResults results;
13134     DockNodeFindInfo(node, &results);
13135     node->CentralNode = results.CentralNode;
13136     node->OnlyNodeWithWindows = (results.CountNodesWithWindows == 1) ? results.FirstNodeWithWindows : NULL;
13137     if (node->LastFocusedNodeId == 0 && results.FirstNodeWithWindows != NULL)
13138         node->LastFocusedNodeId = results.FirstNodeWithWindows->ID;
13139 
13140     // Copy the window class from of our first window so it can be used for proper dock filtering.
13141     // When node has mixed windows, prioritize the class with the most constraint (DockingAllowUnclassed = false) as the reference to copy.
13142     // FIXME-DOCK: We don't recurse properly, this code could be reworked to work from DockNodeUpdateScanRec.
13143     if (ImGuiDockNode* first_node_with_windows = results.FirstNodeWithWindows)
13144     {
13145         node->WindowClass = first_node_with_windows->Windows[0]->WindowClass;
13146         for (int n = 1; n < first_node_with_windows->Windows.Size; n++)
13147             if (first_node_with_windows->Windows[n]->WindowClass.DockingAllowUnclassed == false)
13148             {
13149                 node->WindowClass = first_node_with_windows->Windows[n]->WindowClass;
13150                 break;
13151             }
13152     }
13153 }
13154 
DockNodeUpdate(ImGuiDockNode * node)13155 static void ImGui::DockNodeUpdate(ImGuiDockNode* node)
13156 {
13157     ImGuiContext& g = *GImGui;
13158     IM_ASSERT(node->LastFrameActive != g.FrameCount);
13159     node->LastFrameAlive = g.FrameCount;
13160     node->MarkedForPosSizeWrite = false;
13161 
13162     node->CentralNode = node->OnlyNodeWithWindows = NULL;
13163     if (node->IsRootNode())
13164         DockNodeUpdateForRootNode(node);
13165 
13166     // Remove tab bar if not needed
13167     if (node->TabBar && node->IsNoTabBar())
13168         DockNodeRemoveTabBar(node);
13169 
13170     // Early out for hidden root dock nodes (when all DockId references are in inactive windows, or there is only 1 floating window holding on the DockId)
13171     bool want_to_hide_host_window = false;
13172     if (node->Windows.Size <= 1 && node->IsFloatingNode() && node->IsLeafNode())
13173         if (!g.IO.ConfigDockingAlwaysTabBar && (node->Windows.Size == 0 || !node->Windows[0]->WindowClass.DockingAlwaysTabBar))
13174             want_to_hide_host_window = true;
13175     if (want_to_hide_host_window)
13176     {
13177         if (node->Windows.Size == 1)
13178         {
13179             // Floating window pos/size is authoritative
13180             ImGuiWindow* single_window = node->Windows[0];
13181             node->Pos = single_window->Pos;
13182             node->Size = single_window->SizeFull;
13183             node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window;
13184 
13185             // Transfer focus immediately so when we revert to a regular window it is immediately selected
13186             if (node->HostWindow && g.NavWindow == node->HostWindow)
13187                 FocusWindow(single_window);
13188             if (node->HostWindow)
13189             {
13190                 single_window->Viewport = node->HostWindow->Viewport;
13191                 single_window->ViewportId = node->HostWindow->ViewportId;
13192                 if (node->HostWindow->ViewportOwned)
13193                 {
13194                     single_window->Viewport->Window = single_window;
13195                     single_window->ViewportOwned = true;
13196                 }
13197             }
13198         }
13199 
13200         DockNodeHideHostWindow(node);
13201         node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow;
13202         node->WantCloseAll = false;
13203         node->WantCloseTabId = 0;
13204         node->HasCloseButton = node->HasWindowMenuButton = node->EnableCloseButton = false;
13205         node->LastFrameActive = g.FrameCount;
13206 
13207         if (node->WantMouseMove && node->Windows.Size == 1)
13208             DockNodeStartMouseMovingWindow(node, node->Windows[0]);
13209         return;
13210     }
13211 
13212     // In some circumstance we will defer creating the host window (so everything will be kept hidden),
13213     // while the expected visible window is resizing itself.
13214     // This is important for first-time (no ini settings restored) single window when io.ConfigDockingAlwaysTabBar is enabled,
13215     // otherwise the node ends up using the minimum window size. Effectively those windows will take an extra frame to show up:
13216     //   N+0: Begin(): window created (with no known size), node is created
13217     //   N+1: DockNodeUpdate(): node skip creating host window / Begin(): window size applied, not visible
13218     //   N+2: DockNodeUpdate(): node can create host window / Begin(): window becomes visible
13219     // We could remove this frame if we could reliably calculate the expected window size during node update, before the Begin() code.
13220     // It would require a generalization of CalcWindowExpectedSize(), probably extracting code away from Begin().
13221     // In reality it isn't very important as user quickly ends up with size data in .ini file.
13222     if (node->IsVisible && node->HostWindow == NULL && node->IsFloatingNode() && node->IsLeafNode())
13223     {
13224         IM_ASSERT(node->Windows.Size > 0);
13225         ImGuiWindow* ref_window = NULL;
13226         if (node->SelectedTabId != 0) // Note that we prune single-window-node settings on .ini loading, so this is generally 0 for them!
13227             ref_window = DockNodeFindWindowByID(node, node->SelectedTabId);
13228         if (ref_window == NULL)
13229             ref_window = node->Windows[0];
13230         if (ref_window->AutoFitFramesX > 0 || ref_window->AutoFitFramesY > 0)
13231         {
13232             node->State = ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing;
13233             return;
13234         }
13235     }
13236 
13237     const ImGuiDockNodeFlags node_flags = node->GetMergedFlags();
13238 
13239     // Bind or create host window
13240     ImGuiWindow* host_window = NULL;
13241     bool beginned_into_host_window = false;
13242     if (node->IsDockSpace())
13243     {
13244         // [Explicit root dockspace node]
13245         IM_ASSERT(node->HostWindow);
13246         node->EnableCloseButton = false;
13247         node->HasCloseButton = (node_flags & ImGuiDockNodeFlags_NoCloseButton) == 0;
13248         node->HasWindowMenuButton = (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0;
13249         host_window = node->HostWindow;
13250     }
13251     else
13252     {
13253         // [Automatic root or child nodes]
13254         node->EnableCloseButton = false;
13255         node->HasCloseButton = (node->Windows.Size > 0) && (node_flags & ImGuiDockNodeFlags_NoCloseButton) == 0;
13256         node->HasWindowMenuButton = (node->Windows.Size > 0) && (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0;
13257         for (int window_n = 0; window_n < node->Windows.Size; window_n++)
13258         {
13259             // FIXME-DOCK: Setting DockIsActive here means that for single active window in a leaf node, DockIsActive will be cleared until the next Begin() call.
13260             ImGuiWindow* window = node->Windows[window_n];
13261             window->DockIsActive = (node->Windows.Size > 1);
13262             node->EnableCloseButton |= window->HasCloseButton;
13263         }
13264 
13265         if (node->IsRootNode() && node->IsVisible)
13266         {
13267             ImGuiWindow* ref_window = (node->Windows.Size > 0) ? node->Windows[0] : NULL;
13268 
13269             // Sync Pos
13270             if (node->AuthorityForPos == ImGuiDataAuthority_Window && ref_window)
13271                 SetNextWindowPos(ref_window->Pos);
13272             else if (node->AuthorityForPos == ImGuiDataAuthority_DockNode)
13273                 SetNextWindowPos(node->Pos);
13274 
13275             // Sync Size
13276             if (node->AuthorityForSize == ImGuiDataAuthority_Window && ref_window)
13277                 SetNextWindowSize(ref_window->SizeFull);
13278             else if (node->AuthorityForSize == ImGuiDataAuthority_DockNode)
13279                 SetNextWindowSize(node->Size);
13280 
13281             // Sync Collapsed
13282             if (node->AuthorityForSize == ImGuiDataAuthority_Window && ref_window)
13283                 SetNextWindowCollapsed(ref_window->Collapsed);
13284 
13285             // Sync Viewport
13286             if (node->AuthorityForViewport == ImGuiDataAuthority_Window && ref_window)
13287                 SetNextWindowViewport(ref_window->ViewportId);
13288 
13289             SetNextWindowClass(&node->WindowClass);
13290 
13291             // Begin into the host window
13292             char window_label[20];
13293             DockNodeGetHostWindowTitle(node, window_label, IM_ARRAYSIZE(window_label));
13294             ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_DockNodeHost;
13295             window_flags |= ImGuiWindowFlags_NoFocusOnAppearing;
13296             window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoCollapse;
13297             window_flags |= ImGuiWindowFlags_NoTitleBar;
13298 
13299             PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
13300             Begin(window_label, NULL, window_flags);
13301             PopStyleVar();
13302             beginned_into_host_window = true;
13303 
13304             node->HostWindow = host_window = g.CurrentWindow;
13305             host_window->DockNodeAsHost = node;
13306             host_window->DC.CursorPos = host_window->Pos;
13307             node->Pos = host_window->Pos;
13308             node->Size = host_window->Size;
13309 
13310             // We set ImGuiWindowFlags_NoFocusOnAppearing because we don't want the host window to take full focus (e.g. steal NavWindow)
13311             // But we still it bring it to the front of display. There's no way to choose this precise behavior via window flags.
13312             // One simple case to ponder if: window A has a toggle to create windows B/C/D. Dock B/C/D together, clear the toggle and enable it again.
13313             // When reappearing B/C/D will request focus and be moved to the top of the display pile, but they are not linked to the dock host window
13314             // during the frame they appear. The dock host window would keep its old display order, and the sorting in EndFrame would move B/C/D back
13315             // after the dock host window, losing their top-most status.
13316             if (node->HostWindow->Appearing)
13317                 BringWindowToDisplayFront(node->HostWindow);
13318 
13319             node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Auto;
13320         }
13321         else if (node->ParentNode)
13322         {
13323             node->HostWindow = host_window = node->ParentNode->HostWindow;
13324             node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Auto;
13325         }
13326         if (node->WantMouseMove && node->HostWindow)
13327             DockNodeStartMouseMovingWindow(node, node->HostWindow);
13328     }
13329 
13330     // Update focused node (the one whose title bar is highlight) within a node tree
13331     if (node->IsSplitNode())
13332         IM_ASSERT(node->TabBar == NULL);
13333     if (node->IsRootNode())
13334         if (g.NavWindow && g.NavWindow->RootWindowDockStop->DockNode && g.NavWindow->RootWindowDockStop->ParentWindow == host_window)
13335             node->LastFocusedNodeId = g.NavWindow->RootWindowDockStop->DockNode->ID;
13336 
13337     // We need to draw a background at the root level if requested by ImGuiDockNodeFlags_PassthruCentralNode, but we will only know the correct pos/size
13338     // _after_ processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order!
13339     const bool render_dockspace_bg = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0;
13340     if (render_dockspace_bg)
13341     {
13342         host_window->DrawList->ChannelsSplit(2);
13343         host_window->DrawList->ChannelsSetCurrent(1);
13344     }
13345 
13346     // Register a hit-test hole in the window unless we are currently dragging a window that is compatible with our dockspace
13347     const ImGuiDockNode* central_node = node->CentralNode;
13348     const bool central_node_hole = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0 && central_node != NULL && central_node->IsEmpty();
13349     bool central_node_hole_register_hit_test_hole = central_node_hole;
13350     if (central_node_hole)
13351         if (const ImGuiPayload* payload = ImGui::GetDragDropPayload())
13352             if (payload->IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) && DockNodeIsDropAllowed(host_window, *(ImGuiWindow**)payload->Data))
13353                 central_node_hole_register_hit_test_hole = false;
13354     if (central_node_hole_register_hit_test_hole)
13355     {
13356         // Add a little padding to match the "resize from edges" behavior and allow grabbing the splitter easily.
13357         IM_ASSERT(node->IsDockSpace()); // We cannot pass this flag without the DockSpace() api. Testing this because we also setup the hole in host_window->ParentNode
13358         ImRect central_hole(central_node->Pos, central_node->Pos + central_node->Size);
13359         central_hole.Expand(ImVec2(-WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS, -WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS));
13360         if (central_node_hole && !central_hole.IsInverted())
13361         {
13362             SetWindowHitTestHole(host_window, central_hole.Min, central_hole.Max - central_hole.Min);
13363             SetWindowHitTestHole(host_window->ParentWindow, central_hole.Min, central_hole.Max - central_hole.Min);
13364         }
13365     }
13366 
13367     // Update position/size, process and draw resizing splitters
13368     if (node->IsRootNode() && host_window)
13369     {
13370         DockNodeTreeUpdatePosSize(node, host_window->Pos, host_window->Size);
13371         DockNodeTreeUpdateSplitter(node);
13372     }
13373 
13374     // Draw empty node background (currently can only be the Central Node)
13375     if (host_window && node->IsEmpty() && node->IsVisible && !(node_flags & ImGuiDockNodeFlags_PassthruCentralNode))
13376         host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_DockingEmptyBg));
13377 
13378     // Draw whole dockspace background if ImGuiDockNodeFlags_PassthruCentralNode if set.
13379     if (render_dockspace_bg && node->IsVisible)
13380     {
13381         host_window->DrawList->ChannelsSetCurrent(0);
13382         if (central_node_hole)
13383             RenderRectFilledWithHole(host_window->DrawList, node->Rect(), central_node->Rect(), GetColorU32(ImGuiCol_WindowBg), 0.0f);
13384         else
13385             host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_WindowBg), 0.0f);
13386         host_window->DrawList->ChannelsMerge();
13387     }
13388 
13389     // Draw and populate Tab Bar
13390     if (host_window && node->Windows.Size > 0)
13391     {
13392         DockNodeUpdateTabBar(node, host_window);
13393     }
13394     else
13395     {
13396         node->WantCloseAll = false;
13397         node->WantCloseTabId = 0;
13398         node->IsFocused = false;
13399     }
13400     if (node->TabBar && node->TabBar->SelectedTabId)
13401         node->SelectedTabId = node->TabBar->SelectedTabId;
13402     else if (node->Windows.Size > 0)
13403         node->SelectedTabId = node->Windows[0]->ID;
13404 
13405     // Draw payload drop target
13406     if (host_window && node->IsVisible)
13407         if (node->IsRootNode() && (g.MovingWindow == NULL || g.MovingWindow->RootWindow != host_window))
13408             BeginDockableDragDropTarget(host_window);
13409 
13410     // We update this after DockNodeUpdateTabBar()
13411     node->LastFrameActive = g.FrameCount;
13412 
13413     // Recurse into children
13414     // FIXME-DOCK FIXME-OPT: Should not need to recurse into children
13415     if (host_window)
13416     {
13417         if (node->ChildNodes[0])
13418             DockNodeUpdate(node->ChildNodes[0]);
13419         if (node->ChildNodes[1])
13420             DockNodeUpdate(node->ChildNodes[1]);
13421 
13422         // Render outer borders last (after the tab bar)
13423         if (node->IsRootNode())
13424             RenderWindowOuterBorders(host_window);
13425     }
13426 
13427     // End host window
13428     if (beginned_into_host_window) //-V1020
13429         End();
13430 }
13431 
13432 // Compare TabItem nodes given the last known DockOrder (will persist in .ini file as hint), used to sort tabs when multiple tabs are added on the same frame.
TabItemComparerByDockOrder(const void * lhs,const void * rhs)13433 static int IMGUI_CDECL TabItemComparerByDockOrder(const void* lhs, const void* rhs)
13434 {
13435     ImGuiWindow* a = ((const ImGuiTabItem*)lhs)->Window;
13436     ImGuiWindow* b = ((const ImGuiTabItem*)rhs)->Window;
13437     if (int d = ((a->DockOrder == -1) ? INT_MAX : a->DockOrder) - ((b->DockOrder == -1) ? INT_MAX : b->DockOrder))
13438         return d;
13439     return (a->BeginOrderWithinContext - b->BeginOrderWithinContext);
13440 }
13441 
DockNodeUpdateWindowMenu(ImGuiDockNode * node,ImGuiTabBar * tab_bar)13442 static ImGuiID ImGui::DockNodeUpdateWindowMenu(ImGuiDockNode* node, ImGuiTabBar* tab_bar)
13443 {
13444     // Try to position the menu so it is more likely to stays within the same viewport
13445     ImGuiContext& g = *GImGui;
13446     ImGuiID ret_tab_id = 0;
13447     if (g.Style.WindowMenuButtonPosition == ImGuiDir_Left)
13448         SetNextWindowPos(ImVec2(node->Pos.x, node->Pos.y + GetFrameHeight()), ImGuiCond_Always, ImVec2(0.0f, 0.0f));
13449     else
13450         SetNextWindowPos(ImVec2(node->Pos.x + node->Size.x, node->Pos.y + GetFrameHeight()), ImGuiCond_Always, ImVec2(1.0f, 0.0f));
13451     if (BeginPopup("#WindowMenu"))
13452     {
13453         node->IsFocused = true;
13454         if (tab_bar->Tabs.Size == 1)
13455         {
13456             if (MenuItem("Hide tab bar", NULL, node->IsHiddenTabBar()))
13457                 node->WantHiddenTabBarToggle = true;
13458         }
13459         else
13460         {
13461             for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
13462             {
13463                 ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
13464                 if (tab->Flags & ImGuiTabItemFlags_Button)
13465                     continue;
13466                 if (Selectable(tab_bar->GetTabName(tab), tab->ID == tab_bar->SelectedTabId))
13467                     ret_tab_id = tab->ID;
13468                 SameLine();
13469                 Text("   ");
13470             }
13471         }
13472         EndPopup();
13473     }
13474     return ret_tab_id;
13475 }
13476 
13477 // User helper to append/amend into a dock node tab bar. Most commonly used to add e.g. a "+" button.
DockNodeBeginAmendTabBar(ImGuiDockNode * node)13478 bool ImGui::DockNodeBeginAmendTabBar(ImGuiDockNode* node)
13479 {
13480     if (node->TabBar == NULL || node->HostWindow == NULL)
13481         return false;
13482     if (node->SharedFlags & ImGuiDockNodeFlags_KeepAliveOnly)
13483         return false;
13484     Begin(node->HostWindow->Name);
13485     PushOverrideID(node->ID);
13486     bool ret = BeginTabBarEx(node->TabBar, node->TabBar->BarRect, node->TabBar->Flags, node);
13487     IM_ASSERT(ret);
13488     return true;
13489 }
13490 
DockNodeEndAmendTabBar()13491 void ImGui::DockNodeEndAmendTabBar()
13492 {
13493     EndTabBar();
13494     PopID();
13495     End();
13496 }
13497 
13498 // Submit the tab bar corresponding to a dock node and various housekeeping details.
DockNodeUpdateTabBar(ImGuiDockNode * node,ImGuiWindow * host_window)13499 static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window)
13500 {
13501     ImGuiContext& g = *GImGui;
13502     ImGuiStyle& style = g.Style;
13503 
13504     const bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount);
13505     const bool closed_all = node->WantCloseAll && node_was_active;
13506     const ImGuiID closed_one = node->WantCloseTabId && node_was_active;
13507     node->WantCloseAll = false;
13508     node->WantCloseTabId = 0;
13509 
13510     // Decide if we should use a focused title bar color
13511     bool is_focused = false;
13512     ImGuiDockNode* root_node = DockNodeGetRootNode(node);
13513     if (g.NavWindowingTarget)
13514         is_focused = (g.NavWindowingTarget->DockNode == node);
13515     else if (g.NavWindow && g.NavWindow->RootWindowForTitleBarHighlight == host_window->RootWindow && root_node->LastFocusedNodeId == node->ID)
13516         is_focused = true;
13517 
13518     // Hidden tab bar will show a triangle on the upper-left (in Begin)
13519     if (node->IsHiddenTabBar() || node->IsNoTabBar())
13520     {
13521         node->VisibleWindow = (node->Windows.Size > 0) ? node->Windows[0] : NULL;
13522         node->IsFocused = is_focused;
13523         if (is_focused)
13524             node->LastFrameFocused = g.FrameCount;
13525         if (node->VisibleWindow)
13526         {
13527             // Notify root of visible window (used to display title in OS task bar)
13528             if (is_focused || root_node->VisibleWindow == NULL)
13529                 root_node->VisibleWindow = node->VisibleWindow;
13530             if (node->TabBar)
13531                 node->TabBar->VisibleTabId = node->VisibleWindow->ID;
13532         }
13533         return;
13534     }
13535 
13536     // Move ourselves to the Menu layer (so we can be accessed by tapping Alt) + undo SkipItems flag in order to draw over the title bar even if the window is collapsed
13537     bool backup_skip_item = host_window->SkipItems;
13538     if (!node->IsDockSpace())
13539     {
13540         host_window->SkipItems = false;
13541         host_window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
13542     }
13543 
13544     // Use PushOverrideID() instead of PushID() to use the node id _without_ the host window ID.
13545     // This is to facilitate computing those ID from the outside, and will affect more or less only the ID of the collapse button, popup and tabs,
13546     // as docked windows themselves will override the stack with their own root ID.
13547     PushOverrideID(node->ID);
13548     ImGuiTabBar* tab_bar = node->TabBar;
13549     bool tab_bar_is_recreated = (tab_bar == NULL); // Tab bar are automatically destroyed when a node gets hidden
13550     if (tab_bar == NULL)
13551     {
13552         DockNodeAddTabBar(node);
13553         tab_bar = node->TabBar;
13554     }
13555 
13556     ImGuiID focus_tab_id = 0;
13557     node->IsFocused = is_focused;
13558 
13559     const ImGuiDockNodeFlags node_flags = node->GetMergedFlags();
13560     const bool has_window_menu_button = (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0 && (style.WindowMenuButtonPosition != ImGuiDir_None);
13561     const bool has_close_button = (node_flags & ImGuiDockNodeFlags_NoCloseButton) == 0;
13562 
13563     // In a dock node, the Collapse Button turns into the Window Menu button.
13564     // FIXME-DOCK FIXME-OPT: Could we recycle popups id across multiple dock nodes?
13565     if (has_window_menu_button && IsPopupOpen("#WindowMenu"))
13566     {
13567         if (ImGuiID tab_id = DockNodeUpdateWindowMenu(node, tab_bar))
13568             focus_tab_id = tab_bar->NextSelectedTabId = tab_id;
13569         is_focused |= node->IsFocused;
13570     }
13571 
13572     // Layout
13573     ImRect title_bar_rect, tab_bar_rect;
13574     ImVec2 window_menu_button_pos;
13575     DockNodeCalcTabBarLayout(node, &title_bar_rect, &tab_bar_rect, &window_menu_button_pos);
13576 
13577     // Submit new tabs and apply NavWindow focus back to the tab bar. They will be added as Unsorted and sorted below based on relative DockOrder value.
13578     const int tabs_count_old = tab_bar->Tabs.Size;
13579     for (int window_n = 0; window_n < node->Windows.Size; window_n++)
13580     {
13581         ImGuiWindow* window = node->Windows[window_n];
13582         if (g.NavWindow && g.NavWindow->RootWindowDockStop == window)
13583             tab_bar->SelectedTabId = window->ID;
13584         if (TabBarFindTabByID(tab_bar, window->ID) == NULL)
13585             TabBarAddTab(tab_bar, ImGuiTabItemFlags_Unsorted, window);
13586     }
13587 
13588     // Title bar
13589     if (is_focused)
13590         node->LastFrameFocused = g.FrameCount;
13591     ImU32 title_bar_col = GetColorU32(host_window->Collapsed ? ImGuiCol_TitleBgCollapsed : is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
13592     host_window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window->WindowRounding, ImDrawCornerFlags_Top);
13593 
13594     // Docking/Collapse button
13595     if (has_window_menu_button)
13596     {
13597         if (CollapseButton(host_window->GetID("#COLLAPSE"), window_menu_button_pos, node))
13598             OpenPopup("#WindowMenu");
13599         if (IsItemActive())
13600             focus_tab_id = tab_bar->SelectedTabId;
13601     }
13602 
13603     // If multiple tabs are appearing on the same frame, sort them based on their persistent DockOrder value
13604     int tabs_unsorted_start = tab_bar->Tabs.Size;
13605     for (int tab_n = tab_bar->Tabs.Size - 1; tab_n >= 0 && (tab_bar->Tabs[tab_n].Flags & ImGuiTabItemFlags_Unsorted); tab_n--)
13606     {
13607         // FIXME-DOCK: Consider only clearing the flag after the tab has been alive for a few consecutive frames, allowing late comers to not break sorting?
13608         tab_bar->Tabs[tab_n].Flags &= ~ImGuiTabItemFlags_Unsorted;
13609         tabs_unsorted_start = tab_n;
13610     }
13611     if (tab_bar->Tabs.Size > tabs_unsorted_start)
13612     {
13613         IMGUI_DEBUG_LOG_DOCKING("In node 0x%08X: %d new appearing tabs:%s\n", node->ID, tab_bar->Tabs.Size - tabs_unsorted_start, (tab_bar->Tabs.Size > tabs_unsorted_start + 1) ? " (will sort)" : "");
13614         for (int tab_n = tabs_unsorted_start; tab_n < tab_bar->Tabs.Size; tab_n++)
13615             IMGUI_DEBUG_LOG_DOCKING(" - Tab '%s' Order %d\n", tab_bar->Tabs[tab_n].Window->Name, tab_bar->Tabs[tab_n].Window->DockOrder);
13616         if (tab_bar->Tabs.Size > tabs_unsorted_start + 1)
13617             ImQsort(tab_bar->Tabs.Data + tabs_unsorted_start, tab_bar->Tabs.Size - tabs_unsorted_start, sizeof(ImGuiTabItem), TabItemComparerByDockOrder);
13618     }
13619 
13620     // Selected newly added tabs, or persistent tab ID if the tab bar was just recreated
13621     if (tab_bar_is_recreated && TabBarFindTabByID(tab_bar, node->SelectedTabId) != NULL)
13622         tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = node->SelectedTabId;
13623     else if (tab_bar->Tabs.Size > tabs_count_old)
13624         tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = tab_bar->Tabs.back().Window->ID;
13625 
13626     // Begin tab bar
13627     ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_AutoSelectNewTabs; // | ImGuiTabBarFlags_NoTabListScrollingButtons);
13628     tab_bar_flags |= ImGuiTabBarFlags_SaveSettings | ImGuiTabBarFlags_DockNode;
13629     if (!host_window->Collapsed && is_focused)
13630         tab_bar_flags |= ImGuiTabBarFlags_IsFocused;
13631     BeginTabBarEx(tab_bar, tab_bar_rect, tab_bar_flags, node);
13632     //host_window->DrawList->AddRect(tab_bar_rect.Min, tab_bar_rect.Max, IM_COL32(255,0,255,255));
13633 
13634     // Backup style colors
13635     ImVec4 backup_style_cols[ImGuiWindowDockStyleCol_COUNT];
13636     for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
13637         backup_style_cols[color_n] = g.Style.Colors[GWindowDockStyleColors[color_n]];
13638 
13639     // Submit actual tabs
13640     node->VisibleWindow = NULL;
13641     for (int window_n = 0; window_n < node->Windows.Size; window_n++)
13642     {
13643         ImGuiWindow* window = node->Windows[window_n];
13644         if ((closed_all || closed_one == window->ID) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument))
13645             continue;
13646         if (window->LastFrameActive + 1 >= g.FrameCount || !node_was_active)
13647         {
13648             ImGuiTabItemFlags tab_item_flags = 0;
13649             tab_item_flags |= window->WindowClass.TabItemFlagsOverrideSet;
13650             if (window->Flags & ImGuiWindowFlags_UnsavedDocument)
13651                 tab_item_flags |= ImGuiTabItemFlags_UnsavedDocument;
13652             if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)
13653                 tab_item_flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton;
13654 
13655             // Apply stored style overrides for the window
13656             for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
13657                 g.Style.Colors[GWindowDockStyleColors[color_n]] = ColorConvertU32ToFloat4(window->DockStyle.Colors[color_n]);
13658 
13659             bool tab_open = true;
13660             TabItemEx(tab_bar, window->Name, window->HasCloseButton ? &tab_open : NULL, tab_item_flags, window);
13661             if (!tab_open)
13662                 node->WantCloseTabId = window->ID;
13663             if (tab_bar->VisibleTabId == window->ID)
13664                 node->VisibleWindow = window;
13665 
13666             // Store last item data so it can be queried with IsItemXXX functions after the user Begin() call
13667             window->DockTabItemStatusFlags = host_window->DC.LastItemStatusFlags;
13668             window->DockTabItemRect = host_window->DC.LastItemRect;
13669 
13670             // Update navigation ID on menu layer
13671             if (g.NavWindow && g.NavWindow->RootWindowDockStop == window && (window->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) == 0)
13672                 host_window->NavLastIds[1] = window->ID;
13673         }
13674     }
13675 
13676     // Restore style colors
13677     for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
13678         g.Style.Colors[GWindowDockStyleColors[color_n]] = backup_style_cols[color_n];
13679 
13680     // Notify root of visible window (used to display title in OS task bar)
13681     if (node->VisibleWindow)
13682         if (is_focused || root_node->VisibleWindow == NULL)
13683             root_node->VisibleWindow = node->VisibleWindow;
13684 
13685     // Close button (after VisibleWindow was updated)
13686     // Note that VisibleWindow may have been overrided by CTRL+Tabbing, so VisibleWindow->ID may be != from tab_bar->SelectedTabId
13687     if (has_close_button && node->VisibleWindow)
13688     {
13689         if (!node->VisibleWindow->HasCloseButton)
13690         {
13691             PushItemFlag(ImGuiItemFlags_Disabled, true);
13692             PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_Text] * ImVec4(1.0f,1.0f,1.0f,0.5f));
13693         }
13694         const float button_sz = g.FontSize;
13695         if (CloseButton(host_window->GetID("#CLOSE"), title_bar_rect.GetTR() + ImVec2(-style.FramePadding.x * 2.0f - button_sz, 0.0f)))
13696             if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_bar->VisibleTabId))
13697             {
13698                 node->WantCloseTabId = tab->ID;
13699                 TabBarCloseTab(tab_bar, tab);
13700             }
13701         //if (IsItemActive())
13702         //    focus_tab_id = tab_bar->SelectedTabId;
13703         if (!node->VisibleWindow->HasCloseButton)
13704         {
13705             PopStyleColor();
13706             PopItemFlag();
13707         }
13708     }
13709 
13710     // When clicking on the title bar outside of tabs, we still focus the selected tab for that node
13711     // FIXME: TabItem use AllowItemOverlap so we manually perform a more specific test for now (hovered || held)
13712     ImGuiID title_bar_id = host_window->GetID("#TITLEBAR");
13713     if (g.HoveredId == 0 || g.HoveredId == title_bar_id || g.ActiveId == title_bar_id)
13714     {
13715         bool held;
13716         ButtonBehavior(title_bar_rect, title_bar_id, NULL, &held, ImGuiButtonFlags_AllowItemOverlap);
13717         if (g.HoveredId == title_bar_id)
13718         {
13719             // ImGuiButtonFlags_AllowItemOverlap + SetItemAllowOverlap() required for appending into dock node tab bar,
13720             // otherwise dragging window will steal HoveredId and amended tabs cannot get them.
13721             host_window->DC.LastItemId = title_bar_id;
13722             SetItemAllowOverlap();
13723         }
13724         if (held)
13725         {
13726             if (IsMouseClicked(0))
13727                 focus_tab_id = tab_bar->SelectedTabId;
13728 
13729             // Forward moving request to selected window
13730             if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId))
13731                 StartMouseMovingWindowOrNode(tab->Window ? tab->Window : node->HostWindow, node, false);
13732         }
13733     }
13734 
13735     // Forward focus from host node to selected window
13736     //if (is_focused && g.NavWindow == host_window && !g.NavWindowingTarget)
13737     //    focus_tab_id = tab_bar->SelectedTabId;
13738 
13739     // When clicked on a tab we requested focus to the docked child
13740     // This overrides the value set by "forward focus from host node to selected window".
13741     if (tab_bar->NextSelectedTabId)
13742         focus_tab_id = tab_bar->NextSelectedTabId;
13743 
13744     // Apply navigation focus
13745     if (focus_tab_id != 0)
13746         if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, focus_tab_id))
13747             if (tab->Window)
13748             {
13749                 FocusWindow(tab->Window);
13750                 NavInitWindow(tab->Window, false);
13751             }
13752 
13753     EndTabBar();
13754     PopID();
13755 
13756     // Restore SkipItems flag
13757     if (!node->IsDockSpace())
13758     {
13759         host_window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
13760         host_window->SkipItems = backup_skip_item;
13761     }
13762 }
13763 
DockNodeAddTabBar(ImGuiDockNode * node)13764 static void ImGui::DockNodeAddTabBar(ImGuiDockNode* node)
13765 {
13766     IM_ASSERT(node->TabBar == NULL);
13767     node->TabBar = IM_NEW(ImGuiTabBar);
13768 }
13769 
DockNodeRemoveTabBar(ImGuiDockNode * node)13770 static void ImGui::DockNodeRemoveTabBar(ImGuiDockNode* node)
13771 {
13772     if (node->TabBar == NULL)
13773         return;
13774     IM_DELETE(node->TabBar);
13775     node->TabBar = NULL;
13776 }
13777 
DockNodeIsDropAllowedOne(ImGuiWindow * payload,ImGuiWindow * host_window)13778 static bool DockNodeIsDropAllowedOne(ImGuiWindow* payload, ImGuiWindow* host_window)
13779 {
13780     if (host_window->DockNodeAsHost && host_window->DockNodeAsHost->IsDockSpace() && payload->BeginOrderWithinContext < host_window->BeginOrderWithinContext)
13781         return false;
13782 
13783     ImGuiWindowClass* host_class = host_window->DockNodeAsHost ? &host_window->DockNodeAsHost->WindowClass : &host_window->WindowClass;
13784     ImGuiWindowClass* payload_class = &payload->WindowClass;
13785     if (host_class->ClassId != payload_class->ClassId)
13786     {
13787         if (host_class->ClassId != 0 && host_class->DockingAllowUnclassed && payload_class->ClassId == 0)
13788             return true;
13789         if (payload_class->ClassId != 0 && payload_class->DockingAllowUnclassed && host_class->ClassId == 0)
13790             return true;
13791         return false;
13792     }
13793 
13794     return true;
13795 }
13796 
DockNodeIsDropAllowed(ImGuiWindow * host_window,ImGuiWindow * root_payload)13797 static bool ImGui::DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* root_payload)
13798 {
13799     if (root_payload->DockNodeAsHost && root_payload->DockNodeAsHost->IsSplitNode())
13800         return true;
13801 
13802     const int payload_count = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows.Size : 1;
13803     for (int payload_n = 0; payload_n < payload_count; payload_n++)
13804     {
13805         ImGuiWindow* payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows[payload_n] : root_payload;
13806         if (DockNodeIsDropAllowedOne(payload, host_window))
13807             return true;
13808     }
13809     return false;
13810 }
13811 
13812 // window menu button == collapse button when not in a dock node.
13813 // FIXME: This is similar to RenderWindowTitleBarContents, may want to share code.
DockNodeCalcTabBarLayout(const ImGuiDockNode * node,ImRect * out_title_rect,ImRect * out_tab_bar_rect,ImVec2 * out_window_menu_button_pos)13814 static void ImGui::DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos)
13815 {
13816     ImGuiContext& g = *GImGui;
13817     ImRect r = ImRect(node->Pos.x, node->Pos.y, node->Pos.x + node->Size.x, node->Pos.y + g.FontSize + g.Style.FramePadding.y * 2.0f);
13818     if (out_title_rect) { *out_title_rect = r; }
13819 
13820     ImVec2 window_menu_button_pos = r.Min;
13821     r.Min.x += g.Style.FramePadding.x;
13822     r.Max.x -= g.Style.FramePadding.x;
13823     if (node->HasCloseButton)
13824     {
13825         r.Max.x -= g.FontSize;// +1.0f; // In DockNodeUpdateTabBar() we currently display a disabled close button even if there is none.
13826     }
13827     if (node->HasWindowMenuButton && g.Style.WindowMenuButtonPosition == ImGuiDir_Left)
13828     {
13829         r.Min.x += g.FontSize; // + g.Style.ItemInnerSpacing.x; // <-- Adding ItemInnerSpacing makes the title text moves slightly when in a docking tab bar. Instead we adjusted RenderArrowDockMenu()
13830     }
13831     else if (node->HasWindowMenuButton && g.Style.WindowMenuButtonPosition == ImGuiDir_Right)
13832     {
13833         r.Max.x -= g.FontSize + g.Style.FramePadding.x;
13834         window_menu_button_pos = ImVec2(r.Max.x, r.Min.y);
13835     }
13836     if (out_tab_bar_rect) { *out_tab_bar_rect = r; }
13837     if (out_window_menu_button_pos) { *out_window_menu_button_pos = window_menu_button_pos; }
13838 }
13839 
DockNodeCalcSplitRects(ImVec2 & pos_old,ImVec2 & size_old,ImVec2 & pos_new,ImVec2 & size_new,ImGuiDir dir,ImVec2 size_new_desired)13840 void ImGui::DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired)
13841 {
13842     ImGuiContext& g = *GImGui;
13843     const float dock_spacing = g.Style.ItemInnerSpacing.x;
13844     const ImGuiAxis axis = (dir == ImGuiDir_Left || dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
13845     pos_new[axis ^ 1] = pos_old[axis ^ 1];
13846     size_new[axis ^ 1] = size_old[axis ^ 1];
13847 
13848     // Distribute size on given axis (with a desired size or equally)
13849     const float w_avail = size_old[axis] - dock_spacing;
13850     if (size_new_desired[axis] > 0.0f && size_new_desired[axis] <= w_avail * 0.5f)
13851     {
13852         size_new[axis] = size_new_desired[axis];
13853         size_old[axis] = IM_FLOOR(w_avail - size_new[axis]);
13854     }
13855     else
13856     {
13857         size_new[axis] = IM_FLOOR(w_avail * 0.5f);
13858         size_old[axis] = IM_FLOOR(w_avail - size_new[axis]);
13859     }
13860 
13861     // Position each node
13862     if (dir == ImGuiDir_Right || dir == ImGuiDir_Down)
13863     {
13864         pos_new[axis] = pos_old[axis] + size_old[axis] + dock_spacing;
13865     }
13866     else if (dir == ImGuiDir_Left || dir == ImGuiDir_Up)
13867     {
13868         pos_new[axis] = pos_old[axis];
13869         pos_old[axis] = pos_new[axis] + size_new[axis] + dock_spacing;
13870     }
13871 }
13872 
13873 // Retrieve the drop rectangles for a given direction or for the center + perform hit testing.
DockNodeCalcDropRectsAndTestMousePos(const ImRect & parent,ImGuiDir dir,ImRect & out_r,bool outer_docking,ImVec2 * test_mouse_pos)13874 bool ImGui::DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_r, bool outer_docking, ImVec2* test_mouse_pos)
13875 {
13876     ImGuiContext& g = *GImGui;
13877 
13878     const float parent_smaller_axis = ImMin(parent.GetWidth(), parent.GetHeight());
13879     const float hs_for_central_nodes = ImMin(g.FontSize * 1.5f, ImMax(g.FontSize * 0.5f, parent_smaller_axis / 8.0f));
13880     float hs_w; // Half-size, longer axis
13881     float hs_h; // Half-size, smaller axis
13882     ImVec2 off; // Distance from edge or center
13883     if (outer_docking)
13884     {
13885         //hs_w = ImFloor(ImClamp(parent_smaller_axis - hs_for_central_nodes * 4.0f, g.FontSize * 0.5f, g.FontSize * 8.0f));
13886         //hs_h = ImFloor(hs_w * 0.15f);
13887         //off = ImVec2(ImFloor(parent.GetWidth() * 0.5f - GetFrameHeightWithSpacing() * 1.4f - hs_h), ImFloor(parent.GetHeight() * 0.5f - GetFrameHeightWithSpacing() * 1.4f - hs_h));
13888         hs_w = ImFloor(hs_for_central_nodes * 1.50f);
13889         hs_h = ImFloor(hs_for_central_nodes * 0.80f);
13890         off = ImVec2(ImFloor(parent.GetWidth() * 0.5f - hs_h), ImFloor(parent.GetHeight() * 0.5f - hs_h));
13891     }
13892     else
13893     {
13894         hs_w = ImFloor(hs_for_central_nodes);
13895         hs_h = ImFloor(hs_for_central_nodes * 0.90f);
13896         off = ImVec2(ImFloor(hs_w * 2.40f), ImFloor(hs_w * 2.40f));
13897     }
13898 
13899     ImVec2 c = ImFloor(parent.GetCenter());
13900     if      (dir == ImGuiDir_None)  { out_r = ImRect(c.x - hs_w, c.y - hs_w,         c.x + hs_w, c.y + hs_w);         }
13901     else if (dir == ImGuiDir_Up)    { out_r = ImRect(c.x - hs_w, c.y - off.y - hs_h, c.x + hs_w, c.y - off.y + hs_h); }
13902     else if (dir == ImGuiDir_Down)  { out_r = ImRect(c.x - hs_w, c.y + off.y - hs_h, c.x + hs_w, c.y + off.y + hs_h); }
13903     else if (dir == ImGuiDir_Left)  { out_r = ImRect(c.x - off.x - hs_h, c.y - hs_w, c.x - off.x + hs_h, c.y + hs_w); }
13904     else if (dir == ImGuiDir_Right) { out_r = ImRect(c.x + off.x - hs_h, c.y - hs_w, c.x + off.x + hs_h, c.y + hs_w); }
13905 
13906     if (test_mouse_pos == NULL)
13907         return false;
13908 
13909     ImRect hit_r = out_r;
13910     if (!outer_docking)
13911     {
13912         // Custom hit testing for the 5-way selection, designed to reduce flickering when moving diagonally between sides
13913         hit_r.Expand(ImFloor(hs_w * 0.30f));
13914         ImVec2 mouse_delta = (*test_mouse_pos - c);
13915         float mouse_delta_len2 = ImLengthSqr(mouse_delta);
13916         float r_threshold_center = hs_w * 1.4f;
13917         float r_threshold_sides = hs_w * (1.4f + 1.2f);
13918         if (mouse_delta_len2 < r_threshold_center * r_threshold_center)
13919             return (dir == ImGuiDir_None);
13920         if (mouse_delta_len2 < r_threshold_sides * r_threshold_sides)
13921             return (dir == ImGetDirQuadrantFromDelta(mouse_delta.x, mouse_delta.y));
13922     }
13923     return hit_r.Contains(*test_mouse_pos);
13924 }
13925 
13926 // host_node may be NULL if the window doesn't have a DockNode already.
13927 // FIXME-DOCK: This is misnamed since it's also doing the filtering.
DockNodePreviewDockSetup(ImGuiWindow * host_window,ImGuiDockNode * host_node,ImGuiWindow * root_payload,ImGuiDockPreviewData * data,bool is_explicit_target,bool is_outer_docking)13928 static void ImGui::DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, ImGuiDockPreviewData* data, bool is_explicit_target, bool is_outer_docking)
13929 {
13930     ImGuiContext& g = *GImGui;
13931 
13932     // There is an edge case when docking into a dockspace which only has inactive nodes.
13933     // In this case DockNodeTreeFindNodeByPos() will have selected a leaf node which is inactive.
13934     // Because the inactive leaf node doesn't have proper pos/size yet, we'll use the root node as reference.
13935     ImGuiDockNode* root_payload_as_host = root_payload->DockNodeAsHost;
13936     ImGuiDockNode* ref_node_for_rect = (host_node && !host_node->IsVisible) ? DockNodeGetRootNode(host_node) : host_node;
13937     if (ref_node_for_rect)
13938         IM_ASSERT(ref_node_for_rect->IsVisible);
13939 
13940     // Filter, figure out where we are allowed to dock
13941     ImGuiDockNodeFlags src_node_flags = root_payload_as_host ? root_payload_as_host->GetMergedFlags() : root_payload->WindowClass.DockNodeFlagsOverrideSet;
13942     ImGuiDockNodeFlags dst_node_flags = host_node ? host_node->GetMergedFlags() : host_window->WindowClass.DockNodeFlagsOverrideSet;
13943     data->IsCenterAvailable = true;
13944     if (is_outer_docking)
13945         data->IsCenterAvailable = false;
13946     else if (dst_node_flags & ImGuiDockNodeFlags_NoDocking)
13947         data->IsCenterAvailable = false;
13948     else if (host_node && (dst_node_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) && host_node->IsCentralNode())
13949         data->IsCenterAvailable = false;
13950     else if ((!host_node || !host_node->IsEmpty()) && root_payload_as_host && root_payload_as_host->IsSplitNode() && (root_payload_as_host->OnlyNodeWithWindows == NULL)) // Is _visibly_ split?
13951         data->IsCenterAvailable = false;
13952     else if ((dst_node_flags & ImGuiDockNodeFlags_NoDockingOverMe) || (src_node_flags & ImGuiDockNodeFlags_NoDockingOverOther))
13953         data->IsCenterAvailable = false;
13954 
13955     data->IsSidesAvailable = true;
13956     if ((dst_node_flags & ImGuiDockNodeFlags_NoSplit) || g.IO.ConfigDockingNoSplit)
13957         data->IsSidesAvailable = false;
13958     else if (!is_outer_docking && host_node && host_node->ParentNode == NULL && host_node->IsCentralNode())
13959         data->IsSidesAvailable = false;
13960     else if ((dst_node_flags & ImGuiDockNodeFlags_NoDockingSplitMe) || (src_node_flags & ImGuiDockNodeFlags_NoDockingSplitOther))
13961         data->IsSidesAvailable = false;
13962 
13963     // Build a tentative future node (reuse same structure because it is practical. Shape will be readjusted when previewing a split)
13964     data->FutureNode.HasCloseButton = (host_node ? host_node->HasCloseButton : host_window->HasCloseButton) || (root_payload->HasCloseButton);
13965     data->FutureNode.HasWindowMenuButton = host_node ? true : ((host_window->Flags & ImGuiWindowFlags_NoCollapse) == 0);
13966     data->FutureNode.Pos = ref_node_for_rect ? ref_node_for_rect->Pos : host_window->Pos;
13967     data->FutureNode.Size = ref_node_for_rect ? ref_node_for_rect->Size : host_window->Size;
13968 
13969     // Calculate drop shapes geometry for allowed splitting directions
13970     IM_ASSERT(ImGuiDir_None == -1);
13971     data->SplitNode = host_node;
13972     data->SplitDir = ImGuiDir_None;
13973     data->IsSplitDirExplicit = false;
13974     if (!host_window->Collapsed)
13975         for (int dir = ImGuiDir_None; dir < ImGuiDir_COUNT; dir++)
13976         {
13977             if (dir == ImGuiDir_None && !data->IsCenterAvailable)
13978                 continue;
13979             if (dir != ImGuiDir_None && !data->IsSidesAvailable)
13980                 continue;
13981             if (DockNodeCalcDropRectsAndTestMousePos(data->FutureNode.Rect(), (ImGuiDir)dir, data->DropRectsDraw[dir+1], is_outer_docking, &g.IO.MousePos))
13982             {
13983                 data->SplitDir = (ImGuiDir)dir;
13984                 data->IsSplitDirExplicit = true;
13985             }
13986         }
13987 
13988     // When docking without holding Shift, we only allow and preview docking when hovering over a drop rect or over the title bar
13989     data->IsDropAllowed = (data->SplitDir != ImGuiDir_None) || (data->IsCenterAvailable);
13990     if (!is_explicit_target && !data->IsSplitDirExplicit && !g.IO.ConfigDockingWithShift)
13991         data->IsDropAllowed = false;
13992 
13993     // Calculate split area
13994     data->SplitRatio = 0.0f;
13995     if (data->SplitDir != ImGuiDir_None)
13996     {
13997         ImGuiDir split_dir = data->SplitDir;
13998         ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
13999         ImVec2 pos_new, pos_old = data->FutureNode.Pos;
14000         ImVec2 size_new, size_old = data->FutureNode.Size;
14001         DockNodeCalcSplitRects(pos_old, size_old, pos_new, size_new, split_dir, root_payload->Size);
14002 
14003         // Calculate split ratio so we can pass it down the docking request
14004         float split_ratio = ImSaturate(size_new[split_axis] / data->FutureNode.Size[split_axis]);
14005         data->FutureNode.Pos = pos_new;
14006         data->FutureNode.Size = size_new;
14007         data->SplitRatio = (split_dir == ImGuiDir_Right || split_dir == ImGuiDir_Down) ? (1.0f - split_ratio) : (split_ratio);
14008     }
14009 }
14010 
DockNodePreviewDockRender(ImGuiWindow * host_window,ImGuiDockNode * host_node,ImGuiWindow * root_payload,const ImGuiDockPreviewData * data)14011 static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, const ImGuiDockPreviewData* data)
14012 {
14013     ImGuiContext& g = *GImGui;
14014     IM_ASSERT(g.CurrentWindow == host_window);   // Because we rely on font size to calculate tab sizes
14015 
14016     // With this option, we only display the preview on the target viewport, and the payload viewport is made transparent.
14017     // To compensate for the single layer obstructed by the payload, we'll increase the alpha of the preview nodes.
14018     const bool is_transparent_payload = g.IO.ConfigDockingTransparentPayload;
14019 
14020     // In case the two windows involved are on different viewports, we will draw the overlay on each of them.
14021     int overlay_draw_lists_count = 0;
14022     ImDrawList* overlay_draw_lists[2];
14023     overlay_draw_lists[overlay_draw_lists_count++] = GetForegroundDrawList(host_window->Viewport);
14024     if (host_window->Viewport != root_payload->Viewport && !is_transparent_payload)
14025         overlay_draw_lists[overlay_draw_lists_count++] = GetForegroundDrawList(root_payload->Viewport);
14026 
14027     // Draw main preview rectangle
14028     const ImU32 overlay_col_main = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.60f : 0.40f);
14029     const ImU32 overlay_col_drop = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.90f : 0.70f);
14030     const ImU32 overlay_col_drop_hovered = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 1.20f : 1.00f);
14031     const ImU32 overlay_col_lines = GetColorU32(ImGuiCol_NavWindowingHighlight, is_transparent_payload ? 0.80f : 0.60f);
14032 
14033     // Display area preview
14034     const bool can_preview_tabs = (root_payload->DockNodeAsHost == NULL || root_payload->DockNodeAsHost->Windows.Size > 0);
14035     if (data->IsDropAllowed)
14036     {
14037         ImRect overlay_rect = data->FutureNode.Rect();
14038         if (data->SplitDir == ImGuiDir_None && can_preview_tabs)
14039             overlay_rect.Min.y += GetFrameHeight();
14040         if (data->SplitDir != ImGuiDir_None || data->IsCenterAvailable)
14041             for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++)
14042                 overlay_draw_lists[overlay_n]->AddRectFilled(overlay_rect.Min, overlay_rect.Max, overlay_col_main, host_window->WindowRounding);
14043     }
14044 
14045     // Display tab shape/label preview unless we are splitting node (it generally makes the situation harder to read)
14046     if (data->IsDropAllowed && can_preview_tabs && data->SplitDir == ImGuiDir_None && data->IsCenterAvailable)
14047     {
14048         // Compute target tab bar geometry so we can locate our preview tabs
14049         ImRect tab_bar_rect;
14050         DockNodeCalcTabBarLayout(&data->FutureNode, NULL, &tab_bar_rect, NULL);
14051         ImVec2 tab_pos = tab_bar_rect.Min;
14052         if (host_node && host_node->TabBar)
14053         {
14054             if (!host_node->IsHiddenTabBar() && !host_node->IsNoTabBar())
14055                 tab_pos.x += host_node->TabBar->WidthAllTabs + g.Style.ItemInnerSpacing.x; // We don't use OffsetNewTab because when using non-persistent-order tab bar it is incremented with each Tab submission.
14056             else
14057                 tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_node->Windows[0]->Name, host_node->Windows[0]->HasCloseButton).x;
14058         }
14059         else if (!(host_window->Flags & ImGuiWindowFlags_DockNodeHost))
14060         {
14061             tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_window->Name, host_window->HasCloseButton).x; // Account for slight offset which will be added when changing from title bar to tab bar
14062         }
14063 
14064         // Draw tab shape/label preview (payload may be a loose window or a host window carrying multiple tabbed windows)
14065         if (root_payload->DockNodeAsHost)
14066             IM_ASSERT(root_payload->DockNodeAsHost->Windows.Size <= root_payload->DockNodeAsHost->TabBar->Tabs.Size);
14067         ImGuiTabBar* tab_bar_with_payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->TabBar : NULL;
14068         const int payload_count = tab_bar_with_payload ? tab_bar_with_payload->Tabs.Size : 1;
14069         for (int payload_n = 0; payload_n < payload_count; payload_n++)
14070         {
14071             // DockNode's TabBar may have non-window Tabs manually appended by user
14072             ImGuiWindow* payload_window = tab_bar_with_payload ? tab_bar_with_payload->Tabs[payload_n].Window : root_payload;
14073             if (tab_bar_with_payload && payload_window == NULL)
14074                 continue;
14075             if (!DockNodeIsDropAllowedOne(payload_window, host_window))
14076                 continue;
14077 
14078             // Calculate the tab bounding box for each payload window
14079             ImVec2 tab_size = TabItemCalcSize(payload_window->Name, payload_window->HasCloseButton);
14080             ImRect tab_bb(tab_pos.x, tab_pos.y, tab_pos.x + tab_size.x, tab_pos.y + tab_size.y);
14081             tab_pos.x += tab_size.x + g.Style.ItemInnerSpacing.x;
14082             const ImU32 overlay_col_text = GetColorU32(payload_window->DockStyle.Colors[ImGuiWindowDockStyleCol_Text]);
14083             const ImU32 overlay_col_tabs = GetColorU32(payload_window->DockStyle.Colors[ImGuiWindowDockStyleCol_TabActive]);
14084             PushStyleColor(ImGuiCol_Text, overlay_col_text);
14085             for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++)
14086             {
14087                 ImGuiTabItemFlags tab_flags = ImGuiTabItemFlags_Preview | ((payload_window->Flags & ImGuiWindowFlags_UnsavedDocument) ? ImGuiTabItemFlags_UnsavedDocument : 0);
14088                 if (!tab_bar_rect.Contains(tab_bb))
14089                     overlay_draw_lists[overlay_n]->PushClipRect(tab_bar_rect.Min, tab_bar_rect.Max);
14090                 TabItemBackground(overlay_draw_lists[overlay_n], tab_bb, tab_flags, overlay_col_tabs);
14091                 TabItemLabelAndCloseButton(overlay_draw_lists[overlay_n], tab_bb, tab_flags, g.Style.FramePadding, payload_window->Name, 0, 0, false, NULL, NULL);
14092                 if (!tab_bar_rect.Contains(tab_bb))
14093                     overlay_draw_lists[overlay_n]->PopClipRect();
14094             }
14095             PopStyleColor();
14096         }
14097     }
14098 
14099     // Display drop boxes
14100     const float overlay_rounding = ImMax(3.0f, g.Style.FrameRounding);
14101     for (int dir = ImGuiDir_None; dir < ImGuiDir_COUNT; dir++)
14102     {
14103         if (!data->DropRectsDraw[dir + 1].IsInverted())
14104         {
14105             ImRect draw_r = data->DropRectsDraw[dir + 1];
14106             ImRect draw_r_in = draw_r;
14107             draw_r_in.Expand(-2.0f);
14108             ImU32 overlay_col = (data->SplitDir == (ImGuiDir)dir && data->IsSplitDirExplicit) ? overlay_col_drop_hovered : overlay_col_drop;
14109             for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++)
14110             {
14111                 ImVec2 center = ImFloor(draw_r_in.GetCenter());
14112                 overlay_draw_lists[overlay_n]->AddRectFilled(draw_r.Min, draw_r.Max, overlay_col, overlay_rounding);
14113                 overlay_draw_lists[overlay_n]->AddRect(draw_r_in.Min, draw_r_in.Max, overlay_col_lines, overlay_rounding);
14114                 if (dir == ImGuiDir_Left || dir == ImGuiDir_Right)
14115                     overlay_draw_lists[overlay_n]->AddLine(ImVec2(center.x, draw_r_in.Min.y), ImVec2(center.x, draw_r_in.Max.y), overlay_col_lines);
14116                 if (dir == ImGuiDir_Up || dir == ImGuiDir_Down)
14117                     overlay_draw_lists[overlay_n]->AddLine(ImVec2(draw_r_in.Min.x, center.y), ImVec2(draw_r_in.Max.x, center.y), overlay_col_lines);
14118             }
14119         }
14120 
14121         // Stop after ImGuiDir_None
14122         if ((host_node && (host_node->GetMergedFlags() & ImGuiDockNodeFlags_NoSplit)) || g.IO.ConfigDockingNoSplit)
14123             return;
14124     }
14125 }
14126 
14127 //-----------------------------------------------------------------------------
14128 // Docking: ImGuiDockNode Tree manipulation functions
14129 //-----------------------------------------------------------------------------
14130 // - DockNodeTreeSplit()
14131 // - DockNodeTreeMerge()
14132 // - DockNodeTreeUpdatePosSize()
14133 // - DockNodeTreeUpdateSplitterFindTouchingNode()
14134 // - DockNodeTreeUpdateSplitter()
14135 // - DockNodeTreeFindFallbackLeafNode()
14136 // - DockNodeTreeFindNodeByPos()
14137 //-----------------------------------------------------------------------------
14138 
DockNodeTreeSplit(ImGuiContext * ctx,ImGuiDockNode * parent_node,ImGuiAxis split_axis,int split_inheritor_child_idx,float split_ratio,ImGuiDockNode * new_node)14139 void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_inheritor_child_idx, float split_ratio, ImGuiDockNode* new_node)
14140 {
14141     ImGuiContext& g = *GImGui;
14142     IM_ASSERT(split_axis != ImGuiAxis_None);
14143 
14144     ImGuiDockNode* child_0 = (new_node && split_inheritor_child_idx != 0) ? new_node : DockContextAddNode(ctx, 0);
14145     child_0->ParentNode = parent_node;
14146 
14147     ImGuiDockNode* child_1 = (new_node && split_inheritor_child_idx != 1) ? new_node : DockContextAddNode(ctx, 0);
14148     child_1->ParentNode = parent_node;
14149 
14150     ImGuiDockNode* child_inheritor = (split_inheritor_child_idx == 0) ? child_0 : child_1;
14151     DockNodeMoveChildNodes(child_inheritor, parent_node);
14152     parent_node->ChildNodes[0] = child_0;
14153     parent_node->ChildNodes[1] = child_1;
14154     parent_node->ChildNodes[split_inheritor_child_idx]->VisibleWindow = parent_node->VisibleWindow;
14155     parent_node->SplitAxis = split_axis;
14156     parent_node->VisibleWindow = NULL;
14157     parent_node->AuthorityForPos = parent_node->AuthorityForSize = ImGuiDataAuthority_DockNode;
14158 
14159     float size_avail = (parent_node->Size[split_axis] - DOCKING_SPLITTER_SIZE);
14160     size_avail = ImMax(size_avail, g.Style.WindowMinSize[split_axis] * 2.0f);
14161     IM_ASSERT(size_avail > 0.0f); // If you created a node manually with DockBuilderAddNode(), you need to also call DockBuilderSetNodeSize() before splitting.
14162     child_0->SizeRef = child_1->SizeRef = parent_node->Size;
14163     child_0->SizeRef[split_axis] = ImFloor(size_avail * split_ratio);
14164     child_1->SizeRef[split_axis] = ImFloor(size_avail - child_0->SizeRef[split_axis]);
14165 
14166     DockNodeMoveWindows(parent_node->ChildNodes[split_inheritor_child_idx], parent_node);
14167     DockNodeTreeUpdatePosSize(parent_node, parent_node->Pos, parent_node->Size);
14168 
14169     // Flags transfer (e.g. this is where we transfer the ImGuiDockNodeFlags_CentralNode property)
14170     child_0->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_;
14171     child_1->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_;
14172     child_inheritor->LocalFlags = parent_node->LocalFlags & ImGuiDockNodeFlags_LocalFlagsTransferMask_;
14173     parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_;
14174     if (child_inheritor->IsCentralNode())
14175         DockNodeGetRootNode(parent_node)->CentralNode = child_inheritor;
14176 }
14177 
DockNodeTreeMerge(ImGuiContext * ctx,ImGuiDockNode * parent_node,ImGuiDockNode * merge_lead_child)14178 void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child)
14179 {
14180     // When called from DockContextProcessUndockNode() it is possible that one of the child is NULL.
14181     ImGuiDockNode* child_0 = parent_node->ChildNodes[0];
14182     ImGuiDockNode* child_1 = parent_node->ChildNodes[1];
14183     IM_ASSERT(child_0 || child_1);
14184     IM_ASSERT(merge_lead_child == child_0 || merge_lead_child == child_1);
14185     if ((child_0 && child_0->Windows.Size > 0) || (child_1 && child_1->Windows.Size > 0))
14186     {
14187         IM_ASSERT(parent_node->TabBar == NULL);
14188         IM_ASSERT(parent_node->Windows.Size == 0);
14189     }
14190     IMGUI_DEBUG_LOG_DOCKING("DockNodeTreeMerge 0x%08X & 0x%08X back into parent 0x%08X\n", child_0 ? child_0->ID : 0, child_1 ? child_1->ID : 0, parent_node->ID);
14191 
14192     ImVec2 backup_last_explicit_size = parent_node->SizeRef;
14193     DockNodeMoveChildNodes(parent_node, merge_lead_child);
14194     if (child_0)
14195     {
14196         DockNodeMoveWindows(parent_node, child_0); // Generally only 1 of the 2 child node will have windows
14197         DockSettingsRenameNodeReferences(child_0->ID, parent_node->ID);
14198     }
14199     if (child_1)
14200     {
14201         DockNodeMoveWindows(parent_node, child_1);
14202         DockSettingsRenameNodeReferences(child_1->ID, parent_node->ID);
14203     }
14204     DockNodeApplyPosSizeToWindows(parent_node);
14205     parent_node->AuthorityForPos = parent_node->AuthorityForSize = parent_node->AuthorityForViewport = ImGuiDataAuthority_Auto;
14206     parent_node->VisibleWindow = merge_lead_child->VisibleWindow;
14207     parent_node->SizeRef = backup_last_explicit_size;
14208 
14209     // Flags transfer
14210     parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_; // Preserve Dockspace flag
14211     parent_node->LocalFlags |= (child_0 ? child_0->LocalFlags : 0) & ImGuiDockNodeFlags_LocalFlagsTransferMask_;
14212     parent_node->LocalFlags |= (child_1 ? child_1->LocalFlags : 0) & ImGuiDockNodeFlags_LocalFlagsTransferMask_;
14213 
14214     if (child_0)
14215     {
14216         ctx->DockContext.Nodes.SetVoidPtr(child_0->ID, NULL);
14217         IM_DELETE(child_0);
14218     }
14219     if (child_1)
14220     {
14221         ctx->DockContext.Nodes.SetVoidPtr(child_1->ID, NULL);
14222         IM_DELETE(child_1);
14223     }
14224 }
14225 
14226 // Update Pos/Size for a node hierarchy (don't affect child Windows yet)
DockNodeTreeUpdatePosSize(ImGuiDockNode * node,ImVec2 pos,ImVec2 size,bool only_write_to_marked_nodes)14227 void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, bool only_write_to_marked_nodes)
14228 {
14229     // During the regular dock node update we write to all nodes.
14230     // 'only_write_to_marked_nodes' is only set when turning a node visible mid-frame and we need its size right-away.
14231     const bool write_to_node = (only_write_to_marked_nodes == false) || (node->MarkedForPosSizeWrite);
14232     if (write_to_node)
14233     {
14234         node->Pos = pos;
14235         node->Size = size;
14236     }
14237 
14238     if (node->IsLeafNode())
14239         return;
14240 
14241     ImGuiDockNode* child_0 = node->ChildNodes[0];
14242     ImGuiDockNode* child_1 = node->ChildNodes[1];
14243     ImVec2 child_0_pos = pos, child_1_pos = pos;
14244     ImVec2 child_0_size = size, child_1_size = size;
14245     if (child_0->IsVisible && child_1->IsVisible)
14246     {
14247         const float spacing = DOCKING_SPLITTER_SIZE;
14248         const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis;
14249         const float size_avail = ImMax(size[axis] - spacing, 0.0f);
14250 
14251         // Size allocation policy
14252         // 1) The first 0..WindowMinSize[axis]*2 are allocated evenly to both windows.
14253         ImGuiContext& g = *GImGui;
14254         const float size_min_each = ImFloor(ImMin(size_avail, g.Style.WindowMinSize[axis] * 2.0f) * 0.5f);
14255 
14256         // 2) Process locked absolute size (during a splitter resize we preserve the child of nodes not touching the splitter edge)
14257         if (child_0->WantLockSizeOnce && !child_1->WantLockSizeOnce)
14258         {
14259             child_0_size[axis] = child_0->SizeRef[axis] = ImMin(size_avail - 1.0f, child_0->Size[axis]);
14260             child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]);
14261             IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f);
14262         }
14263         else if (child_1->WantLockSizeOnce && !child_0->WantLockSizeOnce)
14264         {
14265             child_1_size[axis] = child_1->SizeRef[axis] = ImMin(size_avail - 1.0f, child_1->Size[axis]);
14266             child_0_size[axis] = child_0->SizeRef[axis] = (size_avail - child_1_size[axis]);
14267             IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f);
14268         }
14269         else if (child_0->WantLockSizeOnce && child_1->WantLockSizeOnce)
14270         {
14271             // FIXME-DOCK: We cannot honor the requested size, so apply ratio.
14272             // Currently this path will only be taken if code programmatically sets WantLockSizeOnce
14273             float ratio_0 = child_0_size[axis] / (child_0_size[axis] + child_1_size[axis]);
14274             child_0_size[axis] = child_0->SizeRef[axis] = ImFloor(size_avail * ratio_0);
14275             child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]);
14276             IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f);
14277         }
14278 
14279         // 3) If one window is the central node (~ use remaining space, should be made explicit!), use explicit size from the other, and remainder for the central node
14280         else if (child_1->IsCentralNode() && child_0->SizeRef[axis] != 0.0f)
14281         {
14282             child_0_size[axis] = ImMin(size_avail - size_min_each, child_0->SizeRef[axis]);
14283             child_1_size[axis] = (size_avail - child_0_size[axis]);
14284         }
14285         else if (child_0->IsCentralNode() && child_1->SizeRef[axis] != 0.0f)
14286         {
14287             child_1_size[axis] = ImMin(size_avail - size_min_each, child_1->SizeRef[axis]);
14288             child_0_size[axis] = (size_avail - child_1_size[axis]);
14289         }
14290         else
14291         {
14292             // 4) Otherwise distribute according to the relative ratio of each SizeRef value
14293             float split_ratio = child_0->SizeRef[axis] / (child_0->SizeRef[axis] + child_1->SizeRef[axis]);
14294             child_0_size[axis] = ImMax(size_min_each, ImFloor(size_avail * split_ratio + 0.5F));
14295             child_1_size[axis] = (size_avail - child_0_size[axis]);
14296         }
14297 
14298         child_1_pos[axis] += spacing + child_0_size[axis];
14299     }
14300     child_0->WantLockSizeOnce = child_1->WantLockSizeOnce = false;
14301 
14302     if (child_0->IsVisible)
14303         DockNodeTreeUpdatePosSize(child_0, child_0_pos, child_0_size);
14304     if (child_1->IsVisible)
14305         DockNodeTreeUpdatePosSize(child_1, child_1_pos, child_1_size);
14306 }
14307 
DockNodeTreeUpdateSplitterFindTouchingNode(ImGuiDockNode * node,ImGuiAxis axis,int side,ImVector<ImGuiDockNode * > * touching_nodes)14308 static void DockNodeTreeUpdateSplitterFindTouchingNode(ImGuiDockNode* node, ImGuiAxis axis, int side, ImVector<ImGuiDockNode*>* touching_nodes)
14309 {
14310     if (node->IsLeafNode())
14311     {
14312         touching_nodes->push_back(node);
14313         return;
14314     }
14315     if (node->ChildNodes[0]->IsVisible)
14316         if (node->SplitAxis != axis || side == 0 || !node->ChildNodes[1]->IsVisible)
14317             DockNodeTreeUpdateSplitterFindTouchingNode(node->ChildNodes[0], axis, side, touching_nodes);
14318     if (node->ChildNodes[1]->IsVisible)
14319         if (node->SplitAxis != axis || side == 1 || !node->ChildNodes[0]->IsVisible)
14320             DockNodeTreeUpdateSplitterFindTouchingNode(node->ChildNodes[1], axis, side, touching_nodes);
14321 }
14322 
DockNodeTreeUpdateSplitter(ImGuiDockNode * node)14323 void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node)
14324 {
14325     if (node->IsLeafNode())
14326         return;
14327 
14328     ImGuiContext& g = *GImGui;
14329 
14330     ImGuiDockNode* child_0 = node->ChildNodes[0];
14331     ImGuiDockNode* child_1 = node->ChildNodes[1];
14332     if (child_0->IsVisible && child_1->IsVisible)
14333     {
14334         // Bounding box of the splitter cover the space between both nodes (w = Spacing, h = Size[xy^1] for when splitting horizontally)
14335         const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis;
14336         IM_ASSERT(axis != ImGuiAxis_None);
14337         ImRect bb;
14338         bb.Min = child_0->Pos;
14339         bb.Max = child_1->Pos;
14340         bb.Min[axis] += child_0->Size[axis];
14341         bb.Max[axis ^ 1] += child_1->Size[axis ^ 1];
14342         //if (g.IO.KeyCtrl) GetForegroundDrawList(g.CurrentWindow->Viewport)->AddRect(bb.Min, bb.Max, IM_COL32(255,0,255,255));
14343 
14344         const ImGuiDockNodeFlags merged_flags = child_0->GetMergedFlags() | child_1->GetMergedFlags();
14345         const ImGuiDockNodeFlags no_resize_axis_flag = (axis == ImGuiAxis_X) ? ImGuiDockNodeFlags_NoResizeX : ImGuiDockNodeFlags_NoResizeY;
14346         if ((merged_flags & ImGuiDockNodeFlags_NoResize) || (merged_flags & no_resize_axis_flag))
14347         {
14348             ImGuiWindow* window = g.CurrentWindow;
14349             window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator), g.Style.FrameRounding);
14350         }
14351         else
14352         {
14353             //bb.Min[axis] += 1; // Display a little inward so highlight doesn't connect with nearby tabs on the neighbor node.
14354             //bb.Max[axis] -= 1;
14355             PushID(node->ID);
14356 
14357             // Gather list of nodes that are touching the splitter line. Find resizing limits based on those nodes.
14358             ImVector<ImGuiDockNode*> touching_nodes[2];
14359             float min_size = g.Style.WindowMinSize[axis];
14360             float resize_limits[2];
14361             resize_limits[0] = node->ChildNodes[0]->Pos[axis] + min_size;
14362             resize_limits[1] = node->ChildNodes[1]->Pos[axis] + node->ChildNodes[1]->Size[axis] - min_size;
14363 
14364             ImGuiID splitter_id = GetID("##Splitter");
14365             if (g.ActiveId == splitter_id)
14366             {
14367                 // Only process when splitter is active
14368                 DockNodeTreeUpdateSplitterFindTouchingNode(child_0, axis, 1, &touching_nodes[0]);
14369                 DockNodeTreeUpdateSplitterFindTouchingNode(child_1, axis, 0, &touching_nodes[1]);
14370                 for (int touching_node_n = 0; touching_node_n < touching_nodes[0].Size; touching_node_n++)
14371                     resize_limits[0] = ImMax(resize_limits[0], touching_nodes[0][touching_node_n]->Rect().Min[axis] + min_size);
14372                 for (int touching_node_n = 0; touching_node_n < touching_nodes[1].Size; touching_node_n++)
14373                     resize_limits[1] = ImMin(resize_limits[1], touching_nodes[1][touching_node_n]->Rect().Max[axis] - min_size);
14374 
14375                 /*
14376                 // [DEBUG] Render limits
14377                 ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList((ImGuiViewportP*)GetMainViewport());
14378                 for (int n = 0; n < 2; n++)
14379                     if (axis == ImGuiAxis_X)
14380                         draw_list->AddLine(ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y), ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y + node->ChildNodes[n]->Size.y), IM_COL32(255, 0, 255, 255), 3.0f);
14381                     else
14382                         draw_list->AddLine(ImVec2(node->ChildNodes[n]->Pos.x, resize_limits[n]), ImVec2(node->ChildNodes[n]->Pos.x + node->ChildNodes[n]->Size.x, resize_limits[n]), IM_COL32(255, 0, 255, 255), 3.0f);
14383                 */
14384             }
14385 
14386             // Use a short delay before highlighting the splitter (and changing the mouse cursor) in order for regular mouse movement to not highlight many splitters
14387             float cur_size_0 = child_0->Size[axis];
14388             float cur_size_1 = child_1->Size[axis];
14389             float min_size_0 = resize_limits[0] - child_0->Pos[axis];
14390             float min_size_1 = child_1->Pos[axis] + child_1->Size[axis] - resize_limits[1];
14391             if (SplitterBehavior(bb, GetID("##Splitter"), axis, &cur_size_0, &cur_size_1, min_size_0, min_size_1, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS, WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER))
14392             {
14393                 if (touching_nodes[0].Size > 0 && touching_nodes[1].Size > 0)
14394                 {
14395                     child_0->Size[axis] = child_0->SizeRef[axis] = cur_size_0;
14396                     child_1->Pos[axis] -= cur_size_1 - child_1->Size[axis];
14397                     child_1->Size[axis] = child_1->SizeRef[axis] = cur_size_1;
14398 
14399                     // Lock the size of every node that is a sibling of the node we are touching
14400                     // This might be less desirable if we can merge sibling of a same axis into the same parental level.
14401                     for (int side_n = 0; side_n < 2; side_n++)
14402                         for (int touching_node_n = 0; touching_node_n < touching_nodes[side_n].Size; touching_node_n++)
14403                         {
14404                             ImGuiDockNode* touching_node = touching_nodes[side_n][touching_node_n];
14405                             //ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList((ImGuiViewportP*)GetMainViewport());
14406                             //draw_list->AddRect(touching_node->Pos, touching_node->Pos + touching_node->Size, IM_COL32(255, 128, 0, 255));
14407                             while (touching_node->ParentNode != node)
14408                             {
14409                                 if (touching_node->ParentNode->SplitAxis == axis)
14410                                 {
14411                                     // Mark other node so its size will be preserved during the upcoming call to DockNodeTreeUpdatePosSize().
14412                                     ImGuiDockNode* node_to_preserve = touching_node->ParentNode->ChildNodes[side_n];
14413                                     node_to_preserve->WantLockSizeOnce = true;
14414                                     //draw_list->AddRect(touching_node->Pos, touching_node->Rect().Max, IM_COL32(255, 0, 0, 255));
14415                                     //draw_list->AddRectFilled(node_to_preserve->Pos, node_to_preserve->Rect().Max, IM_COL32(0, 255, 0, 100));
14416                                 }
14417                                 touching_node = touching_node->ParentNode;
14418                             }
14419                         }
14420 
14421                     DockNodeTreeUpdatePosSize(child_0, child_0->Pos, child_0->Size);
14422                     DockNodeTreeUpdatePosSize(child_1, child_1->Pos, child_1->Size);
14423                     MarkIniSettingsDirty();
14424                 }
14425             }
14426             PopID();
14427         }
14428     }
14429 
14430     if (child_0->IsVisible)
14431         DockNodeTreeUpdateSplitter(child_0);
14432     if (child_1->IsVisible)
14433         DockNodeTreeUpdateSplitter(child_1);
14434 }
14435 
DockNodeTreeFindFallbackLeafNode(ImGuiDockNode * node)14436 ImGuiDockNode* ImGui::DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node)
14437 {
14438     if (node->IsLeafNode())
14439         return node;
14440     if (ImGuiDockNode* leaf_node = DockNodeTreeFindFallbackLeafNode(node->ChildNodes[0]))
14441         return leaf_node;
14442     if (ImGuiDockNode* leaf_node = DockNodeTreeFindFallbackLeafNode(node->ChildNodes[1]))
14443         return leaf_node;
14444     return NULL;
14445 }
14446 
DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode * node,ImVec2 pos)14447 ImGuiDockNode* ImGui::DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos)
14448 {
14449     if (!node->IsVisible)
14450         return NULL;
14451 
14452     const float dock_spacing = 0.0f;// g.Style.ItemInnerSpacing.x; // FIXME: Relation to DOCKING_SPLITTER_SIZE?
14453     ImRect r(node->Pos, node->Pos + node->Size);
14454     r.Expand(dock_spacing * 0.5f);
14455     bool inside = r.Contains(pos);
14456     if (!inside)
14457         return NULL;
14458 
14459     if (node->IsLeafNode())
14460         return node;
14461     if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(node->ChildNodes[0], pos))
14462         return hovered_node;
14463     if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(node->ChildNodes[1], pos))
14464         return hovered_node;
14465 
14466     return NULL;
14467 }
14468 
14469 //-----------------------------------------------------------------------------
14470 // Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport)
14471 //-----------------------------------------------------------------------------
14472 // - SetWindowDock() [Internal]
14473 // - DockSpace()
14474 // - DockSpaceOverViewport()
14475 //-----------------------------------------------------------------------------
14476 
14477 // [Internal] Called via SetNextWindowDockID()
SetWindowDock(ImGuiWindow * window,ImGuiID dock_id,ImGuiCond cond)14478 void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond)
14479 {
14480     // Test condition (NB: bit 0 is always true) and clear flags for next time
14481     if (cond && (window->SetWindowDockAllowFlags & cond) == 0)
14482         return;
14483     window->SetWindowDockAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
14484 
14485     if (window->DockId == dock_id)
14486         return;
14487 
14488     // If the user attempt to set a dock id that is a split node, we'll dig within to find a suitable docking spot
14489     ImGuiContext* ctx = GImGui;
14490     if (ImGuiDockNode* new_node = DockContextFindNodeByID(ctx, dock_id))
14491         if (new_node->IsSplitNode())
14492         {
14493             // Policy: Find central node or latest focused node. We first move back to our root node.
14494             new_node = DockNodeGetRootNode(new_node);
14495             if (new_node->CentralNode)
14496             {
14497                 IM_ASSERT(new_node->CentralNode->IsCentralNode());
14498                 dock_id = new_node->CentralNode->ID;
14499             }
14500             else
14501             {
14502                 dock_id = new_node->LastFocusedNodeId;
14503             }
14504         }
14505 
14506     if (window->DockId == dock_id)
14507         return;
14508 
14509     if (window->DockNode)
14510         DockNodeRemoveWindow(window->DockNode, window, 0);
14511     window->DockId = dock_id;
14512 }
14513 
14514 // Create an explicit dockspace node within an existing window. Also expose dock node flags and creates a CentralNode by default.
14515 // The Central Node is always displayed even when empty and shrink/extend according to the requested size of its neighbors.
14516 // DockSpace() needs to be submitted _before_ any window they can host. If you use a dockspace, submit it early in your app.
DockSpace(ImGuiID id,const ImVec2 & size_arg,ImGuiDockNodeFlags flags,const ImGuiWindowClass * window_class)14517 void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags flags, const ImGuiWindowClass* window_class)
14518 {
14519     ImGuiContext* ctx = GImGui;
14520     ImGuiContext& g = *ctx;
14521     ImGuiWindow* window = GetCurrentWindow();
14522     if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
14523         return;
14524 
14525     // Early out if parent window is hidden/collapsed
14526     // This is faster but also DockNodeUpdateTabBar() relies on TabBarLayout() running (which won't if SkipItems=true) to set NextSelectedTabId = 0). See #2960.
14527     // If for whichever reason this is causing problem we would need to ensure that DockNodeUpdateTabBar() ends up clearing NextSelectedTabId even if SkipItems=true.
14528     if (window->SkipItems)
14529         flags |= ImGuiDockNodeFlags_KeepAliveOnly;
14530 
14531     IM_ASSERT((flags & ImGuiDockNodeFlags_DockSpace) == 0);
14532     IM_ASSERT(id != 0);
14533     ImGuiDockNode* node = DockContextFindNodeByID(ctx, id);
14534     if (!node)
14535     {
14536         IMGUI_DEBUG_LOG_DOCKING("DockSpace: dockspace node 0x%08X created\n", id);
14537         node = DockContextAddNode(ctx, id);
14538         node->LocalFlags |= ImGuiDockNodeFlags_CentralNode;
14539     }
14540     if (window_class && window_class->ClassId != node->WindowClass.ClassId)
14541         IMGUI_DEBUG_LOG_DOCKING("DockSpace: dockspace node 0x%08X: setup WindowClass 0x%08X -> 0x%08X\n", id, node->WindowClass.ClassId, window_class->ClassId);
14542     node->SharedFlags = flags;
14543     node->WindowClass = window_class ? *window_class : ImGuiWindowClass();
14544 
14545     // When a DockSpace transitioned form implicit to explicit this may be called a second time
14546     // It is possible that the node has already been claimed by a docked window which appeared before the DockSpace() node, so we overwrite IsDockSpace again.
14547     if (node->LastFrameActive == g.FrameCount && !(flags & ImGuiDockNodeFlags_KeepAliveOnly))
14548     {
14549         IM_ASSERT(node->IsDockSpace() == false && "Cannot call DockSpace() twice a frame with the same ID");
14550         node->LocalFlags |= ImGuiDockNodeFlags_DockSpace;
14551         return;
14552     }
14553     node->LocalFlags |= ImGuiDockNodeFlags_DockSpace;
14554 
14555     // Keep alive mode, this is allow windows docked into this node so stay docked even if they are not visible
14556     if (flags & ImGuiDockNodeFlags_KeepAliveOnly)
14557     {
14558         node->LastFrameAlive = g.FrameCount;
14559         return;
14560     }
14561 
14562     const ImVec2 content_avail = GetContentRegionAvail();
14563     ImVec2 size = ImFloor(size_arg);
14564     if (size.x <= 0.0f)
14565         size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
14566     if (size.y <= 0.0f)
14567         size.y = ImMax(content_avail.y + size.y, 4.0f);
14568     IM_ASSERT(size.x > 0.0f && size.y > 0.0f);
14569 
14570     node->Pos = window->DC.CursorPos;
14571     node->Size = node->SizeRef = size;
14572     SetNextWindowPos(node->Pos);
14573     SetNextWindowSize(node->Size);
14574     g.NextWindowData.PosUndock = false;
14575 
14576     // FIXME-DOCK Why do we need a child window to host a dockspace, could we host it in the existing window?
14577     ImGuiWindowFlags window_flags = ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_DockNodeHost;
14578     window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar;
14579     window_flags |= ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;
14580 
14581     char title[256];
14582     ImFormatString(title, IM_ARRAYSIZE(title), "%s/DockSpace_%08X", window->Name, id);
14583 
14584     // FIXME-DOCK: What is the reason for not simply calling BeginChild()?
14585     if (node->Windows.Size > 0 || node->IsSplitNode())
14586         PushStyleColor(ImGuiCol_ChildBg, IM_COL32(0, 0, 0, 0));
14587     PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0.0f);
14588     Begin(title, NULL, window_flags);
14589     PopStyleVar();
14590     if (node->Windows.Size > 0 || node->IsSplitNode())
14591         PopStyleColor();
14592 
14593     ImGuiWindow* host_window = g.CurrentWindow;
14594     host_window->DockNodeAsHost = node;
14595     host_window->ChildId = window->GetID(title);
14596     node->HostWindow = host_window;
14597     node->OnlyNodeWithWindows = NULL;
14598 
14599     IM_ASSERT(node->IsRootNode());
14600 
14601     // We need to handle the rare case were a central node is missing.
14602     // This can happen if the node was first created manually with DockBuilderAddNode() but _without_ the ImGuiDockNodeFlags_Dockspace.
14603     // Doing it correctly would set the _CentralNode flags, which would then propagate according to subsequent split.
14604     // It would also be ambiguous to attempt to assign a central node while there are split nodes, so we wait until there's a single node remaining.
14605     // The specific sub-property of _CentralNode we are interested in recovering here is the "Don't delete when empty" property,
14606     // as it doesn't make sense for an empty dockspace to not have this property.
14607     if (node->IsLeafNode() && !node->IsCentralNode())
14608         node->LocalFlags |= ImGuiDockNodeFlags_CentralNode;
14609 
14610     // Update the node
14611     DockNodeUpdate(node);
14612 
14613     End();
14614     ItemSize(size);
14615 }
14616 
14617 // Tips: Use with ImGuiDockNodeFlags_PassthruCentralNode!
14618 // The limitation with this call is that your window won't have a menu bar.
14619 // Even though we could pass window flags, it would also require the user to be able to call BeginMenuBar() somehow meaning we can't Begin/End in a single function.
14620 // But you can also use BeginMainMenuBar(). If you really want a menu bar inside the same window as the one hosting the dockspace, you will need to copy this code somewhere and tweak it.
DockSpaceOverViewport(ImGuiViewport * viewport,ImGuiDockNodeFlags dockspace_flags,const ImGuiWindowClass * window_class)14621 ImGuiID ImGui::DockSpaceOverViewport(ImGuiViewport* viewport, ImGuiDockNodeFlags dockspace_flags, const ImGuiWindowClass* window_class)
14622 {
14623     if (viewport == NULL)
14624         viewport = GetMainViewport();
14625 
14626     SetNextWindowPos(viewport->GetWorkPos());
14627     SetNextWindowSize(viewport->GetWorkSize());
14628     SetNextWindowViewport(viewport->ID);
14629 
14630     ImGuiWindowFlags host_window_flags = 0;
14631     host_window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking;
14632     host_window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
14633     if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode)
14634         host_window_flags |= ImGuiWindowFlags_NoBackground;
14635 
14636     char label[32];
14637     ImFormatString(label, IM_ARRAYSIZE(label), "DockSpaceViewport_%08X", viewport->ID);
14638 
14639     PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
14640     PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
14641     PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
14642     Begin(label, NULL, host_window_flags);
14643     PopStyleVar(3);
14644 
14645     ImGuiID dockspace_id = GetID("DockSpace");
14646     DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags, window_class);
14647     End();
14648 
14649     return dockspace_id;
14650 }
14651 
14652 //-----------------------------------------------------------------------------
14653 // Docking: Builder Functions
14654 //-----------------------------------------------------------------------------
14655 // Very early end-user API to manipulate dock nodes.
14656 // Only available in imgui_internal.h. Expect this API to change/break!
14657 // It is expected that those functions are all called _before_ the dockspace node submission.
14658 //-----------------------------------------------------------------------------
14659 // - DockBuilderDockWindow()
14660 // - DockBuilderGetNode()
14661 // - DockBuilderSetNodePos()
14662 // - DockBuilderSetNodeSize()
14663 // - DockBuilderAddNode()
14664 // - DockBuilderRemoveNode()
14665 // - DockBuilderRemoveNodeChildNodes()
14666 // - DockBuilderRemoveNodeDockedWindows()
14667 // - DockBuilderSplitNode()
14668 // - DockBuilderCopyNodeRec()
14669 // - DockBuilderCopyNode()
14670 // - DockBuilderCopyWindowSettings()
14671 // - DockBuilderCopyDockSpace()
14672 // - DockBuilderFinish()
14673 //-----------------------------------------------------------------------------
14674 
DockBuilderDockWindow(const char * window_name,ImGuiID node_id)14675 void ImGui::DockBuilderDockWindow(const char* window_name, ImGuiID node_id)
14676 {
14677     // We don't preserve relative order of multiple docked windows (by clearing DockOrder back to -1)
14678     ImGuiID window_id = ImHashStr(window_name);
14679     if (ImGuiWindow* window = FindWindowByID(window_id))
14680     {
14681         // Apply to created window
14682         SetWindowDock(window, node_id, ImGuiCond_Always);
14683         window->DockOrder = -1;
14684     }
14685     else
14686     {
14687         // Apply to settings
14688         ImGuiWindowSettings* settings = FindWindowSettings(window_id);
14689         if (settings == NULL)
14690             settings = CreateNewWindowSettings(window_name);
14691         settings->DockId = node_id;
14692         settings->DockOrder = -1;
14693     }
14694 }
14695 
DockBuilderGetNode(ImGuiID node_id)14696 ImGuiDockNode* ImGui::DockBuilderGetNode(ImGuiID node_id)
14697 {
14698     ImGuiContext* ctx = GImGui;
14699     return DockContextFindNodeByID(ctx, node_id);
14700 }
14701 
DockBuilderSetNodePos(ImGuiID node_id,ImVec2 pos)14702 void ImGui::DockBuilderSetNodePos(ImGuiID node_id, ImVec2 pos)
14703 {
14704     ImGuiContext* ctx = GImGui;
14705     ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_id);
14706     if (node == NULL)
14707         return;
14708     node->Pos = pos;
14709     node->AuthorityForPos = ImGuiDataAuthority_DockNode;
14710 }
14711 
DockBuilderSetNodeSize(ImGuiID node_id,ImVec2 size)14712 void ImGui::DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size)
14713 {
14714     ImGuiContext* ctx = GImGui;
14715     ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_id);
14716     if (node == NULL)
14717         return;
14718     IM_ASSERT(size.x > 0.0f && size.y > 0.0f);
14719     node->Size = node->SizeRef = size;
14720     node->AuthorityForSize = ImGuiDataAuthority_DockNode;
14721 }
14722 
14723 // Make sure to use the ImGuiDockNodeFlags_DockSpace flag to create a dockspace node! Otherwise this will create a floating node!
14724 // - Floating node: you can then call DockBuilderSetNodePos()/DockBuilderSetNodeSize() to position and size the floating node.
14725 // - Dockspace node: calling DockBuilderSetNodePos() is unnecessary.
14726 // - If you intend to split a node immediately after creation using DockBuilderSplitNode(), make sure to call DockBuilderSetNodeSize() beforehand!
14727 //   For various reason, the splitting code currently needs a base size otherwise space may not be allocated as precisely as you would expect.
14728 // - Use (id == 0) to let the system allocate a node identifier.
14729 // - Existing node with a same id will be removed.
DockBuilderAddNode(ImGuiID id,ImGuiDockNodeFlags flags)14730 ImGuiID ImGui::DockBuilderAddNode(ImGuiID id, ImGuiDockNodeFlags flags)
14731 {
14732     ImGuiContext* ctx = GImGui;
14733     ImGuiDockNode* node = NULL;
14734 
14735     if (id != 0)
14736         DockBuilderRemoveNode(id);
14737 
14738     if (flags & ImGuiDockNodeFlags_DockSpace)
14739     {
14740         DockSpace(id, ImVec2(0, 0), (flags & ~ImGuiDockNodeFlags_DockSpace) | ImGuiDockNodeFlags_KeepAliveOnly);
14741         node = DockContextFindNodeByID(ctx, id);
14742     }
14743     else
14744     {
14745         node = DockContextAddNode(ctx, id);
14746         node->LocalFlags = flags;
14747     }
14748     node->LastFrameAlive = ctx->FrameCount;   // Set this otherwise BeginDocked will undock during the same frame.
14749     return node->ID;
14750 }
14751 
DockBuilderRemoveNode(ImGuiID node_id)14752 void ImGui::DockBuilderRemoveNode(ImGuiID node_id)
14753 {
14754     ImGuiContext* ctx = GImGui;
14755     ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_id);
14756     if (node == NULL)
14757         return;
14758     DockBuilderRemoveNodeDockedWindows(node_id, true);
14759     DockBuilderRemoveNodeChildNodes(node_id);
14760     if (node->IsCentralNode() && node->ParentNode)
14761         node->ParentNode->LocalFlags |= ImGuiDockNodeFlags_CentralNode;
14762     DockContextRemoveNode(ctx, node, true);
14763 }
14764 
14765 // root_id = 0 to remove all, root_id != 0 to remove child of given node.
DockBuilderRemoveNodeChildNodes(ImGuiID root_id)14766 void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id)
14767 {
14768     ImGuiContext* ctx = GImGui;
14769     ImGuiDockContext* dc  = &ctx->DockContext;
14770 
14771     ImGuiDockNode* root_node = root_id ? DockContextFindNodeByID(ctx, root_id) : NULL;
14772     if (root_id && root_node == NULL)
14773         return;
14774     bool has_central_node = false;
14775 
14776     ImGuiDataAuthority backup_root_node_authority_for_pos = root_node ? root_node->AuthorityForPos : ImGuiDataAuthority_Auto;
14777     ImGuiDataAuthority backup_root_node_authority_for_size = root_node ? root_node->AuthorityForSize : ImGuiDataAuthority_Auto;
14778 
14779     // Process active windows
14780     ImVector<ImGuiDockNode*> nodes_to_remove;
14781     for (int n = 0; n < dc->Nodes.Data.Size; n++)
14782         if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
14783         {
14784             bool want_removal = (root_id == 0) || (node->ID != root_id && DockNodeGetRootNode(node)->ID == root_id);
14785             if (want_removal)
14786             {
14787                 if (node->IsCentralNode())
14788                     has_central_node = true;
14789                 if (root_id != 0)
14790                     DockContextQueueNotifyRemovedNode(ctx, node);
14791                 if (root_node)
14792                     DockNodeMoveWindows(root_node, node);
14793                 nodes_to_remove.push_back(node);
14794             }
14795         }
14796 
14797     // DockNodeMoveWindows->DockNodeAddWindow will normally set those when reaching two windows (which is only adequate during interactive merge)
14798     // Make sure we don't lose our current pos/size. (FIXME-DOCK: Consider tidying up that code in DockNodeAddWindow instead)
14799     if (root_node)
14800     {
14801         root_node->AuthorityForPos = backup_root_node_authority_for_pos;
14802         root_node->AuthorityForSize = backup_root_node_authority_for_size;
14803     }
14804 
14805     // Apply to settings
14806     for (ImGuiWindowSettings* settings = ctx->SettingsWindows.begin(); settings != NULL; settings = ctx->SettingsWindows.next_chunk(settings))
14807         if (ImGuiID window_settings_dock_id = settings->DockId)
14808             for (int n = 0; n < nodes_to_remove.Size; n++)
14809                 if (nodes_to_remove[n]->ID == window_settings_dock_id)
14810                 {
14811                     settings->DockId = root_id;
14812                     break;
14813                 }
14814 
14815     // Not really efficient, but easier to destroy a whole hierarchy considering DockContextRemoveNode is attempting to merge nodes
14816     if (nodes_to_remove.Size > 1)
14817         ImQsort(nodes_to_remove.Data, nodes_to_remove.Size, sizeof(ImGuiDockNode*), DockNodeComparerDepthMostFirst);
14818     for (int n = 0; n < nodes_to_remove.Size; n++)
14819         DockContextRemoveNode(ctx, nodes_to_remove[n], false);
14820 
14821     if (root_id == 0)
14822     {
14823         dc->Nodes.Clear();
14824         dc->Requests.clear();
14825     }
14826     else if (has_central_node)
14827     {
14828         root_node->LocalFlags |= ImGuiDockNodeFlags_CentralNode;
14829         root_node->CentralNode = root_node;
14830     }
14831 }
14832 
DockBuilderRemoveNodeDockedWindows(ImGuiID root_id,bool clear_settings_refs)14833 void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiID root_id, bool clear_settings_refs)
14834 {
14835     // Clear references in settings
14836     ImGuiContext* ctx = GImGui;
14837     ImGuiContext& g = *ctx;
14838     if (clear_settings_refs)
14839     {
14840         for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
14841         {
14842             bool want_removal = (root_id == 0) || (settings->DockId == root_id);
14843             if (!want_removal && settings->DockId != 0)
14844                 if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, settings->DockId))
14845                     if (DockNodeGetRootNode(node)->ID == root_id)
14846                         want_removal = true;
14847             if (want_removal)
14848                 settings->DockId = 0;
14849         }
14850     }
14851 
14852     // Clear references in windows
14853     for (int n = 0; n < g.Windows.Size; n++)
14854     {
14855         ImGuiWindow* window = g.Windows[n];
14856         bool want_removal = (root_id == 0) || (window->DockNode && DockNodeGetRootNode(window->DockNode)->ID == root_id) || (window->DockNodeAsHost && window->DockNodeAsHost->ID == root_id);
14857         if (want_removal)
14858         {
14859             const ImGuiID backup_dock_id = window->DockId;
14860             IM_UNUSED(backup_dock_id);
14861             DockContextProcessUndockWindow(ctx, window, clear_settings_refs);
14862             if (!clear_settings_refs)
14863                 IM_ASSERT(window->DockId == backup_dock_id);
14864         }
14865     }
14866 }
14867 
14868 // If 'out_id_at_dir' or 'out_id_at_opposite_dir' are non NULL, the function will write out the ID of the two new nodes created.
14869 // Return value is ID of the node at the specified direction, so same as (*out_id_at_dir) if that pointer is set.
14870 // FIXME-DOCK: We are not exposing nor using split_outer.
DockBuilderSplitNode(ImGuiID id,ImGuiDir split_dir,float size_ratio_for_node_at_dir,ImGuiID * out_id_at_dir,ImGuiID * out_id_at_opposite_dir)14871 ImGuiID ImGui::DockBuilderSplitNode(ImGuiID id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_at_opposite_dir)
14872 {
14873     ImGuiContext* ctx = GImGui;
14874     IM_ASSERT(split_dir != ImGuiDir_None);
14875     IMGUI_DEBUG_LOG_DOCKING("DockBuilderSplitNode node 0x%08X, split_dir %d\n", id, split_dir);
14876 
14877     ImGuiDockNode* node = DockContextFindNodeByID(ctx, id);
14878     if (node == NULL)
14879     {
14880         IM_ASSERT(node != NULL);
14881         return 0;
14882     }
14883 
14884     IM_ASSERT(!node->IsSplitNode()); // Assert if already Split
14885 
14886     ImGuiDockRequest req;
14887     req.Type = ImGuiDockRequestType_Split;
14888     req.DockTargetWindow = NULL;
14889     req.DockTargetNode = node;
14890     req.DockPayload = NULL;
14891     req.DockSplitDir = split_dir;
14892     req.DockSplitRatio = ImSaturate((split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? size_ratio_for_node_at_dir : 1.0f - size_ratio_for_node_at_dir);
14893     req.DockSplitOuter = false;
14894     DockContextProcessDock(ctx, &req);
14895 
14896     ImGuiID id_at_dir = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 0 : 1]->ID;
14897     ImGuiID id_at_opposite_dir = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0]->ID;
14898     if (out_id_at_dir)
14899         *out_id_at_dir = id_at_dir;
14900     if (out_id_at_opposite_dir)
14901         *out_id_at_opposite_dir = id_at_opposite_dir;
14902     return id_at_dir;
14903 }
14904 
DockBuilderCopyNodeRec(ImGuiDockNode * src_node,ImGuiID dst_node_id_if_known,ImVector<ImGuiID> * out_node_remap_pairs)14905 static ImGuiDockNode* DockBuilderCopyNodeRec(ImGuiDockNode* src_node, ImGuiID dst_node_id_if_known, ImVector<ImGuiID>* out_node_remap_pairs)
14906 {
14907     ImGuiContext* ctx = GImGui;
14908     ImGuiDockNode* dst_node = ImGui::DockContextAddNode(ctx, dst_node_id_if_known);
14909     dst_node->SharedFlags = src_node->SharedFlags;
14910     dst_node->LocalFlags = src_node->LocalFlags;
14911     dst_node->Pos = src_node->Pos;
14912     dst_node->Size = src_node->Size;
14913     dst_node->SizeRef = src_node->SizeRef;
14914     dst_node->SplitAxis = src_node->SplitAxis;
14915 
14916     out_node_remap_pairs->push_back(src_node->ID);
14917     out_node_remap_pairs->push_back(dst_node->ID);
14918 
14919     for (int child_n = 0; child_n < IM_ARRAYSIZE(src_node->ChildNodes); child_n++)
14920         if (src_node->ChildNodes[child_n])
14921         {
14922             dst_node->ChildNodes[child_n] = DockBuilderCopyNodeRec(src_node->ChildNodes[child_n], 0, out_node_remap_pairs);
14923             dst_node->ChildNodes[child_n]->ParentNode = dst_node;
14924         }
14925 
14926     IMGUI_DEBUG_LOG_DOCKING("Fork node %08X -> %08X (%d childs)\n", src_node->ID, dst_node->ID, dst_node->IsSplitNode() ? 2 : 0);
14927     return dst_node;
14928 }
14929 
DockBuilderCopyNode(ImGuiID src_node_id,ImGuiID dst_node_id,ImVector<ImGuiID> * out_node_remap_pairs)14930 void ImGui::DockBuilderCopyNode(ImGuiID src_node_id, ImGuiID dst_node_id, ImVector<ImGuiID>* out_node_remap_pairs)
14931 {
14932     ImGuiContext* ctx = GImGui;
14933     IM_ASSERT(src_node_id != 0);
14934     IM_ASSERT(dst_node_id != 0);
14935     IM_ASSERT(out_node_remap_pairs != NULL);
14936 
14937     ImGuiDockNode* src_node = DockContextFindNodeByID(ctx, src_node_id);
14938     IM_ASSERT(src_node != NULL);
14939 
14940     out_node_remap_pairs->clear();
14941     DockBuilderRemoveNode(dst_node_id);
14942     DockBuilderCopyNodeRec(src_node, dst_node_id, out_node_remap_pairs);
14943 
14944     IM_ASSERT((out_node_remap_pairs->Size % 2) == 0);
14945 }
14946 
DockBuilderCopyWindowSettings(const char * src_name,const char * dst_name)14947 void ImGui::DockBuilderCopyWindowSettings(const char* src_name, const char* dst_name)
14948 {
14949     ImGuiWindow* src_window = FindWindowByName(src_name);
14950     if (src_window == NULL)
14951         return;
14952     if (ImGuiWindow* dst_window = FindWindowByName(dst_name))
14953     {
14954         dst_window->Pos = src_window->Pos;
14955         dst_window->Size = src_window->Size;
14956         dst_window->SizeFull = src_window->SizeFull;
14957         dst_window->Collapsed = src_window->Collapsed;
14958     }
14959     else if (ImGuiWindowSettings* dst_settings = FindOrCreateWindowSettings(dst_name))
14960     {
14961         ImVec2ih window_pos_2ih = ImVec2ih(src_window->Pos);
14962         if (src_window->ViewportId != 0 && src_window->ViewportId != IMGUI_VIEWPORT_DEFAULT_ID)
14963         {
14964             dst_settings->ViewportPos = window_pos_2ih;
14965             dst_settings->ViewportId = src_window->ViewportId;
14966             dst_settings->Pos = ImVec2ih(0, 0);
14967         }
14968         else
14969         {
14970             dst_settings->Pos = window_pos_2ih;
14971         }
14972         dst_settings->Size = ImVec2ih(src_window->SizeFull);
14973         dst_settings->Collapsed = src_window->Collapsed;
14974     }
14975 }
14976 
14977 // FIXME: Will probably want to change this signature, in particular how the window remapping pairs are passed.
DockBuilderCopyDockSpace(ImGuiID src_dockspace_id,ImGuiID dst_dockspace_id,ImVector<const char * > * in_window_remap_pairs)14978 void ImGui::DockBuilderCopyDockSpace(ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector<const char*>* in_window_remap_pairs)
14979 {
14980     IM_ASSERT(src_dockspace_id != 0);
14981     IM_ASSERT(dst_dockspace_id != 0);
14982     IM_ASSERT(in_window_remap_pairs != NULL);
14983     IM_ASSERT((in_window_remap_pairs->Size % 2) == 0);
14984 
14985     // Duplicate entire dock
14986     // FIXME: When overwriting dst_dockspace_id, windows that aren't part of our dockspace window class but that are docked in a same node will be split apart,
14987     // whereas we could attempt to at least keep them together in a new, same floating node.
14988     ImVector<ImGuiID> node_remap_pairs;
14989     DockBuilderCopyNode(src_dockspace_id, dst_dockspace_id, &node_remap_pairs);
14990 
14991     // Attempt to transition all the upcoming windows associated to dst_dockspace_id into the newly created hierarchy of dock nodes
14992     // (The windows associated to src_dockspace_id are staying in place)
14993     ImVector<ImGuiID> src_windows;
14994     for (int remap_window_n = 0; remap_window_n < in_window_remap_pairs->Size; remap_window_n += 2)
14995     {
14996         const char* src_window_name = (*in_window_remap_pairs)[remap_window_n];
14997         const char* dst_window_name = (*in_window_remap_pairs)[remap_window_n + 1];
14998         ImGuiID src_window_id = ImHashStr(src_window_name);
14999         src_windows.push_back(src_window_id);
15000 
15001         // Search in the remapping tables
15002         ImGuiID src_dock_id = 0;
15003         if (ImGuiWindow* src_window = FindWindowByID(src_window_id))
15004             src_dock_id = src_window->DockId;
15005         else if (ImGuiWindowSettings* src_window_settings = FindWindowSettings(src_window_id))
15006             src_dock_id = src_window_settings->DockId;
15007         ImGuiID dst_dock_id = 0;
15008         for (int dock_remap_n = 0; dock_remap_n < node_remap_pairs.Size; dock_remap_n += 2)
15009             if (node_remap_pairs[dock_remap_n] == src_dock_id)
15010             {
15011                 dst_dock_id = node_remap_pairs[dock_remap_n + 1];
15012                 //node_remap_pairs[dock_remap_n] = node_remap_pairs[dock_remap_n + 1] = 0; // Clear
15013                 break;
15014             }
15015 
15016         if (dst_dock_id != 0)
15017         {
15018             // Docked windows gets redocked into the new node hierarchy.
15019             IMGUI_DEBUG_LOG_DOCKING("Remap live window '%s' 0x%08X -> '%s' 0x%08X\n", src_window_name, src_dock_id, dst_window_name, dst_dock_id);
15020             DockBuilderDockWindow(dst_window_name, dst_dock_id);
15021         }
15022         else
15023         {
15024             // Floating windows gets their settings transferred (regardless of whether the new window already exist or not)
15025             // When this is leading to a Copy and not a Move, we would get two overlapping floating windows. Could we possibly dock them together?
15026             IMGUI_DEBUG_LOG_DOCKING("Remap window settings '%s' -> '%s'\n", src_window_name, dst_window_name);
15027             DockBuilderCopyWindowSettings(src_window_name, dst_window_name);
15028         }
15029     }
15030 
15031     // Anything else in the source nodes of 'node_remap_pairs' are windows that were docked in src_dockspace_id but are not owned by it (unaffiliated windows, e.g. "ImGui Demo")
15032     // Find those windows and move to them to the cloned dock node. This may be optional?
15033     for (int dock_remap_n = 0; dock_remap_n < node_remap_pairs.Size; dock_remap_n += 2)
15034         if (ImGuiID src_dock_id = node_remap_pairs[dock_remap_n])
15035         {
15036             ImGuiID dst_dock_id = node_remap_pairs[dock_remap_n + 1];
15037             ImGuiDockNode* node = DockBuilderGetNode(src_dock_id);
15038             for (int window_n = 0; window_n < node->Windows.Size; window_n++)
15039             {
15040                 ImGuiWindow* window = node->Windows[window_n];
15041                 if (src_windows.contains(window->ID))
15042                     continue;
15043 
15044                 // Docked windows gets redocked into the new node hierarchy.
15045                 IMGUI_DEBUG_LOG_DOCKING("Remap window '%s' %08X -> %08X\n", window->Name, src_dock_id, dst_dock_id);
15046                 DockBuilderDockWindow(window->Name, dst_dock_id);
15047             }
15048         }
15049 }
15050 
DockBuilderFinish(ImGuiID root_id)15051 void ImGui::DockBuilderFinish(ImGuiID root_id)
15052 {
15053     ImGuiContext* ctx = GImGui;
15054     //DockContextRebuild(ctx);
15055     DockContextBuildAddWindowsToNodes(ctx, root_id);
15056 }
15057 
15058 //-----------------------------------------------------------------------------
15059 // Docking: Begin/End Support Functions (called from Begin/End)
15060 //-----------------------------------------------------------------------------
15061 // - GetWindowAlwaysWantOwnTabBar()
15062 // - DockContextBindNodeToWindow()
15063 // - BeginDocked()
15064 // - BeginDockableDragDropSource()
15065 // - BeginDockableDragDropTarget()
15066 //-----------------------------------------------------------------------------
15067 
GetWindowAlwaysWantOwnTabBar(ImGuiWindow * window)15068 bool ImGui::GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window)
15069 {
15070     ImGuiContext& g = *GImGui;
15071     if (g.IO.ConfigDockingAlwaysTabBar || window->WindowClass.DockingAlwaysTabBar)
15072         if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking)) == 0)
15073             if (!window->IsFallbackWindow)    // We don't support AlwaysTabBar on the fallback/implicit window to avoid unused dock-node overhead/noise
15074                 return true;
15075     return false;
15076 }
15077 
DockContextBindNodeToWindow(ImGuiContext * ctx,ImGuiWindow * window)15078 static ImGuiDockNode* ImGui::DockContextBindNodeToWindow(ImGuiContext* ctx, ImGuiWindow* window)
15079 {
15080     ImGuiContext& g = *ctx;
15081     ImGuiDockNode* node = DockContextFindNodeByID(ctx, window->DockId);
15082     IM_ASSERT(window->DockNode == NULL);
15083 
15084     // We should not be docking into a split node (SetWindowDock should avoid this)
15085     if (node && node->IsSplitNode())
15086     {
15087         DockContextProcessUndockWindow(ctx, window);
15088         return NULL;
15089     }
15090 
15091     // Create node
15092     if (node == NULL)
15093     {
15094         node = DockContextAddNode(ctx, window->DockId);
15095         node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window;
15096         node->LastFrameAlive = g.FrameCount;
15097     }
15098 
15099     // If the node just turned visible and is part of a hierarchy, it doesn't have a Size assigned by DockNodeTreeUpdatePosSize() yet,
15100     // so we're forcing a Pos/Size update from the first ancestor that is already visible (often it will be the root node).
15101     // If we don't do this, the window will be assigned a zero-size on its first frame, which won't ideally warm up the layout.
15102     // This is a little wonky because we don't normally update the Pos/Size of visible node mid-frame.
15103     if (!node->IsVisible)
15104     {
15105         ImGuiDockNode* ancestor_node = node;
15106         while (!ancestor_node->IsVisible)
15107         {
15108             ancestor_node->IsVisible = true;
15109             ancestor_node->MarkedForPosSizeWrite = true;
15110             if (ancestor_node->ParentNode)
15111                 ancestor_node = ancestor_node->ParentNode;
15112         }
15113         IM_ASSERT(ancestor_node->Size.x > 0.0f && ancestor_node->Size.y > 0.0f);
15114         DockNodeTreeUpdatePosSize(ancestor_node, ancestor_node->Pos, ancestor_node->Size, true);
15115     }
15116 
15117     // Add window to node
15118     DockNodeAddWindow(node, window, true);
15119     IM_ASSERT(node == window->DockNode);
15120     return node;
15121 }
15122 
BeginDocked(ImGuiWindow * window,bool * p_open)15123 void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open)
15124 {
15125     ImGuiContext* ctx = GImGui;
15126     ImGuiContext& g = *ctx;
15127 
15128     const bool auto_dock_node = GetWindowAlwaysWantOwnTabBar(window);
15129     if (auto_dock_node)
15130     {
15131         if (window->DockId == 0)
15132         {
15133             IM_ASSERT(window->DockNode == NULL);
15134             window->DockId = DockContextGenNodeID(ctx);
15135         }
15136     }
15137     else
15138     {
15139         // Calling SetNextWindowPos() undock windows by default (by setting PosUndock)
15140         bool want_undock = false;
15141         want_undock |= (window->Flags & ImGuiWindowFlags_NoDocking) != 0;
15142         want_undock |= (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) && (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) && g.NextWindowData.PosUndock;
15143         if (want_undock)
15144         {
15145             DockContextProcessUndockWindow(ctx, window);
15146             return;
15147         }
15148     }
15149 
15150     // Bind to our dock node
15151     ImGuiDockNode* node = window->DockNode;
15152     if (node != NULL)
15153         IM_ASSERT(window->DockId == node->ID);
15154     if (window->DockId != 0 && node == NULL)
15155     {
15156         node = DockContextBindNodeToWindow(ctx, window);
15157         if (node == NULL)
15158             return;
15159     }
15160 
15161 #if 0
15162     // Undock if the ImGuiDockNodeFlags_NoDockingInCentralNode got set
15163     if (node->IsCentralNode && (node->Flags & ImGuiDockNodeFlags_NoDockingInCentralNode))
15164     {
15165         DockContextProcessUndockWindow(ctx, window);
15166         return;
15167     }
15168 #endif
15169 
15170     // Undock if our dockspace node disappeared
15171     // Note how we are testing for LastFrameAlive and NOT LastFrameActive. A DockSpace node can be maintained alive while being inactive with ImGuiDockNodeFlags_KeepAliveOnly.
15172     if (node->LastFrameAlive < g.FrameCount)
15173     {
15174         // If the window has been orphaned, transition the docknode to an implicit node processed in DockContextNewFrameUpdateDocking()
15175         ImGuiDockNode* root_node = DockNodeGetRootNode(node);
15176         if (root_node->LastFrameAlive < g.FrameCount)
15177         {
15178             DockContextProcessUndockWindow(ctx, window);
15179         }
15180         else
15181         {
15182             window->DockIsActive = true;
15183             window->DockTabIsVisible = false;
15184         }
15185         return;
15186     }
15187 
15188     // Store style overrides
15189     for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
15190         window->DockStyle.Colors[color_n] = ColorConvertFloat4ToU32(g.Style.Colors[GWindowDockStyleColors[color_n]]);
15191 
15192     // Fast path return. It is common for windows to hold on a persistent DockId but be the only visible window,
15193     // and never create neither a host window neither a tab bar.
15194     // FIXME-DOCK: replace ->HostWindow NULL compare with something more explicit (~was initially intended as a first frame test)
15195     if (node->HostWindow == NULL)
15196     {
15197         window->DockIsActive = (node->State == ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing);
15198         window->DockTabIsVisible = false;
15199         return;
15200     }
15201 
15202     // We can have zero-sized nodes (e.g. children of a small-size dockspace)
15203     IM_ASSERT(node->HostWindow);
15204     IM_ASSERT(node->IsLeafNode());
15205     IM_ASSERT(node->Size.x >= 0.0f && node->Size.y >= 0.0f);
15206     node->State = ImGuiDockNodeState_HostWindowVisible;
15207 
15208     // Undock if we are submitted earlier than the host window
15209     if (window->BeginOrderWithinContext < node->HostWindow->BeginOrderWithinContext)
15210     {
15211         DockContextProcessUndockWindow(ctx, window);
15212         return;
15213     }
15214 
15215     // Position/Size window
15216     SetNextWindowPos(node->Pos);
15217     SetNextWindowSize(node->Size);
15218     g.NextWindowData.PosUndock = false; // Cancel implicit undocking of SetNextWindowPos()
15219     window->DockIsActive = true;
15220     window->DockTabIsVisible = false;
15221     if (node->SharedFlags & ImGuiDockNodeFlags_KeepAliveOnly)
15222         return;
15223 
15224     // When the window is selected we mark it as visible.
15225     if (node->VisibleWindow == window)
15226         window->DockTabIsVisible = true;
15227 
15228     // Update window flag
15229     IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) == 0);
15230     window->Flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_NoResize;
15231     if (node->IsHiddenTabBar() || node->IsNoTabBar())
15232         window->Flags |= ImGuiWindowFlags_NoTitleBar;
15233     else
15234         window->Flags &= ~ImGuiWindowFlags_NoTitleBar;      // Clear the NoTitleBar flag in case the user set it: confusingly enough we need a title bar height so we are correctly offset, but it won't be displayed!
15235 
15236     // Save new dock order only if the tab bar has been visible once.
15237     // This allows multiple windows to be created in the same frame and have their respective dock orders preserved.
15238     if (node->TabBar && node->TabBar->CurrFrameVisible != -1)
15239         window->DockOrder = (short)DockNodeGetTabOrder(window);
15240 
15241     if ((node->WantCloseAll || node->WantCloseTabId == window->ID) && p_open != NULL)
15242         *p_open = false;
15243 
15244     // Update ChildId to allow returning from Child to Parent with Escape
15245     ImGuiWindow* parent_window = window->DockNode->HostWindow;
15246     window->ChildId = parent_window->GetID(window->Name);
15247 }
15248 
BeginDockableDragDropSource(ImGuiWindow * window)15249 void ImGui::BeginDockableDragDropSource(ImGuiWindow* window)
15250 {
15251     ImGuiContext& g = *GImGui;
15252     IM_ASSERT(g.ActiveId == window->MoveId);
15253     IM_ASSERT(g.MovingWindow == window);
15254 
15255     window->DC.LastItemId = window->MoveId;
15256     window = window->RootWindow;
15257     IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0);
15258     bool is_drag_docking = (g.IO.ConfigDockingWithShift) || ImRect(0, 0, window->SizeFull.x, GetFrameHeight()).Contains(g.ActiveIdClickOffset);
15259     if (is_drag_docking && BeginDragDropSource(ImGuiDragDropFlags_SourceNoPreviewTooltip | ImGuiDragDropFlags_SourceNoHoldToOpenOthers | ImGuiDragDropFlags_SourceAutoExpirePayload))
15260     {
15261         SetDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, &window, sizeof(window));
15262         EndDragDropSource();
15263 
15264         // Store style overrides
15265         for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
15266             window->DockStyle.Colors[color_n] = ColorConvertFloat4ToU32(g.Style.Colors[GWindowDockStyleColors[color_n]]);
15267     }
15268 }
15269 
BeginDockableDragDropTarget(ImGuiWindow * window)15270 void ImGui::BeginDockableDragDropTarget(ImGuiWindow* window)
15271 {
15272     ImGuiContext* ctx = GImGui;
15273     ImGuiContext& g = *ctx;
15274 
15275     //IM_ASSERT(window->RootWindow == window); // May also be a DockSpace
15276     IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0);
15277     if (!g.DragDropActive)
15278         return;
15279     //GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
15280     if (!BeginDragDropTargetCustom(window->Rect(), window->ID))
15281         return;
15282 
15283     // Peek into the payload before calling AcceptDragDropPayload() so we can handle overlapping dock nodes with filtering
15284     // (this is a little unusual pattern, normally most code would call AcceptDragDropPayload directly)
15285     const ImGuiPayload* payload = &g.DragDropPayload;
15286     if (!payload->IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) || !DockNodeIsDropAllowed(window, *(ImGuiWindow**)payload->Data))
15287     {
15288         EndDragDropTarget();
15289         return;
15290     }
15291 
15292     ImGuiWindow* payload_window = *(ImGuiWindow**)payload->Data;
15293     if (AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect))
15294     {
15295         // Select target node
15296         // (Important: we cannot use g.HoveredDockNode here! Because each of our target node have filters based on payload, each candidate drop target will do its own evaluation)
15297         bool dock_into_floating_window = false;
15298         ImGuiDockNode* node = NULL;
15299         if (window->DockNodeAsHost)
15300         {
15301             // Cannot assume that node will != NULL even though we passed the rectangle test: it depends on padding/spacing handled by DockNodeTreeFindVisibleNodeByPos().
15302             node = DockNodeTreeFindVisibleNodeByPos(window->DockNodeAsHost, g.IO.MousePos);
15303 
15304             // There is an edge case when docking into a dockspace which only has _inactive_ nodes (because none of the windows are active)
15305             // In this case we need to fallback into any leaf mode, possibly the central node.
15306             // FIXME-20181220: We should not have to test for IsLeafNode() here but we have another bug to fix first.
15307             if (node && node->IsDockSpace() && node->IsRootNode())
15308                 node = (node->CentralNode && node->IsLeafNode()) ? node->CentralNode : DockNodeTreeFindFallbackLeafNode(node);
15309         }
15310         else
15311         {
15312             if (window->DockNode)
15313                 node = window->DockNode;
15314             else
15315                 dock_into_floating_window = true; // Dock into a regular window
15316         }
15317 
15318         const ImRect explicit_target_rect = (node && node->TabBar && !node->IsHiddenTabBar() && !node->IsNoTabBar()) ? node->TabBar->BarRect : ImRect(window->Pos, window->Pos + ImVec2(window->Size.x, GetFrameHeight()));
15319         const bool is_explicit_target = g.IO.ConfigDockingWithShift || IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max);
15320 
15321         // Preview docking request and find out split direction/ratio
15322         //const bool do_preview = true;     // Ignore testing for payload->IsPreview() which removes one frame of delay, but breaks overlapping drop targets within the same window.
15323         const bool do_preview = payload->IsPreview() || payload->IsDelivery();
15324         if (do_preview && (node != NULL || dock_into_floating_window))
15325         {
15326             ImGuiDockPreviewData split_inner;
15327             ImGuiDockPreviewData split_outer;
15328             ImGuiDockPreviewData* split_data = &split_inner;
15329             if (node && (node->ParentNode || node->IsCentralNode()))
15330                 if (ImGuiDockNode* root_node = DockNodeGetRootNode(node))
15331                 {
15332                     DockNodePreviewDockSetup(window, root_node, payload_window, &split_outer, is_explicit_target, true);
15333                     if (split_outer.IsSplitDirExplicit)
15334                         split_data = &split_outer;
15335                 }
15336             DockNodePreviewDockSetup(window, node, payload_window, &split_inner, is_explicit_target, false);
15337             if (split_data == &split_outer)
15338                 split_inner.IsDropAllowed = false;
15339 
15340             // Draw inner then outer, so that previewed tab (in inner data) will be behind the outer drop boxes
15341             DockNodePreviewDockRender(window, node, payload_window, &split_inner);
15342             DockNodePreviewDockRender(window, node, payload_window, &split_outer);
15343 
15344             // Queue docking request
15345             if (split_data->IsDropAllowed && payload->IsDelivery())
15346                 DockContextQueueDock(ctx, window, split_data->SplitNode, payload_window, split_data->SplitDir, split_data->SplitRatio, split_data == &split_outer);
15347         }
15348     }
15349     EndDragDropTarget();
15350 }
15351 
15352 //-----------------------------------------------------------------------------
15353 // Docking: Settings
15354 //-----------------------------------------------------------------------------
15355 // - DockSettingsRenameNodeReferences()
15356 // - DockSettingsRemoveNodeReferences()
15357 // - DockSettingsFindNodeSettings()
15358 // - DockSettingsHandler_ApplyAll()
15359 // - DockSettingsHandler_ReadOpen()
15360 // - DockSettingsHandler_ReadLine()
15361 // - DockSettingsHandler_DockNodeToSettings()
15362 // - DockSettingsHandler_WriteAll()
15363 //-----------------------------------------------------------------------------
15364 
DockSettingsRenameNodeReferences(ImGuiID old_node_id,ImGuiID new_node_id)15365 static void ImGui::DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id)
15366 {
15367     ImGuiContext& g = *GImGui;
15368     IMGUI_DEBUG_LOG_DOCKING("DockSettingsRenameNodeReferences: from 0x%08X -> to 0x%08X\n", old_node_id, new_node_id);
15369     for (int window_n = 0; window_n < g.Windows.Size; window_n++)
15370     {
15371         ImGuiWindow* window = g.Windows[window_n];
15372         if (window->DockId == old_node_id && window->DockNode == NULL)
15373             window->DockId = new_node_id;
15374     }
15375     //// FIXME-OPT: We could remove this loop by storing the index in the map
15376     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
15377         if (settings->DockId == old_node_id)
15378             settings->DockId = new_node_id;
15379 }
15380 
15381 // Remove references stored in ImGuiWindowSettings to the given ImGuiDockNodeSettings
DockSettingsRemoveNodeReferences(ImGuiID * node_ids,int node_ids_count)15382 static void ImGui::DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ids_count)
15383 {
15384     ImGuiContext& g = *GImGui;
15385     int found = 0;
15386     //// FIXME-OPT: We could remove this loop by storing the index in the map
15387     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
15388         for (int node_n = 0; node_n < node_ids_count; node_n++)
15389             if (settings->DockId == node_ids[node_n])
15390             {
15391                 settings->DockId = 0;
15392                 settings->DockOrder = -1;
15393                 if (++found < node_ids_count)
15394                     break;
15395                 return;
15396             }
15397 }
15398 
DockSettingsFindNodeSettings(ImGuiContext * ctx,ImGuiID id)15399 static ImGuiDockNodeSettings* ImGui::DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID id)
15400 {
15401     // FIXME-OPT
15402     ImGuiDockContext* dc  = &ctx->DockContext;
15403     for (int n = 0; n < dc->NodesSettings.Size; n++)
15404         if (dc->NodesSettings[n].ID == id)
15405             return &dc->NodesSettings[n];
15406     return NULL;
15407 }
15408 
15409 // Clear settings data
DockSettingsHandler_ClearAll(ImGuiContext * ctx,ImGuiSettingsHandler *)15410 static void ImGui::DockSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
15411 {
15412     ImGuiDockContext* dc  = &ctx->DockContext;
15413     dc->NodesSettings.clear();
15414     DockContextClearNodes(ctx, 0, true);
15415 }
15416 
15417 // Recreate nodes based on settings data
DockSettingsHandler_ApplyAll(ImGuiContext * ctx,ImGuiSettingsHandler *)15418 static void ImGui::DockSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
15419 {
15420     // Prune settings at boot time only
15421     ImGuiDockContext* dc  = &ctx->DockContext;
15422     if (ctx->Windows.Size == 0)
15423         DockContextPruneUnusedSettingsNodes(ctx);
15424     DockContextBuildNodesFromSettings(ctx, dc->NodesSettings.Data, dc->NodesSettings.Size);
15425     DockContextBuildAddWindowsToNodes(ctx, 0);
15426 }
15427 
DockSettingsHandler_ReadOpen(ImGuiContext *,ImGuiSettingsHandler *,const char * name)15428 static void* ImGui::DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
15429 {
15430     if (strcmp(name, "Data") != 0)
15431         return NULL;
15432     return (void*)1;
15433 }
15434 
DockSettingsHandler_ReadLine(ImGuiContext * ctx,ImGuiSettingsHandler *,void *,const char * line)15435 static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettingsHandler*, void*, const char* line)
15436 {
15437     char c = 0;
15438     int x = 0, y = 0;
15439     int r = 0;
15440 
15441     // Parsing, e.g.
15442     // " DockNode   ID=0x00000001 Pos=383,193 Size=201,322 Split=Y,0.506 "
15443     // "   DockNode ID=0x00000002 Parent=0x00000001 "
15444     // Important: this code expect currently fields in a fixed order.
15445     ImGuiDockNodeSettings node;
15446     line = ImStrSkipBlank(line);
15447     if      (strncmp(line, "DockNode", 8) == 0)  { line = ImStrSkipBlank(line + strlen("DockNode")); }
15448     else if (strncmp(line, "DockSpace", 9) == 0) { line = ImStrSkipBlank(line + strlen("DockSpace")); node.Flags |= ImGuiDockNodeFlags_DockSpace; }
15449     else return;
15450     if (sscanf(line, "ID=0x%08X%n",      &node.ID, &r) == 1)            { line += r; } else return;
15451     if (sscanf(line, " Parent=0x%08X%n", &node.ParentNodeId, &r) == 1)  { line += r; if (node.ParentNodeId == 0) return; }
15452     if (sscanf(line, " Window=0x%08X%n", &node.ParentWindowId, &r) ==1) { line += r; if (node.ParentWindowId == 0) return; }
15453     if (node.ParentNodeId == 0)
15454     {
15455         if (sscanf(line, " Pos=%i,%i%n",  &x, &y, &r) == 2)         { line += r; node.Pos = ImVec2ih((short)x, (short)y); } else return;
15456         if (sscanf(line, " Size=%i,%i%n", &x, &y, &r) == 2)         { line += r; node.Size = ImVec2ih((short)x, (short)y); } else return;
15457     }
15458     else
15459     {
15460         if (sscanf(line, " SizeRef=%i,%i%n", &x, &y, &r) == 2)      { line += r; node.SizeRef = ImVec2ih((short)x, (short)y); }
15461     }
15462     if (sscanf(line, " Split=%c%n", &c, &r) == 1)                   { line += r; if (c == 'X') node.SplitAxis = ImGuiAxis_X; else if (c == 'Y') node.SplitAxis = ImGuiAxis_Y; }
15463     if (sscanf(line, " NoResize=%d%n", &x, &r) == 1)                { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoResize; }
15464     if (sscanf(line, " CentralNode=%d%n", &x, &r) == 1)             { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_CentralNode; }
15465     if (sscanf(line, " NoTabBar=%d%n", &x, &r) == 1)                { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoTabBar; }
15466     if (sscanf(line, " HiddenTabBar=%d%n", &x, &r) == 1)            { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_HiddenTabBar; }
15467     if (sscanf(line, " NoWindowMenuButton=%d%n", &x, &r) == 1)      { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoWindowMenuButton; }
15468     if (sscanf(line, " NoCloseButton=%d%n", &x, &r) == 1)           { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoCloseButton; }
15469     if (sscanf(line, " Selected=0x%08X%n", &node.SelectedWindowId,&r) == 1) { line += r; }
15470     if (node.ParentNodeId != 0)
15471         if (ImGuiDockNodeSettings* parent_settings = DockSettingsFindNodeSettings(ctx, node.ParentNodeId))
15472             node.Depth = parent_settings->Depth + 1;
15473     ctx->DockContext.NodesSettings.push_back(node);
15474 }
15475 
DockSettingsHandler_DockNodeToSettings(ImGuiDockContext * dc,ImGuiDockNode * node,int depth)15476 static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDockNode* node, int depth)
15477 {
15478     ImGuiDockNodeSettings node_settings;
15479     IM_ASSERT(depth < (1 << (sizeof(node_settings.Depth) << 3)));
15480     node_settings.ID = node->ID;
15481     node_settings.ParentNodeId = node->ParentNode ? node->ParentNode->ID : 0;
15482     node_settings.ParentWindowId = (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow) ? node->HostWindow->ParentWindow->ID : 0;
15483     node_settings.SelectedWindowId = node->SelectedTabId;
15484     node_settings.SplitAxis = (signed char)(node->IsSplitNode() ? node->SplitAxis : ImGuiAxis_None);
15485     node_settings.Depth = (char)depth;
15486     node_settings.Flags = (node->LocalFlags & ImGuiDockNodeFlags_SavedFlagsMask_);
15487     node_settings.Pos = ImVec2ih(node->Pos);
15488     node_settings.Size = ImVec2ih(node->Size);
15489     node_settings.SizeRef = ImVec2ih(node->SizeRef);
15490     dc->NodesSettings.push_back(node_settings);
15491     if (node->ChildNodes[0])
15492         DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[0], depth + 1);
15493     if (node->ChildNodes[1])
15494         DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[1], depth + 1);
15495 }
15496 
DockSettingsHandler_WriteAll(ImGuiContext * ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)15497 static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
15498 {
15499     ImGuiContext& g = *ctx;
15500     ImGuiDockContext* dc = &ctx->DockContext;
15501     if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
15502         return;
15503 
15504     // Gather settings data
15505     // (unlike our windows settings, because nodes are always built we can do a full rewrite of the SettingsNode buffer)
15506     dc->NodesSettings.resize(0);
15507     dc->NodesSettings.reserve(dc->Nodes.Data.Size);
15508     for (int n = 0; n < dc->Nodes.Data.Size; n++)
15509         if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
15510             if (node->IsRootNode())
15511                 DockSettingsHandler_DockNodeToSettings(dc, node, 0);
15512 
15513     int max_depth = 0;
15514     for (int node_n = 0; node_n < dc->NodesSettings.Size; node_n++)
15515         max_depth = ImMax((int)dc->NodesSettings[node_n].Depth, max_depth);
15516 
15517     // Write to text buffer
15518     buf->appendf("[%s][Data]\n", handler->TypeName);
15519     for (int node_n = 0; node_n < dc->NodesSettings.Size; node_n++)
15520     {
15521         const int line_start_pos = buf->size(); (void)line_start_pos;
15522         const ImGuiDockNodeSettings* node_settings = &dc->NodesSettings[node_n];
15523         buf->appendf("%*s%s%*s", node_settings->Depth * 2, "", (node_settings->Flags & ImGuiDockNodeFlags_DockSpace) ? "DockSpace" : "DockNode ", (max_depth - node_settings->Depth) * 2, "");  // Text align nodes to facilitate looking at .ini file
15524         buf->appendf(" ID=0x%08X", node_settings->ID);
15525         if (node_settings->ParentNodeId)
15526         {
15527             buf->appendf(" Parent=0x%08X SizeRef=%d,%d", node_settings->ParentNodeId, node_settings->SizeRef.x, node_settings->SizeRef.y);
15528         }
15529         else
15530         {
15531             if (node_settings->ParentWindowId)
15532                 buf->appendf(" Window=0x%08X", node_settings->ParentWindowId);
15533             buf->appendf(" Pos=%d,%d Size=%d,%d", node_settings->Pos.x, node_settings->Pos.y, node_settings->Size.x, node_settings->Size.y);
15534         }
15535         if (node_settings->SplitAxis != ImGuiAxis_None)
15536             buf->appendf(" Split=%c", (node_settings->SplitAxis == ImGuiAxis_X) ? 'X' : 'Y');
15537         if (node_settings->Flags & ImGuiDockNodeFlags_NoResize)
15538             buf->appendf(" NoResize=1");
15539         if (node_settings->Flags & ImGuiDockNodeFlags_CentralNode)
15540             buf->appendf(" CentralNode=1");
15541         if (node_settings->Flags & ImGuiDockNodeFlags_NoTabBar)
15542             buf->appendf(" NoTabBar=1");
15543         if (node_settings->Flags & ImGuiDockNodeFlags_HiddenTabBar)
15544             buf->appendf(" HiddenTabBar=1");
15545         if (node_settings->Flags & ImGuiDockNodeFlags_NoWindowMenuButton)
15546             buf->appendf(" NoWindowMenuButton=1");
15547         if (node_settings->Flags & ImGuiDockNodeFlags_NoCloseButton)
15548             buf->appendf(" NoCloseButton=1");
15549         if (node_settings->SelectedWindowId)
15550             buf->appendf(" Selected=0x%08X", node_settings->SelectedWindowId);
15551 
15552 #if IMGUI_DEBUG_INI_SETTINGS
15553         // [DEBUG] Include comments in the .ini file to ease debugging
15554         if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_settings->ID))
15555         {
15556             buf->appendf("%*s", ImMax(2, (line_start_pos + 92) - buf->size()), "");     // Align everything
15557             if (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow)
15558                 buf->appendf(" ; in '%s'", node->HostWindow->ParentWindow->Name);
15559             // Iterate settings so we can give info about windows that didn't exist during the session.
15560             int contains_window = 0;
15561             for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
15562                 if (settings->DockId == node_settings->ID)
15563                 {
15564                     if (contains_window++ == 0)
15565                         buf->appendf(" ; contains ");
15566                     buf->appendf("'%s' ", settings->GetName());
15567                 }
15568         }
15569 #endif
15570         buf->appendf("\n");
15571     }
15572     buf->appendf("\n");
15573 }
15574 
15575 
15576 //-----------------------------------------------------------------------------
15577 // [SECTION] PLATFORM DEPENDENT HELPERS
15578 //-----------------------------------------------------------------------------
15579 
15580 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
15581 
15582 #ifdef _MSC_VER
15583 #pragma comment(lib, "user32")
15584 #pragma comment(lib, "kernel32")
15585 #endif
15586 
15587 // Win32 clipboard implementation
15588 // We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown()
GetClipboardTextFn_DefaultImpl(void *)15589 static const char* GetClipboardTextFn_DefaultImpl(void*)
15590 {
15591     ImGuiContext& g = *GImGui;
15592     g.ClipboardHandlerData.clear();
15593     if (!::OpenClipboard(NULL))
15594         return NULL;
15595     HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
15596     if (wbuf_handle == NULL)
15597     {
15598         ::CloseClipboard();
15599         return NULL;
15600     }
15601     if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle))
15602     {
15603         int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL);
15604         g.ClipboardHandlerData.resize(buf_len);
15605         ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL);
15606     }
15607     ::GlobalUnlock(wbuf_handle);
15608     ::CloseClipboard();
15609     return g.ClipboardHandlerData.Data;
15610 }
15611 
SetClipboardTextFn_DefaultImpl(void *,const char * text)15612 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
15613 {
15614     if (!::OpenClipboard(NULL))
15615         return;
15616     const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);
15617     HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR));
15618     if (wbuf_handle == NULL)
15619     {
15620         ::CloseClipboard();
15621         return;
15622     }
15623     WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle);
15624     ::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length);
15625     ::GlobalUnlock(wbuf_handle);
15626     ::EmptyClipboard();
15627     if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
15628         ::GlobalFree(wbuf_handle);
15629     ::CloseClipboard();
15630 }
15631 
15632 #elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS)
15633 
15634 #include <Carbon/Carbon.h>  // Use old API to avoid need for separate .mm file
15635 static PasteboardRef main_clipboard = 0;
15636 
15637 // OSX clipboard implementation
15638 // If you enable this you will need to add '-framework ApplicationServices' to your linker command-line!
SetClipboardTextFn_DefaultImpl(void *,const char * text)15639 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
15640 {
15641     if (!main_clipboard)
15642         PasteboardCreate(kPasteboardClipboard, &main_clipboard);
15643     PasteboardClear(main_clipboard);
15644     CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text));
15645     if (cf_data)
15646     {
15647         PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0);
15648         CFRelease(cf_data);
15649     }
15650 }
15651 
GetClipboardTextFn_DefaultImpl(void *)15652 static const char* GetClipboardTextFn_DefaultImpl(void*)
15653 {
15654     if (!main_clipboard)
15655         PasteboardCreate(kPasteboardClipboard, &main_clipboard);
15656     PasteboardSynchronize(main_clipboard);
15657 
15658     ItemCount item_count = 0;
15659     PasteboardGetItemCount(main_clipboard, &item_count);
15660     for (ItemCount i = 0; i < item_count; i++)
15661     {
15662         PasteboardItemID item_id = 0;
15663         PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id);
15664         CFArrayRef flavor_type_array = 0;
15665         PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array);
15666         for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++)
15667         {
15668             CFDataRef cf_data;
15669             if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr)
15670             {
15671                 ImGuiContext& g = *GImGui;
15672                 g.ClipboardHandlerData.clear();
15673                 int length = (int)CFDataGetLength(cf_data);
15674                 g.ClipboardHandlerData.resize(length + 1);
15675                 CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data);
15676                 g.ClipboardHandlerData[length] = 0;
15677                 CFRelease(cf_data);
15678                 return g.ClipboardHandlerData.Data;
15679             }
15680         }
15681     }
15682     return NULL;
15683 }
15684 
15685 #else
15686 
15687 // Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers.
GetClipboardTextFn_DefaultImpl(void *)15688 static const char* GetClipboardTextFn_DefaultImpl(void*)
15689 {
15690     ImGuiContext& g = *GImGui;
15691     return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin();
15692 }
15693 
SetClipboardTextFn_DefaultImpl(void *,const char * text)15694 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
15695 {
15696     ImGuiContext& g = *GImGui;
15697     g.ClipboardHandlerData.clear();
15698     const char* text_end = text + strlen(text);
15699     g.ClipboardHandlerData.resize((int)(text_end - text) + 1);
15700     memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text));
15701     g.ClipboardHandlerData[(int)(text_end - text)] = 0;
15702 }
15703 
15704 #endif
15705 
15706 //-----------------------------------------------------------------------------
15707 // [SECTION] METRICS/DEBUGGER WINDOW
15708 //-----------------------------------------------------------------------------
15709 // - RenderViewportThumbnail() [Internal]
15710 // - RenderViewportsThumbnails() [Internal]
15711 // - MetricsHelpMarker() [Internal]
15712 // - ShowMetricsWindow()
15713 // - DebugNodeColumns() [Internal]
15714 // - DebugNodeDockNode() [Internal]
15715 // - DebugNodeDrawList() [Internal]
15716 // - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal]
15717 // - DebugNodeStorage() [Internal]
15718 // - DebugNodeTabBar() [Internal]
15719 // - DebugNodeViewport() [Internal]
15720 // - DebugNodeWindow() [Internal]
15721 // - DebugNodeWindowSettings() [Internal]
15722 // - DebugNodeWindowsList() [Internal]
15723 //-----------------------------------------------------------------------------
15724 
15725 #ifndef IMGUI_DISABLE_METRICS_WINDOW
15726 
RenderViewportThumbnail(ImDrawList * draw_list,ImGuiViewportP * viewport,const ImRect & bb)15727 static void RenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb)
15728 {
15729     ImGuiContext& g = *GImGui;
15730     ImGuiWindow* window = g.CurrentWindow;
15731 
15732     ImVec2 scale = bb.GetSize() / viewport->Size;
15733     ImVec2 off = bb.Min - viewport->Pos * scale;
15734     float alpha_mul = (viewport->Flags & ImGuiViewportFlags_Minimized) ? 0.30f : 1.00f;
15735     window->DrawList->AddRectFilled(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f));
15736     for (int i = 0; i != g.Windows.Size; i++)
15737     {
15738         ImGuiWindow* thumb_window = g.Windows[i];
15739         if (!thumb_window->WasActive || ((thumb_window->Flags & ImGuiWindowFlags_ChildWindow)))
15740             continue;
15741         if (thumb_window->SkipItems && (thumb_window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME-DOCK: Skip hidden docked windows. Identify those betters.
15742             continue;
15743         if (thumb_window->Viewport != viewport)
15744             continue;
15745 
15746         ImRect thumb_r = thumb_window->Rect();
15747         ImRect title_r = thumb_window->TitleBarRect();
15748         ImRect thumb_r_scaled = ImRect(ImFloor(off + thumb_r.Min * scale), ImFloor(off +  thumb_r.Max * scale));
15749         ImRect title_r_scaled = 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
15750         thumb_r_scaled.ClipWithFull(bb);
15751         title_r_scaled.ClipWithFull(bb);
15752         const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight);
15753         window->DrawList->AddRectFilled(thumb_r_scaled.Min, thumb_r_scaled.Max, ImGui::GetColorU32(ImGuiCol_WindowBg, alpha_mul));
15754         window->DrawList->AddRectFilled(title_r_scaled.Min, title_r_scaled.Max, ImGui::GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg, alpha_mul));
15755         window->DrawList->AddRect(thumb_r_scaled.Min, thumb_r_scaled.Max, ImGui::GetColorU32(ImGuiCol_Border, alpha_mul));
15756         if (ImGuiWindow* window_for_title = GetWindowForTitleDisplay(thumb_window))
15757             window->DrawList->AddText(g.Font, g.FontSize * 1.0f, title_r_scaled.Min, ImGui::GetColorU32(ImGuiCol_Text, alpha_mul), window_for_title->Name, ImGui::FindRenderedTextEnd(window_for_title->Name));
15758     }
15759     draw_list->AddRect(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_Border, alpha_mul));
15760 }
15761 
RenderViewportsThumbnails()15762 static void RenderViewportsThumbnails()
15763 {
15764     ImGuiContext& g = *GImGui;
15765     ImGuiWindow* window = g.CurrentWindow;
15766 
15767     // 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.
15768     float SCALE = 1.0f / 8.0f;
15769     ImRect bb_full;
15770     //for (int n = 0; n < g.PlatformIO.Monitors.Size; n++)
15771     //    bb_full.Add(GetPlatformMonitorMainRect(g.PlatformIO.Monitors[n]));
15772     for (int n = 0; n < g.Viewports.Size; n++)
15773         bb_full.Add(g.Viewports[n]->GetMainRect());
15774     ImVec2 p = window->DC.CursorPos;
15775     ImVec2 off = p - bb_full.Min * SCALE;
15776     //for (int n = 0; n < g.PlatformIO.Monitors.Size; n++)
15777     //    window->DrawList->AddRect(off + g.PlatformIO.Monitors[n].MainPos * SCALE, off + (g.PlatformIO.Monitors[n].MainPos + g.PlatformIO.Monitors[n].MainSize) * SCALE, ImGui::GetColorU32(ImGuiCol_Border));
15778     for (int n = 0; n < g.Viewports.Size; n++)
15779     {
15780         ImGuiViewportP* viewport = g.Viewports[n];
15781         ImRect viewport_draw_bb(off + (viewport->Pos) * SCALE, off + (viewport->Pos + viewport->Size) * SCALE);
15782         RenderViewportThumbnail(window->DrawList, viewport, viewport_draw_bb);
15783     }
15784     ImGui::Dummy(bb_full.GetSize() * SCALE);
15785 }
15786 
15787 // Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds.
MetricsHelpMarker(const char * desc)15788 static void MetricsHelpMarker(const char* desc)
15789 {
15790     ImGui::TextDisabled("(?)");
15791     if (ImGui::IsItemHovered())
15792     {
15793         ImGui::BeginTooltip();
15794         ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
15795         ImGui::TextUnformatted(desc);
15796         ImGui::PopTextWrapPos();
15797         ImGui::EndTooltip();
15798     }
15799 }
15800 
ShowMetricsWindow(bool * p_open)15801 void ImGui::ShowMetricsWindow(bool* p_open)
15802 {
15803     if (!Begin("Dear ImGui Metrics/Debugger", p_open))
15804     {
15805         End();
15806         return;
15807     }
15808 
15809     ImGuiContext& g = *GImGui;
15810     ImGuiIO& io = g.IO;
15811     ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
15812 
15813     // Basic info
15814     Text("Dear ImGui %s", GetVersion());
15815     Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
15816     Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
15817     Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
15818     Text("%d active allocations", io.MetricsActiveAllocations);
15819     //SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; }
15820 
15821     Separator();
15822 
15823     // Debugging enums
15824     enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentIdeal, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type
15825     const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentIdeal", "ContentRegionRect" };
15826     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
15827     const char* trt_rects_names[TRT_Count] = { "OuterRect", "InnerRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsWorkRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentFrozen", "ColumnsContentUnfrozen" };
15828     if (cfg->ShowWindowsRectsType < 0)
15829         cfg->ShowWindowsRectsType = WRT_WorkRect;
15830     if (cfg->ShowTablesRectsType < 0)
15831         cfg->ShowTablesRectsType = TRT_WorkRect;
15832 
15833     struct Funcs
15834     {
15835         static ImRect GetTableRect(ImGuiTable* table, int rect_type, int n)
15836         {
15837             if (rect_type == TRT_OuterRect)                     { return table->OuterRect; }
15838             else if (rect_type == TRT_InnerRect)                { return table->InnerRect; }
15839             else if (rect_type == TRT_WorkRect)                 { return table->WorkRect; }
15840             else if (rect_type == TRT_HostClipRect)             { return table->HostClipRect; }
15841             else if (rect_type == TRT_InnerClipRect)            { return table->InnerClipRect; }
15842             else if (rect_type == TRT_BackgroundClipRect)       { return table->BgClipRect; }
15843             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); }
15844             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); }
15845             else if (rect_type == TRT_ColumnsClipRect)          { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; }
15846             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
15847             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); }
15848             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); }
15849             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); }
15850             IM_ASSERT(0);
15851             return ImRect();
15852         }
15853 
15854         static ImRect GetWindowRect(ImGuiWindow* window, int rect_type)
15855         {
15856             if (rect_type == WRT_OuterRect)                 { return window->Rect(); }
15857             else if (rect_type == WRT_OuterRectClipped)     { return window->OuterRectClipped; }
15858             else if (rect_type == WRT_InnerRect)            { return window->InnerRect; }
15859             else if (rect_type == WRT_InnerClipRect)        { return window->InnerClipRect; }
15860             else if (rect_type == WRT_WorkRect)             { return window->WorkRect; }
15861             else if (rect_type == WRT_Content)       { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
15862             else if (rect_type == WRT_ContentIdeal)         { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSizeIdeal); }
15863             else if (rect_type == WRT_ContentRegionRect)    { return window->ContentRegionRect; }
15864             IM_ASSERT(0);
15865             return ImRect();
15866         }
15867     };
15868 
15869     // Tools
15870     if (TreeNode("Tools"))
15871     {
15872         // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
15873         if (Button("Item Picker.."))
15874             DebugStartItemPicker();
15875         SameLine();
15876         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.");
15877 
15878         Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder);
15879         Checkbox("Show windows rectangles", &cfg->ShowWindowsRects);
15880         SameLine();
15881         SetNextItemWidth(GetFontSize() * 12);
15882         cfg->ShowWindowsRects |= Combo("##show_windows_rect_type", &cfg->ShowWindowsRectsType, wrt_rects_names, WRT_Count, WRT_Count);
15883         if (cfg->ShowWindowsRects && g.NavWindow != NULL)
15884         {
15885             BulletText("'%s':", g.NavWindow->Name);
15886             Indent();
15887             for (int rect_n = 0; rect_n < WRT_Count; rect_n++)
15888             {
15889                 ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n);
15890                 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]);
15891             }
15892             Unindent();
15893         }
15894         Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh);
15895         Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes);
15896 
15897         Checkbox("Show tables rectangles", &cfg->ShowTablesRects);
15898         SameLine();
15899         SetNextItemWidth(GetFontSize() * 12);
15900         cfg->ShowTablesRects |= Combo("##show_table_rects_type", &cfg->ShowTablesRectsType, trt_rects_names, TRT_Count, TRT_Count);
15901         if (cfg->ShowTablesRects && g.NavWindow != NULL)
15902         {
15903             for (int table_n = 0; table_n < g.Tables.GetSize(); table_n++)
15904             {
15905                 ImGuiTable* table = g.Tables.GetByIndex(table_n);
15906                 if (table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow))
15907                     continue;
15908 
15909                 BulletText("Table 0x%08X (%d columns, in '%s')", table->ID, table->ColumnsCount, table->OuterWindow->Name);
15910                 if (IsItemHovered())
15911                     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);
15912                 Indent();
15913                 char buf[128];
15914                 for (int rect_n = 0; rect_n < TRT_Count; rect_n++)
15915                 {
15916                     if (rect_n >= TRT_ColumnsRect)
15917                     {
15918                         if (rect_n != TRT_ColumnsRect && rect_n != TRT_ColumnsClipRect)
15919                             continue;
15920                         for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
15921                         {
15922                             ImRect r = Funcs::GetTableRect(table, rect_n, column_n);
15923                             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]);
15924                             Selectable(buf);
15925                             if (IsItemHovered())
15926                                 GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, ~0, 2.0f);
15927                         }
15928                     }
15929                     else
15930                     {
15931                         ImRect r = Funcs::GetTableRect(table, rect_n, -1);
15932                         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]);
15933                         Selectable(buf);
15934                         if (IsItemHovered())
15935                             GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, ~0, 2.0f);
15936                     }
15937                 }
15938                 Unindent();
15939             }
15940         }
15941 
15942         TreePop();
15943     }
15944 
15945     // Contents
15946     DebugNodeWindowsList(&g.Windows, "Windows");
15947     //DebugNodeWindowsList(&g.WindowsFocusOrder, "WindowsFocusOrder");
15948     if (TreeNode("Viewports", "Viewports (%d)", g.Viewports.Size))
15949     {
15950         Indent(GetTreeNodeToLabelSpacing());
15951         RenderViewportsThumbnails();
15952         Unindent(GetTreeNodeToLabelSpacing());
15953         bool open = TreeNode("Monitors", "Monitors (%d)", g.PlatformIO.Monitors.Size);
15954         ImGui::SameLine();
15955         MetricsHelpMarker("Dear ImGui uses monitor data:\n- to query DPI settings on a per monitor basis\n- to position popup/tooltips so they don't straddle monitors.");
15956         if (open)
15957         {
15958             for (int i = 0; i < g.PlatformIO.Monitors.Size; i++)
15959             {
15960                 const ImGuiPlatformMonitor& mon = g.PlatformIO.Monitors[i];
15961                 BulletText("Monitor #%d: DPI %.0f%%\n MainMin (%.0f,%.0f), MainMax (%.0f,%.0f), MainSize (%.0f,%.0f)\n WorkMin (%.0f,%.0f), WorkMax (%.0f,%.0f), WorkSize (%.0f,%.0f)",
15962                     i, mon.DpiScale * 100.0f,
15963                     mon.MainPos.x, mon.MainPos.y, mon.MainPos.x + mon.MainSize.x, mon.MainPos.y + mon.MainSize.y, mon.MainSize.x, mon.MainSize.y,
15964                     mon.WorkPos.x, mon.WorkPos.y, mon.WorkPos.x + mon.WorkSize.x, mon.WorkPos.y + mon.WorkSize.y, mon.WorkSize.x, mon.WorkSize.y);
15965             }
15966             TreePop();
15967         }
15968         for (int i = 0; i < g.Viewports.Size; i++)
15969             DebugNodeViewport(g.Viewports[i]);
15970         TreePop();
15971     }
15972 
15973     // Details for Popups
15974     if (TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
15975     {
15976         for (int i = 0; i < g.OpenPopupStack.Size; i++)
15977         {
15978             ImGuiWindow* window = g.OpenPopupStack[i].Window;
15979             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" : "");
15980         }
15981         TreePop();
15982     }
15983 
15984     // Details for TabBars
15985     if (TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetSize()))
15986     {
15987         for (int n = 0; n < g.TabBars.GetSize(); n++)
15988             DebugNodeTabBar(g.TabBars.GetByIndex(n), "TabBar");
15989         TreePop();
15990     }
15991 
15992     // Details for Tables
15993 #ifdef IMGUI_HAS_TABLE
15994     if (TreeNode("Tables", "Tables (%d)", g.Tables.GetSize()))
15995     {
15996         for (int n = 0; n < g.Tables.GetSize(); n++)
15997             DebugNodeTable(g.Tables.GetByIndex(n));
15998         TreePop();
15999     }
16000 #endif // #ifdef IMGUI_HAS_TABLE
16001 
16002     // Details for Docking
16003 #ifdef IMGUI_HAS_DOCK
16004     if (TreeNode("Docking"))
16005     {
16006         static bool root_nodes_only = true;
16007         ImGuiDockContext* dc = &g.DockContext;
16008         Checkbox("List root nodes", &root_nodes_only);
16009         Checkbox("Ctrl shows window dock info", &cfg->ShowDockingNodes);
16010         if (SmallButton("Clear nodes")) { DockContextClearNodes(&g, 0, true); }
16011         SameLine();
16012         if (SmallButton("Rebuild all")) { dc->WantFullRebuild = true; }
16013         for (int n = 0; n < dc->Nodes.Data.Size; n++)
16014             if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
16015                 if (!root_nodes_only || node->IsRootNode())
16016                     DebugNodeDockNode(node, "Node");
16017         TreePop();
16018     }
16019 #endif // #ifdef IMGUI_HAS_DOCK
16020 
16021     // Settings
16022     if (TreeNode("Settings"))
16023     {
16024         if (SmallButton("Clear"))
16025             ClearIniSettings();
16026         SameLine();
16027         if (SmallButton("Save to memory"))
16028             SaveIniSettingsToMemory();
16029         SameLine();
16030         if (SmallButton("Save to disk"))
16031             SaveIniSettingsToDisk(g.IO.IniFilename);
16032         SameLine();
16033         if (g.IO.IniFilename)
16034             Text("\"%s\"", g.IO.IniFilename);
16035         else
16036             TextUnformatted("<NULL>");
16037         Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer);
16038         if (TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size))
16039         {
16040             for (int n = 0; n < g.SettingsHandlers.Size; n++)
16041                 BulletText("%s", g.SettingsHandlers[n].TypeName);
16042             TreePop();
16043         }
16044         if (TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size()))
16045         {
16046             for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
16047                 DebugNodeWindowSettings(settings);
16048             TreePop();
16049         }
16050 
16051 #ifdef IMGUI_HAS_TABLE
16052         if (TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size()))
16053         {
16054             for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))
16055                 DebugNodeTableSettings(settings);
16056             TreePop();
16057         }
16058 #endif // #ifdef IMGUI_HAS_TABLE
16059 
16060 #ifdef IMGUI_HAS_DOCK
16061         if (ImGui::TreeNode("SettingsDocking", "Settings packed data: Docking"))
16062         {
16063             ImGuiDockContext* dc = &g.DockContext;
16064             ImGui::Text("In SettingsWindows:");
16065             for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
16066                 if (settings->DockId != 0)
16067                     ImGui::BulletText("Window '%s' -> DockId %08X", settings->GetName(), settings->DockId);
16068             ImGui::Text("In SettingsNodes:");
16069             for (int n = 0; n < dc->NodesSettings.Size; n++)
16070             {
16071                 ImGuiDockNodeSettings* settings = &dc->NodesSettings[n];
16072                 const char* selected_tab_name = NULL;
16073                 if (settings->SelectedWindowId)
16074                 {
16075                     if (ImGuiWindow* window = FindWindowByID(settings->SelectedWindowId))
16076                         selected_tab_name = window->Name;
16077                     else if (ImGuiWindowSettings* window_settings = FindWindowSettings(settings->SelectedWindowId))
16078                         selected_tab_name = window_settings->GetName();
16079                 }
16080                 ImGui::BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings->ID, settings->ParentNodeId, settings->SelectedWindowId, selected_tab_name ? selected_tab_name : settings->SelectedWindowId ? "N/A" : "");
16081             }
16082             ImGui::TreePop();
16083         }
16084 #endif // #ifdef IMGUI_HAS_DOCK
16085 
16086         if (TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size()))
16087         {
16088             InputTextMultiline("##Ini", (char*)(void*)g.SettingsIniData.c_str(), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, GetTextLineHeight() * 20), ImGuiInputTextFlags_ReadOnly);
16089             TreePop();
16090         }
16091         TreePop();
16092     }
16093 
16094     // Misc Details
16095     if (TreeNode("Internal state"))
16096     {
16097         const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
16098 
16099         Text("WINDOWING");
16100         Indent();
16101         Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
16102         Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
16103         Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL");
16104         Text("HoveredDockNode: 0x%08X", g.HoveredDockNode ? g.HoveredDockNode->ID : 0);
16105         Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
16106         Text("MouseViewport: 0x%08X (UserHovered 0x%08X, LastHovered 0x%08X)", g.MouseViewport->ID, g.IO.MouseHoveredViewport, g.MouseLastHoveredViewport ? g.MouseLastHoveredViewport->ID : 0);
16107         Unindent();
16108 
16109         Text("ITEMS");
16110         Indent();
16111         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]);
16112         Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
16113         Text("HoveredId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not
16114         Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
16115         Unindent();
16116 
16117         Text("NAV,FOCUS");
16118         Indent();
16119         Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
16120         Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
16121         Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
16122         Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
16123         Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
16124         Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
16125         Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId);
16126         Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
16127         Unindent();
16128 
16129         TreePop();
16130     }
16131 
16132     // Overlay: Display windows Rectangles and Begin Order
16133     if (cfg->ShowWindowsRects || cfg->ShowWindowsBeginOrder)
16134     {
16135         for (int n = 0; n < g.Windows.Size; n++)
16136         {
16137             ImGuiWindow* window = g.Windows[n];
16138             if (!window->WasActive)
16139                 continue;
16140             ImDrawList* draw_list = GetForegroundDrawList(window);
16141             if (cfg->ShowWindowsRects)
16142             {
16143                 ImRect r = Funcs::GetWindowRect(window, cfg->ShowWindowsRectsType);
16144                 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
16145             }
16146             if (cfg->ShowWindowsBeginOrder && !(window->Flags & ImGuiWindowFlags_ChildWindow))
16147             {
16148                 char buf[32];
16149                 ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
16150                 float font_size = GetFontSize();
16151                 draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
16152                 draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf);
16153             }
16154         }
16155     }
16156 
16157 #ifdef IMGUI_HAS_TABLE
16158     // Overlay: Display Tables Rectangles
16159     if (cfg->ShowTablesRects)
16160     {
16161         for (int table_n = 0; table_n < g.Tables.GetSize(); table_n++)
16162         {
16163             ImGuiTable* table = g.Tables.GetByIndex(table_n);
16164             if (table->LastFrameActive < g.FrameCount - 1)
16165                 continue;
16166             ImDrawList* draw_list = GetForegroundDrawList(table->OuterWindow);
16167             if (cfg->ShowTablesRectsType >= TRT_ColumnsRect)
16168             {
16169                 for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
16170                 {
16171                     ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, column_n);
16172                     ImU32 col = (table->HoveredColumnBody == column_n) ? IM_COL32(255, 255, 128, 255) : IM_COL32(255, 0, 128, 255);
16173                     float thickness = (table->HoveredColumnBody == column_n) ? 3.0f : 1.0f;
16174                     draw_list->AddRect(r.Min, r.Max, col, 0.0f, ~0, thickness);
16175                 }
16176             }
16177             else
16178             {
16179                 ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, -1);
16180                 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
16181             }
16182         }
16183     }
16184 #endif // #ifdef IMGUI_HAS_TABLE
16185 
16186 #ifdef IMGUI_HAS_DOCK
16187     // Overlay: Display Docking info
16188     if (cfg->ShowDockingNodes && g.IO.KeyCtrl && g.HoveredDockNode)
16189     {
16190         char buf[64] = "";
16191         char* p = buf;
16192         ImGuiDockNode* node = g.HoveredDockNode;
16193         ImDrawList* overlay_draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList((ImGuiViewportP*)GetMainViewport());
16194         p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "DockId: %X%s\n", node->ID, node->IsCentralNode() ? " *CentralNode*" : "");
16195         p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "WindowClass: %08X\n", node->WindowClass.ClassId);
16196         p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "Size: (%.0f, %.0f)\n", node->Size.x, node->Size.y);
16197         p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "SizeRef: (%.0f, %.0f)\n", node->SizeRef.x, node->SizeRef.y);
16198         int depth = DockNodeGetDepth(node);
16199         overlay_draw_list->AddRect(node->Pos + ImVec2(3, 3) * (float)depth, node->Pos + node->Size - ImVec2(3, 3) * (float)depth, IM_COL32(200, 100, 100, 255));
16200         ImVec2 pos = node->Pos + ImVec2(3, 3) * (float)depth;
16201         overlay_draw_list->AddRectFilled(pos - ImVec2(1, 1), pos + CalcTextSize(buf) + ImVec2(1, 1), IM_COL32(200, 100, 100, 255));
16202         overlay_draw_list->AddText(NULL, 0.0f, pos, IM_COL32(255, 255, 255, 255), buf);
16203     }
16204 #endif // #ifdef IMGUI_HAS_DOCK
16205 
16206     End();
16207 }
16208 
16209 // [DEBUG] Display contents of Columns
DebugNodeColumns(ImGuiOldColumns * columns)16210 void ImGui::DebugNodeColumns(ImGuiOldColumns* columns)
16211 {
16212     if (!TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
16213         return;
16214     BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX);
16215     for (int column_n = 0; column_n < columns->Columns.Size; column_n++)
16216         BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, GetColumnOffsetFromNorm(columns, columns->Columns[column_n].OffsetNorm));
16217     TreePop();
16218 }
16219 
16220 // [DEBUG] Display contents of ImDockNode
DebugNodeDockNode(ImGuiDockNode * node,const char * label)16221 void ImGui::DebugNodeDockNode(ImGuiDockNode* node, const char* label)
16222 {
16223     ImGuiContext& g = *GImGui;
16224     const bool is_alive = (g.FrameCount - node->LastFrameAlive < 2);    // Submitted with ImGuiDockNodeFlags_KeepAliveOnly
16225     const bool is_active = (g.FrameCount - node->LastFrameActive < 2);  // Submitted
16226     if (!is_alive) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
16227     bool open;
16228     if (node->Windows.Size > 0)
16229         open = TreeNode((void*)(intptr_t)node->ID, "%s 0x%04X%s: %d windows (vis: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", node->Windows.Size, node->VisibleWindow ? node->VisibleWindow->Name : "NULL");
16230     else
16231         open = TreeNode((void*)(intptr_t)node->ID, "%s 0x%04X%s: %s split (vis: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", (node->SplitAxis == ImGuiAxis_X) ? "horizontal" : (node->SplitAxis == ImGuiAxis_Y) ? "vertical" : "n/a", node->VisibleWindow ? node->VisibleWindow->Name : "NULL");
16232     if (!is_alive) { PopStyleColor(); }
16233     if (is_active && IsItemHovered())
16234         if (ImGuiWindow* window = node->HostWindow ? node->HostWindow : node->VisibleWindow)
16235             GetForegroundDrawList(window)->AddRect(node->Pos, node->Pos + node->Size, IM_COL32(255, 255, 0, 255));
16236     if (open)
16237     {
16238         IM_ASSERT(node->ChildNodes[0] == NULL || node->ChildNodes[0]->ParentNode == node);
16239         IM_ASSERT(node->ChildNodes[1] == NULL || node->ChildNodes[1]->ParentNode == node);
16240         BulletText("Pos (%.0f,%.0f), Size (%.0f, %.0f) Ref (%.0f, %.0f)",
16241             node->Pos.x, node->Pos.y, node->Size.x, node->Size.y, node->SizeRef.x, node->SizeRef.y);
16242         DebugNodeWindow(node->HostWindow, "HostWindow");
16243         DebugNodeWindow(node->VisibleWindow, "VisibleWindow");
16244         BulletText("SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X", node->SelectedTabId, node->LastFocusedNodeId);
16245         BulletText("Misc:%s%s%s%s%s",
16246             node->IsDockSpace() ? " IsDockSpace" : "",
16247             node->IsCentralNode() ? " IsCentralNode" : "",
16248             is_alive ? " IsAlive" : "", is_active ? " IsActive" : "",
16249             node->WantLockSizeOnce ? " WantLockSizeOnce" : "");
16250         if (TreeNode("flags", "LocalFlags: 0x%04X SharedFlags: 0x%04X", node->LocalFlags, node->SharedFlags))
16251         {
16252             CheckboxFlags("LocalFlags: NoDocking", &node->LocalFlags, ImGuiDockNodeFlags_NoDocking);
16253             CheckboxFlags("LocalFlags: NoSplit", &node->LocalFlags, ImGuiDockNodeFlags_NoSplit);
16254             CheckboxFlags("LocalFlags: NoResize", &node->LocalFlags, ImGuiDockNodeFlags_NoResize);
16255             CheckboxFlags("LocalFlags: NoResizeX", &node->LocalFlags, ImGuiDockNodeFlags_NoResizeX);
16256             CheckboxFlags("LocalFlags: NoResizeY", &node->LocalFlags, ImGuiDockNodeFlags_NoResizeY);
16257             CheckboxFlags("LocalFlags: NoTabBar", &node->LocalFlags, ImGuiDockNodeFlags_NoTabBar);
16258             CheckboxFlags("LocalFlags: HiddenTabBar", &node->LocalFlags, ImGuiDockNodeFlags_HiddenTabBar);
16259             CheckboxFlags("LocalFlags: NoWindowMenuButton", &node->LocalFlags, ImGuiDockNodeFlags_NoWindowMenuButton);
16260             CheckboxFlags("LocalFlags: NoCloseButton", &node->LocalFlags, ImGuiDockNodeFlags_NoCloseButton);
16261             TreePop();
16262         }
16263         if (node->ParentNode)
16264             DebugNodeDockNode(node->ParentNode, "ParentNode");
16265         if (node->ChildNodes[0])
16266             DebugNodeDockNode(node->ChildNodes[0], "Child[0]");
16267         if (node->ChildNodes[1])
16268             DebugNodeDockNode(node->ChildNodes[1], "Child[1]");
16269         if (node->TabBar)
16270             DebugNodeTabBar(node->TabBar, "TabBar");
16271         TreePop();
16272     }
16273 }
16274 
16275 // [DEBUG] Display contents of ImDrawList
16276 // Note that both 'window' and 'viewport' may be NULL here. Viewport is generally null of destroyed popups which previously owned a viewport.
DebugNodeDrawList(ImGuiWindow * window,ImGuiViewportP * viewport,const ImDrawList * draw_list,const char * label)16277 void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label)
16278 {
16279     ImGuiContext& g = *GImGui;
16280     ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
16281     int cmd_count = draw_list->CmdBuffer.Size;
16282     if (cmd_count > 0 && draw_list->CmdBuffer.back().ElemCount == 0 && draw_list->CmdBuffer.back().UserCallback == NULL)
16283         cmd_count--;
16284     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);
16285     if (draw_list == GetWindowDrawList())
16286     {
16287         SameLine();
16288         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)
16289         if (node_open)
16290             TreePop();
16291         return;
16292     }
16293 
16294     ImDrawList* fg_draw_list = viewport ? GetForegroundDrawList(viewport) : NULL; // Render additional visuals into the top-most draw list
16295     if (window && fg_draw_list && IsItemHovered())
16296         fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
16297     if (!node_open)
16298         return;
16299 
16300     if (window && !window->WasActive)
16301         TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!");
16302 
16303     for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.Data; pcmd < draw_list->CmdBuffer.Data + cmd_count; pcmd++)
16304     {
16305         if (pcmd->UserCallback)
16306         {
16307             BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
16308             continue;
16309         }
16310 
16311         char buf[300];
16312         ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d tris, Tex 0x%p, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
16313             pcmd->ElemCount / 3, (void*)(intptr_t)pcmd->TextureId,
16314             pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
16315         bool pcmd_node_open = TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf);
16316         if (IsItemHovered() && (cfg->ShowDrawCmdMesh || cfg->ShowDrawCmdBoundingBoxes) && fg_draw_list)
16317             DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, cfg->ShowDrawCmdMesh, cfg->ShowDrawCmdBoundingBoxes);
16318         if (!pcmd_node_open)
16319             continue;
16320 
16321         // Calculate approximate coverage area (touched pixel count)
16322         // This will be in pixels squared as long there's no post-scaling happening to the renderer output.
16323         const ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
16324         const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + pcmd->VtxOffset;
16325         float total_area = 0.0f;
16326         for (unsigned int idx_n = pcmd->IdxOffset; idx_n < pcmd->IdxOffset + pcmd->ElemCount; )
16327         {
16328             ImVec2 triangle[3];
16329             for (int n = 0; n < 3; n++, idx_n++)
16330                 triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos;
16331             total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]);
16332         }
16333 
16334         // Display vertex information summary. Hover to get all triangles drawn in wire-frame
16335         ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area);
16336         Selectable(buf);
16337         if (IsItemHovered() && fg_draw_list)
16338             DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, true, false);
16339 
16340         // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
16341         ImGuiListClipper clipper;
16342         clipper.Begin(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
16343         while (clipper.Step())
16344             for (int prim = clipper.DisplayStart, idx_i = pcmd->IdxOffset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++)
16345             {
16346                 char* buf_p = buf, * buf_end = buf + IM_ARRAYSIZE(buf);
16347                 ImVec2 triangle[3];
16348                 for (int n = 0; n < 3; n++, idx_i++)
16349                 {
16350                     const ImDrawVert& v = vtx_buffer[idx_buffer ? idx_buffer[idx_i] : idx_i];
16351                     triangle[n] = v.pos;
16352                     buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n",
16353                         (n == 0) ? "Vert:" : "     ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
16354                 }
16355 
16356                 Selectable(buf, false);
16357                 if (fg_draw_list && IsItemHovered())
16358                 {
16359                     ImDrawListFlags backup_flags = fg_draw_list->Flags;
16360                     fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
16361                     fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), true, 1.0f);
16362                     fg_draw_list->Flags = backup_flags;
16363                 }
16364             }
16365         TreePop();
16366     }
16367     TreePop();
16368 }
16369 
16370 // [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)16371 void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb)
16372 {
16373     IM_ASSERT(show_mesh || show_aabb);
16374     ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
16375     ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset;
16376 
16377     // Draw wire-frame version of all triangles
16378     ImRect clip_rect = draw_cmd->ClipRect;
16379     ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
16380     ImDrawListFlags backup_flags = out_draw_list->Flags;
16381     out_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
16382     for (unsigned int idx_n = draw_cmd->IdxOffset; idx_n < draw_cmd->IdxOffset + draw_cmd->ElemCount; )
16383     {
16384         ImVec2 triangle[3];
16385         for (int n = 0; n < 3; n++, idx_n++)
16386             vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos));
16387         if (show_mesh)
16388             out_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), true, 1.0f); // In yellow: mesh triangles
16389     }
16390     // Draw bounding boxes
16391     if (show_aabb)
16392     {
16393         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
16394         out_draw_list->AddRect(ImFloor(vtxs_rect.Min), ImFloor(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles
16395     }
16396     out_draw_list->Flags = backup_flags;
16397 }
16398 
16399 // [DEBUG] Display contents of ImGuiStorage
DebugNodeStorage(ImGuiStorage * storage,const char * label)16400 void ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label)
16401 {
16402     if (!TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes()))
16403         return;
16404     for (int n = 0; n < storage->Data.Size; n++)
16405     {
16406         const ImGuiStorage::ImGuiStoragePair& p = storage->Data[n];
16407         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.
16408     }
16409     TreePop();
16410 }
16411 
16412 // [DEBUG] Display contents of ImGuiTabBar
DebugNodeTabBar(ImGuiTabBar * tab_bar,const char * label)16413 void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label)
16414 {
16415     // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
16416     char buf[256];
16417     char* p = buf;
16418     const char* buf_end = buf + IM_ARRAYSIZE(buf);
16419     const bool is_active = (tab_bar->PrevFrameVisible >= GetFrameCount() - 2);
16420     p += ImFormatString(p, buf_end - p, "%s 0x%08X (%d tabs)%s", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*");
16421     p += ImFormatString(p, buf_end - p, "  { ");
16422     for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++)
16423     {
16424         ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
16425         p += ImFormatString(p, buf_end - p, "%s'%s'",
16426             tab_n > 0 ? ", " : "", (tab->Window || tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???");
16427     }
16428     p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } ");
16429     if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
16430     bool open = TreeNode(tab_bar, "%s", buf);
16431     if (!is_active) { PopStyleColor(); }
16432     if (is_active && IsItemHovered())
16433     {
16434         ImDrawList* draw_list = GetForegroundDrawList();
16435         draw_list->AddRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, IM_COL32(255, 255, 0, 255));
16436         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));
16437         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));
16438     }
16439     if (open)
16440     {
16441         for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
16442         {
16443             const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
16444             PushID(tab);
16445             if (SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } SameLine(0, 2);
16446             if (SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } SameLine();
16447             Text("%02d%c Tab 0x%08X '%s' Offset: %.1f, Width: %.1f/%.1f",
16448                 tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->Window || tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???", tab->Offset, tab->Width, tab->ContentWidth);
16449             PopID();
16450         }
16451         TreePop();
16452     }
16453 }
16454 
DebugNodeViewport(ImGuiViewportP * viewport)16455 void ImGui::DebugNodeViewport(ImGuiViewportP* viewport)
16456 {
16457     SetNextItemOpen(true, ImGuiCond_Once);
16458     if (TreeNode((void*)(intptr_t)viewport->ID, "Viewport #%d, ID: 0x%08X, Parent: 0x%08X, Window: \"%s\"", viewport->Idx, viewport->ID, viewport->ParentViewportId, viewport->Window ? viewport->Window->Name : "N/A"))
16459     {
16460         ImGuiWindowFlags flags = viewport->Flags;
16461         BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Offset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f\nMonitor: %d, DpiScale: %.0f%%",
16462             viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y,
16463             viewport->WorkOffsetMin.x, viewport->WorkOffsetMin.y, viewport->WorkOffsetMax.x, viewport->WorkOffsetMax.y,
16464             viewport->PlatformMonitor, viewport->DpiScale * 100.0f);
16465         if (viewport->Idx > 0) { SameLine(); if (SmallButton("Reset Pos")) { viewport->Pos = ImVec2(200,200); if (viewport->Window) viewport->Window->Pos = ImVec2(200,200); } }
16466         BulletText("Flags: 0x%04X =%s%s%s%s%s%s%s", viewport->Flags,
16467             (flags & ImGuiViewportFlags_CanHostOtherWindows) ? " CanHostOtherWindows" : "", (flags & ImGuiViewportFlags_NoDecoration) ? " NoDecoration" : "",
16468             (flags & ImGuiViewportFlags_NoFocusOnAppearing)  ? " NoFocusOnAppearing"  : "", (flags & ImGuiViewportFlags_NoInputs)     ? " NoInputs"     : "",
16469             (flags & ImGuiViewportFlags_NoRendererClear)     ? " NoRendererClear"     : "", (flags & ImGuiViewportFlags_Minimized)    ? " Minimized"    : "",
16470             (flags & ImGuiViewportFlags_NoAutoMerge)         ? " NoAutoMerge"         : "");
16471         for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++)
16472             for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++)
16473                 DebugNodeDrawList(NULL, viewport, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList");
16474         TreePop();
16475     }
16476 }
16477 
DebugNodeWindow(ImGuiWindow * window,const char * label)16478 void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)
16479 {
16480     if (window == NULL)
16481     {
16482         BulletText("%s: NULL", label);
16483         return;
16484     }
16485 
16486     ImGuiContext& g = *GImGui;
16487     const bool is_active = window->WasActive;
16488     ImGuiTreeNodeFlags tree_node_flags = (window == g.NavWindow) ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None;
16489     if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
16490     const bool open = TreeNodeEx(label, tree_node_flags, "%s '%s'%s", label, window->Name, is_active ? "" : " *Inactive*");
16491     if (!is_active) { PopStyleColor(); }
16492     if (IsItemHovered() && is_active)
16493         GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
16494     if (!open)
16495         return;
16496 
16497     if (window->MemoryCompacted)
16498         TextDisabled("Note: some memory buffers have been compacted/freed.");
16499 
16500     ImGuiWindowFlags flags = window->Flags;
16501     DebugNodeDrawList(window, window->Viewport, window->DrawList, "DrawList");
16502     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);
16503     BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
16504         (flags & ImGuiWindowFlags_ChildWindow)  ? "Child " : "",      (flags & ImGuiWindowFlags_Tooltip)     ? "Tooltip "   : "",  (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
16505         (flags & ImGuiWindowFlags_Modal)        ? "Modal " : "",      (flags & ImGuiWindowFlags_ChildMenu)   ? "ChildMenu " : "",  (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
16506         (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
16507     BulletText("WindowClassId: 0x%08X", window->WindowClass.ClassId);
16508     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" : "");
16509     BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
16510     BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems);
16511     BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask);
16512     BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
16513     if (!window->NavRectRel[0].IsInverted())
16514         BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y);
16515     else
16516         BulletText("NavRectRel[0]: <None>");
16517     BulletText("Viewport: %d%s, ViewportId: 0x%08X, ViewportPos: (%.1f,%.1f)", window->Viewport ? window->Viewport->Idx : -1, window->ViewportOwned ? " (Owned)" : "", window->ViewportId, window->ViewportPos.x, window->ViewportPos.y);
16518     BulletText("ViewportMonitor: %d", window->Viewport ? window->Viewport->PlatformMonitor : -1);
16519     BulletText("DockId: 0x%04X, DockOrder: %d, Act: %d, Vis: %d", window->DockId, window->DockOrder, window->DockIsActive, window->DockTabIsVisible);
16520     if (window->DockNode || window->DockNodeAsHost)
16521         DebugNodeDockNode(window->DockNodeAsHost ? window->DockNodeAsHost : window->DockNode, window->DockNodeAsHost ? "DockNodeAsHost" : "DockNode");
16522     if (window->RootWindow != window)       { DebugNodeWindow(window->RootWindow, "RootWindow"); }
16523     if (window->RootWindowDockStop != window->RootWindow) { DebugNodeWindow(window->RootWindowDockStop, "RootWindowDockStop"); }
16524     if (window->ParentWindow != NULL)       { DebugNodeWindow(window->ParentWindow, "ParentWindow"); }
16525     if (window->DC.ChildWindows.Size > 0)   { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); }
16526     if (window->ColumnsStorage.Size > 0 && TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
16527     {
16528         for (int n = 0; n < window->ColumnsStorage.Size; n++)
16529             DebugNodeColumns(&window->ColumnsStorage[n]);
16530         TreePop();
16531     }
16532     DebugNodeStorage(&window->StateStorage, "Storage");
16533     TreePop();
16534 }
16535 
DebugNodeWindowSettings(ImGuiWindowSettings * settings)16536 void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings* settings)
16537 {
16538     Text("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d",
16539         settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed);
16540 }
16541 
DebugNodeWindowsList(ImVector<ImGuiWindow * > * windows,const char * label)16542 void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label)
16543 {
16544     if (!TreeNode(label, "%s (%d)", label, windows->Size))
16545         return;
16546     Text("(In front-to-back order:)");
16547     for (int i = windows->Size - 1; i >= 0; i--) // Iterate front to back
16548     {
16549         PushID((*windows)[i]);
16550         DebugNodeWindow((*windows)[i], "Window");
16551         PopID();
16552     }
16553     TreePop();
16554 }
16555 
16556 #else
16557 
ShowMetricsWindow(bool *)16558 void ImGui::ShowMetricsWindow(bool*) {}
DebugNodeColumns(ImGuiOldColumns *)16559 void ImGui::DebugNodeColumns(ImGuiOldColumns*) {}
DebugNodeDrawList(ImGuiWindow *,ImGuiViewportP *,const ImDrawList *,const char *)16560 void ImGui::DebugNodeDrawList(ImGuiWindow*, ImGuiViewportP*, const ImDrawList*, const char*) {}
DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList *,const ImDrawList *,const ImDrawCmd *,bool,bool)16561 void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList*, const ImDrawList*, const ImDrawCmd*, bool, bool) {}
DebugNodeStorage(ImGuiStorage *,const char *)16562 void ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {}
DebugNodeTabBar(ImGuiTabBar *,const char *)16563 void ImGui::DebugNodeTabBar(ImGuiTabBar*, const char*) {}
DebugNodeWindow(ImGuiWindow *,const char *)16564 void ImGui::DebugNodeWindow(ImGuiWindow*, const char*) {}
DebugNodeWindowSettings(ImGuiWindowSettings *)16565 void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {}
DebugNodeWindowsList(ImVector<ImGuiWindow * > *,const char *)16566 void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>*, const char*) {}
DebugNodeViewport(ImGuiViewportP *)16567 void ImGui::DebugNodeViewport(ImGuiViewportP*) {}
16568 
16569 #endif
16570 
16571 //-----------------------------------------------------------------------------
16572 
16573 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
16574 // 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.
16575 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
16576 #include "imgui_user.inl"
16577 #endif
16578 
16579 //-----------------------------------------------------------------------------
16580 
16581 #endif // #ifndef IMGUI_DISABLE
16582