1 // dear imgui, v1.84 WIP
2 // (main code and documentation)
3 
4 // Help:
5 // - Read FAQ at http://dearimgui.org/faq
6 // - Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase.
7 // - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
8 // Read imgui.cpp for details, links and comments.
9 
10 // Resources:
11 // - FAQ                   http://dearimgui.org/faq
12 // - Homepage & latest     https://github.com/ocornut/imgui
13 // - Releases & changelog  https://github.com/ocornut/imgui/releases
14 // - Gallery               https://github.com/ocornut/imgui/issues/3793 (please post your screenshots/video there!)
15 // - Wiki                  https://github.com/ocornut/imgui/wiki (lots of good stuff there)
16 // - Glossary              https://github.com/ocornut/imgui/wiki/Glossary
17 // - Issues & support      https://github.com/ocornut/imgui/issues
18 // - Discussions           https://github.com/ocornut/imgui/discussions
19 
20 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
21 // See LICENSE.txt for copyright and licensing details (standard MIT License).
22 // This library is free but needs your support to sustain development and maintenance.
23 // Businesses: you can support continued development via invoiced technical support, maintenance and sponsoring contracts. Please reach out to "contact AT dearimgui.com".
24 // Individuals: you can support continued development via donations. See docs/README or web page.
25 
26 // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
27 // Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
28 // modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
29 // come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
30 // to a better solution or official support for them.
31 
32 /*
33 
34 Index of this file:
35 
36 DOCUMENTATION
37 
38 - MISSION STATEMENT
39 - END-USER GUIDE
40 - PROGRAMMER GUIDE
41   - READ FIRST
42   - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
43   - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
44   - HOW A SIMPLE APPLICATION MAY LOOK LIKE
45   - HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
46   - USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
47 - API BREAKING CHANGES (read me when you update!)
48 - FREQUENTLY ASKED QUESTIONS (FAQ)
49   - Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer)
50 
51 CODE
52 (search for "[SECTION]" in the code to find them)
53 
54 // [SECTION] INCLUDES
55 // [SECTION] FORWARD DECLARATIONS
56 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
57 // [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
58 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
59 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
60 // [SECTION] MISC HELPERS/UTILITIES (File functions)
61 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
62 // [SECTION] MISC HELPERS/UTILITIES (Color functions)
63 // [SECTION] ImGuiStorage
64 // [SECTION] ImGuiTextFilter
65 // [SECTION] ImGuiTextBuffer
66 // [SECTION] ImGuiListClipper
67 // [SECTION] STYLING
68 // [SECTION] RENDER HELPERS
69 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
70 // [SECTION] ERROR CHECKING
71 // [SECTION] LAYOUT
72 // [SECTION] SCROLLING
73 // [SECTION] TOOLTIPS
74 // [SECTION] POPUPS
75 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
76 // [SECTION] DRAG AND DROP
77 // [SECTION] LOGGING/CAPTURING
78 // [SECTION] SETTINGS
79 // [SECTION] VIEWPORTS, PLATFORM WINDOWS
80 // [SECTION] DOCKING
81 // [SECTION] PLATFORM DEPENDENT HELPERS
82 // [SECTION] METRICS/DEBUGGER WINDOW
83 
84 */
85 
86 //-----------------------------------------------------------------------------
87 // DOCUMENTATION
88 //-----------------------------------------------------------------------------
89 
90 /*
91 
92  MISSION STATEMENT
93  =================
94 
95  - Easy to use to create code-driven and data-driven tools.
96  - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
97  - Easy to hack and improve.
98  - Minimize setup and maintenance.
99  - Minimize state storage on user side.
100  - Portable, minimize dependencies, run on target (consoles, phones, etc.).
101  - Efficient runtime and memory consumption.
102 
103  Designed for developers and content-creators, not the typical end-user! Some of the current weaknesses includes:
104 
105  - Doesn't look fancy, doesn't animate.
106  - Limited layout features, intricate layouts are typically crafted in code.
107 
108 
109  END-USER GUIDE
110  ==============
111 
112  - Double-click on title bar to collapse window.
113  - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin().
114  - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents).
115  - Click and drag on any empty space to move window.
116  - TAB/SHIFT+TAB to cycle through keyboard editable fields.
117  - CTRL+Click on a slider or drag box to input value as text.
118  - Use mouse wheel to scroll.
119  - Text editor:
120    - Hold SHIFT or use mouse to select text.
121    - CTRL+Left/Right to word jump.
122    - CTRL+Shift+Left/Right to select words.
123    - CTRL+A our Double-Click to select all.
124    - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
125    - CTRL+Z,CTRL+Y to undo/redo.
126    - ESCAPE to revert text to its original value.
127    - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
128    - Controls are automatically adjusted for OSX to match standard OSX text editing operations.
129  - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard.
130  - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://dearimgui.org/controls_sheets
131 
132 
133  PROGRAMMER GUIDE
134  ================
135 
136  READ FIRST
137  ----------
138  - Remember to check the wonderful Wiki (https://github.com/ocornut/imgui/wiki)
139  - Your code creates the UI, if your code doesn't run the UI is gone! The UI can be highly dynamic, there are no construction or
140    destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, fewer bugs.
141  - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
142  - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
143  - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
144    You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in Wiki.
145  - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
146    For every application frame, your UI code will be called only once. This is in contrast to e.g. Unity's implementation of an IMGUI,
147    where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
148  - Our origin is on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
149  - This codebase is also optimized to yield decent performances with typical "Debug" builds settings.
150  - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).
151    If you get an assert, read the messages and comments around the assert.
152  - 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.
153  - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
154    See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
155    However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase.
156  - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!).
157 
158 
159  HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
160  ----------------------------------------------
161  - Overwrite all the sources files except for imconfig.h (if you have modified your copy of imconfig.h)
162  - Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over "master".
163  - You can also use '#define IMGUI_USER_CONFIG "my_config_file.h" to redirect configuration to your own file.
164  - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
165    If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
166    from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
167    likely be a comment about it. Please report any issue to the GitHub page!
168  - To find out usage of old API, you can add '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in your configuration file.
169  - Try to keep your copy of Dear ImGui reasonably up to date.
170 
171 
172  GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
173  ---------------------------------------------------------------
174  - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
175  - In the majority of cases you should be able to use unmodified backends files available in the backends/ folder.
176  - Add the Dear ImGui source files + selected backend source files to your projects or using your preferred build system.
177    It is recommended you build and statically link the .cpp files as part of your project and NOT as a shared library (DLL).
178  - 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.
179  - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
180  - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
181    Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
182    phases of your own application. All rendering information is stored into command-lists that you will retrieve after calling ImGui::Render().
183  - Refer to the backends and demo applications in the examples/ folder for instruction on how to setup your code.
184  - 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.
185 
186 
187  HOW A SIMPLE APPLICATION MAY LOOK LIKE
188  --------------------------------------
189  EXHIBIT 1: USING THE EXAMPLE BACKENDS (= imgui_impl_XXX.cpp files from the backends/ folder).
190  The sub-folders in examples/ contain examples applications following this structure.
191 
192      // Application init: create a dear imgui context, setup some options, load fonts
193      ImGui::CreateContext();
194      ImGuiIO& io = ImGui::GetIO();
195      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
196      // TODO: Fill optional fields of the io structure later.
197      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
198 
199      // Initialize helper Platform and Renderer backends (here we are using imgui_impl_win32.cpp and imgui_impl_dx11.cpp)
200      ImGui_ImplWin32_Init(hwnd);
201      ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
202 
203      // Application main loop
204      while (true)
205      {
206          // Feed inputs to dear imgui, start new frame
207          ImGui_ImplDX11_NewFrame();
208          ImGui_ImplWin32_NewFrame();
209          ImGui::NewFrame();
210 
211          // Any application code here
212          ImGui::Text("Hello, world!");
213 
214          // Render dear imgui into screen
215          ImGui::Render();
216          ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
217          g_pSwapChain->Present(1, 0);
218      }
219 
220      // Shutdown
221      ImGui_ImplDX11_Shutdown();
222      ImGui_ImplWin32_Shutdown();
223      ImGui::DestroyContext();
224 
225  EXHIBIT 2: IMPLEMENTING CUSTOM BACKEND / CUSTOM ENGINE
226 
227      // Application init: create a dear imgui context, setup some options, load fonts
228      ImGui::CreateContext();
229      ImGuiIO& io = ImGui::GetIO();
230      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
231      // TODO: Fill optional fields of the io structure later.
232      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
233 
234      // Build and load the texture atlas into a texture
235      // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
236      int width, height;
237      unsigned char* pixels = NULL;
238      io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
239 
240      // At this point you've got the texture data and you need to upload that to your graphic system:
241      // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
242      // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID.
243      MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
244      io.Fonts->SetTexID((void*)texture);
245 
246      // Application main loop
247      while (true)
248      {
249         // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
250         // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform Backends)
251         io.DeltaTime = 1.0f/60.0f;              // set the time elapsed since the previous frame (in seconds)
252         io.DisplaySize.x = 1920.0f;             // set the current display width
253         io.DisplaySize.y = 1280.0f;             // set the current display height here
254         io.MousePos = my_mouse_pos;             // set the mouse position
255         io.MouseDown[0] = my_mouse_buttons[0];  // set the mouse button states
256         io.MouseDown[1] = my_mouse_buttons[1];
257 
258         // Call NewFrame(), after this point you can use ImGui::* functions anytime
259         // (So you want to try calling NewFrame() as early as you can in your main loop to be able to use Dear ImGui everywhere)
260         ImGui::NewFrame();
261 
262         // Most of your application code here
263         ImGui::Text("Hello, world!");
264         MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
265         MyGameRender(); // may use any Dear ImGui functions as well!
266 
267         // Render dear imgui, swap buffers
268         // (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)
269         ImGui::EndFrame();
270         ImGui::Render();
271         ImDrawData* draw_data = ImGui::GetDrawData();
272         MyImGuiRenderFunction(draw_data);
273         SwapBuffers();
274      }
275 
276      // Shutdown
277      ImGui::DestroyContext();
278 
279  To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest of your application,
280  you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
281  Please read the FAQ and example applications for details about this!
282 
283 
284  HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
285  ---------------------------------------------
286  The backends in impl_impl_XXX.cpp files contain many working implementations of a rendering function.
287 
288     void void MyImGuiRenderFunction(ImDrawData* draw_data)
289     {
290        // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
291        // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
292        // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
293        // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
294        for (int n = 0; n < draw_data->CmdListsCount; n++)
295        {
296           const ImDrawList* cmd_list = draw_data->CmdLists[n];
297           const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;  // vertex buffer generated by Dear ImGui
298           const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;   // index buffer generated by Dear ImGui
299           for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
300           {
301              const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
302              if (pcmd->UserCallback)
303              {
304                  pcmd->UserCallback(cmd_list, pcmd);
305              }
306              else
307              {
308                  // The texture for the draw call is specified by pcmd->GetTexID().
309                  // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization.
310                  MyEngineBindTexture((MyTexture*)pcmd->GetTexID());
311 
312                  // We are using scissoring to clip some objects. All low-level graphics API should support it.
313                  // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
314                  //   (some elements visible outside their bounds) but you can fix that once everything else works!
315                  // - Clipping coordinates are provided in imgui coordinates space:
316                  //   - For a given viewport, draw_data->DisplayPos == viewport->Pos and draw_data->DisplaySize == viewport->Size
317                  //   - In a single viewport application, draw_data->DisplayPos == (0,0) and draw_data->DisplaySize == io.DisplaySize, but always use GetMainViewport()->Pos/Size instead of hardcoding those values.
318                  //   - In the interest of supporting multi-viewport applications (see 'docking' branch on github),
319                  //     always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
320                  // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
321                  ImVec2 pos = draw_data->DisplayPos;
322                  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));
323 
324                  // Render 'pcmd->ElemCount/3' indexed triangles.
325                  // 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.
326                  MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
327              }
328              idx_buffer += pcmd->ElemCount;
329           }
330        }
331     }
332 
333 
334  USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
335  ------------------------------------------
336  - The gamepad/keyboard navigation is fairly functional and keeps being improved.
337  - Gamepad support is particularly useful to use Dear ImGui on a console system (e.g. PS4, Switch, XB1) without a mouse!
338  - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787
339  - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
340  - Keyboard:
341     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
342       NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
343     - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
344       will be set. For more advanced uses, you may want to read from:
345        - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
346        - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
347        - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions.
348       Please reach out if you think the game vs navigation input sharing could be improved.
349  - Gamepad:
350     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
351     - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
352       Note that io.NavInputs[] is cleared by EndFrame().
353     - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
354          0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
355     - We use a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
356       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.).
357     - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://dearimgui.org/controls_sheets
358     - 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
359       to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
360  - Mouse:
361     - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
362     - 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.
363     - 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.
364       Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
365       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.
366       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.
367       (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, imgui will misbehave as it will see your mouse moving back and forth!)
368       (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
369        to set a boolean to ignore your other external mouse positions until the external source is moved again.)
370 
371 
372  API BREAKING CHANGES
373  ====================
374 
375  Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
376  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.
377  When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
378  You can read releases logs https://github.com/ocornut/imgui/releases for more details.
379 
380 (Docking/Viewport Branch)
381  - 2021/XX/XX (1.XX) - when multi-viewports are enabled, all positions will be in your natural OS coordinates space. It means that:
382                         - reference to hard-coded positions such as in SetNextWindowPos(ImVec2(0,0)) are probably not what you want anymore.
383                           you may use GetMainViewport()->Pos to offset hard-coded positions, e.g. SetNextWindowPos(GetMainViewport()->Pos)
384                         - likewise io.MousePos and GetMousePos() will use OS coordinates.
385                           If you query mouse positions to interact with non-imgui coordinates you will need to offset them, e.g. subtract GetWindowViewport()->Pos.
386  - 2021/XX/XX (1.XX) - Moved IME support functions from io.ImeSetInputScreenPosFn, io.ImeWindowHandle to the PlatformIO api.
387 
388 
389  - 2021/05/19 (1.83) - backends: obsoleted direct access to ImDrawCmd::TextureId in favor of calling ImDrawCmd::GetTexID().
390                         - if you are using official backends from the source tree: you have nothing to do.
391                         - if you have copied old backend code or using your own: change access to draw_cmd->TextureId to draw_cmd->GetTexID().
392  - 2021/03/12 (1.82) - upgraded ImDrawList::AddRect(), AddRectFilled(), PathRect() to use ImDrawFlags instead of ImDrawCornersFlags.
393                         - ImDrawCornerFlags_TopLeft  -> use ImDrawFlags_RoundCornersTopLeft
394                         - ImDrawCornerFlags_BotRight -> use ImDrawFlags_RoundCornersBottomRight
395                         - ImDrawCornerFlags_None     -> use ImDrawFlags_RoundCornersNone etc.
396                        flags now sanely defaults to 0 instead of 0x0F, consistent with all other flags in the API.
397                        breaking: the default with rounding > 0.0f is now "round all corners" vs old implicit "round no corners":
398                         - rounding == 0.0f + flags == 0 --> meant no rounding  --> unchanged (common use)
399                         - rounding  > 0.0f + flags != 0 --> meant rounding     --> unchanged (common use)
400                         - rounding == 0.0f + flags != 0 --> meant no rounding  --> unchanged (unlikely use)
401                         - rounding  > 0.0f + flags == 0 --> meant no rounding  --> BREAKING (unlikely use): will now round all corners --> use ImDrawFlags_RoundCornersNone or rounding == 0.0f.
402                        this ONLY matters for hard coded use of 0 + rounding > 0.0f. Use of named ImDrawFlags_RoundCornersNone (new) or ImDrawCornerFlags_None (old) are ok.
403                        the old ImDrawCornersFlags used awkward default values of ~0 or 0xF (4 lower bits set) to signify "round all corners" and we sometimes encouraged using them as shortcuts.
404                        legacy path still support use of hard coded ~0 or any value from 0x1 or 0xF. They will behave the same with legacy paths enabled (will assert otherwise).
405  - 2021/03/11 (1.82) - removed redirecting functions/enums names that were marked obsolete in 1.66 (September 2018):
406                         - ImGui::SetScrollHere()              -> use ImGui::SetScrollHereY()
407  - 2021/03/11 (1.82) - clarified that ImDrawList::PathArcTo(), ImDrawList::PathArcToFast() won't render with radius < 0.0f. Previously it sorts of accidentally worked but would generally lead to counter-clockwise paths and have an effect on anti-aliasing.
408  - 2021/03/10 (1.82) - upgraded ImDrawList::AddPolyline() and PathStroke() "bool closed" parameter to "ImDrawFlags flags". The matching ImDrawFlags_Closed value is guaranteed to always stay == 1 in the future.
409  - 2021/02/22 (1.82) - (*undone in 1.84*) win32+mingw: Re-enabled IME functions by default even under MinGW. In July 2016, issue #738 had me incorrectly disable those default functions for MinGW. MinGW users should: either link with -limm32, either set their imconfig file  with '#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS'.
410  - 2021/02/17 (1.82) - renamed rarely used style.CircleSegmentMaxError (old default = 1.60f) to style.CircleTessellationMaxError (new default = 0.30f) as the meaning of the value changed.
411  - 2021/02/03 (1.81) - renamed ListBoxHeader(const char* label, ImVec2 size) to BeginListBox(). Kept inline redirection function (will obsolete).
412                      - removed ListBoxHeader(const char* label, int items_count, int height_in_items = -1) in favor of specifying size. Kept inline redirection function (will obsolete).
413                      - renamed ListBoxFooter() to EndListBox(). Kept inline redirection function (will obsolete).
414  - 2021/01/26 (1.81) - removed ImGuiFreeType::BuildFontAtlas(). Kept inline redirection function. Prefer using '#define IMGUI_ENABLE_FREETYPE', but there's a runtime selection path available too. The shared extra flags parameters (very rarely used) are now stored in ImFontAtlas::FontBuilderFlags.
415                      - renamed ImFontConfig::RasterizerFlags (used by FreeType) to ImFontConfig::FontBuilderFlags.
416                      - renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API.
417  - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.63 (August 2018):
418                         - ImGui::IsItemDeactivatedAfterChange() -> use ImGui::IsItemDeactivatedAfterEdit().
419                         - ImGuiCol_ModalWindowDarkening       -> use ImGuiCol_ModalWindowDimBg
420                         - ImGuiInputTextCallback              -> use ImGuiTextEditCallback
421                         - ImGuiInputTextCallbackData          -> use ImGuiTextEditCallbackData
422  - 2020/12/21 (1.80) - renamed ImDrawList::AddBezierCurve() to AddBezierCubic(), and PathBezierCurveTo() to PathBezierCubicCurveTo(). Kept inline redirection function (will obsolete).
423  - 2020/12/04 (1.80) - added imgui_tables.cpp file! Manually constructed project files will need the new file added!
424  - 2020/11/18 (1.80) - renamed undocumented/internals ImGuiColumnsFlags_* to ImGuiOldColumnFlags_* in prevision of incoming Tables API.
425  - 2020/11/03 (1.80) - renamed io.ConfigWindowsMemoryCompactTimer to io.ConfigMemoryCompactTimer as the feature will apply to other data structures
426  - 2020/10/14 (1.80) - backends: moved all backends files (imgui_impl_XXXX.cpp, imgui_impl_XXXX.h) from examples/ to backends/.
427  - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.60 (April 2018):
428                         - io.RenderDrawListsFn pointer        -> use ImGui::GetDrawData() value and call the render function of your backend
429                         - ImGui::IsAnyWindowFocused()         -> use ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)
430                         - ImGui::IsAnyWindowHovered()         -> use ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
431                         - ImGuiStyleVar_Count_                -> use ImGuiStyleVar_COUNT
432                         - ImGuiMouseCursor_Count_             -> use ImGuiMouseCursor_COUNT
433                       - removed redirecting functions names that were marked obsolete in 1.61 (May 2018):
434                         - InputFloat (... int decimal_precision ...) -> use InputFloat (... const char* format ...) with format = "%.Xf" where X is your value for decimal_precision.
435                         - same for InputFloat2()/InputFloat3()/InputFloat4() variants taking a `int decimal_precision` parameter.
436  - 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).
437  - 2020/09/25 (1.79) - renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. Kept redirection enum (will obsolete sooner because previous name was added recently).
438  - 2020/09/25 (1.79) - renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton.
439  - 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.
440  - 2020/09/21 (1.79) - removed return value from OpenPopupOnItemClick() - returned true on mouse release on an item - because it is inconsistent with other popup APIs and makes others misleading. It's also and unnecessary: you can use IsWindowAppearing() after BeginPopup() for a similar result.
441  - 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!
442  - 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().
443                        replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags).
444                        worked out a backward-compatibility scheme so hopefully most C++ codebase should not be affected. in short, when calling those functions:
445                        - if you omitted the 'power' parameter (likely!), you are not affected.
446                        - 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.
447                        - 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.
448                        see https://github.com/ocornut/imgui/issues/3361 for all details.
449                        kept inline redirection functions (will obsolete) apart for: DragFloatRange2(), VSliderFloat(), VSliderScalar(). For those three the 'float power=1.0f' version was removed directly as they were most unlikely ever used.
450                        for shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`.
451                      - 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.
452  - 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.
453  - 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete). [NOTE: THIS WAS REVERTED IN 1.79]
454  - 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017.
455  - 2020/04/23 (1.77) - removed unnecessary ID (first arg) of ImFontAtlas::AddCustomRectRegular().
456  - 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more.
457  - 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.
458  - 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.
459  - 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017):
460                        - ShowTestWindow()                    -> use ShowDemoWindow()
461                        - IsRootWindowFocused()               -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow)
462                        - IsRootWindowOrAnyChildFocused()     -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
463                        - SetNextWindowContentWidth(w)        -> use SetNextWindowContentSize(ImVec2(w, 0.0f)
464                        - GetItemsLineHeightWithSpacing()     -> use GetFrameHeightWithSpacing()
465                        - ImGuiCol_ChildWindowBg              -> use ImGuiCol_ChildBg
466                        - ImGuiStyleVar_ChildWindowRounding   -> use ImGuiStyleVar_ChildRounding
467                        - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap
468                        - IMGUI_DISABLE_TEST_WINDOWS          -> use IMGUI_DISABLE_DEMO_WINDOWS
469  - 2019/12/08 (1.75) - obsoleted calling ImDrawList::PrimReserve() with a negative count (which was vaguely documented and rarely if ever used). Instead, we added an explicit PrimUnreserve() API.
470  - 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).
471  - 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.
472  - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency.
473  - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency.
474  - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017):
475                        - Begin() [old 5 args version]        -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed
476                        - IsRootWindowOrAnyChildHovered()     -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows)
477                        - AlignFirstTextHeightToWidgets()     -> use AlignTextToFramePadding()
478                        - SetNextWindowPosCenter()            -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f)
479                        - ImFont::Glyph                       -> use ImFontGlyph
480  - 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.
481                        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.
482                        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).
483                        If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you.
484  - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete).
485  - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete).
486  - 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.
487  - 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
488                        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.
489                        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.
490                        Please reach out if you are affected.
491  - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete).
492  - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c).
493  - 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.
494  - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).
495  - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).
496  - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).
497  - 2019/02/14 (1.68) - made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). If for some reason your time step calculation gives you a zero value, replace it with an arbitrarily small value!
498  - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
499  - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
500  - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).
501  - 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.
502  - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
503  - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
504  - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
505  - 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.
506                        If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
507  - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
508  - 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.
509                        NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
510                        Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
511  - 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).
512  - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
513  - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
514  - 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.
515  - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
516  - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
517  - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
518  - 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.).
519                        old backends will still work as is, however prefer using the separated backends as they will be updated to support multi-viewports.
520                        when adopting new backends follow the main.cpp code of your preferred examples/ folder to know which functions to call.
521                        in particular, note that old backends called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function.
522  - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
523  - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
524  - 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.
525                        If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
526                        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.
527                        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.
528  - 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",
529                        consistent with other functions. Kept redirection functions (will obsolete).
530  - 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.
531  - 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).
532  - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
533  - 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.
534  - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
535  - 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.
536  - 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.
537  - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
538                        - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
539                        - removed Shutdown() function, as DestroyContext() serve this purpose.
540                        - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
541                        - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
542                        - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
543  - 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.
544  - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
545  - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
546  - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
547  - 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.
548  - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
549  - 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
550  - 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.
551  - 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.
552  - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
553  - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
554                      - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
555  - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
556  - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
557  - 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.
558  - 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.
559                        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.
560  - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
561  - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
562  - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
563  - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
564  - 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.
565  - 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.
566  - 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.
567                        removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
568                          IsItemHoveredRect()        --> IsItemHovered(ImGuiHoveredFlags_RectOnly)
569                          IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
570                          IsMouseHoveringWindow()    --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior]
571  - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
572  - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
573  - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).
574  - 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).
575  - 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)".
576  - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
577                      - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
578                      - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
579  - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
580  - 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.
581  - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type.
582  - 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.
583  - 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).
584  - 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).
585  - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
586  - 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.
587                      - 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.
588                      - 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))'
589  - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
590  - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
591  - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
592  - 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().
593  - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
594  - 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.
595  - 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.
596  - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
597                        If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you, otherwise if <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
598                        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:
599                        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); }
600                        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.
601  - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
602  - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
603  - 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).
604  - 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.
605  - 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).
606  - 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)
607  - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
608  - 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.
609  - 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.
610  - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
611  - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
612  - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
613                        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.
614                        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!
615  - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
616  - 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.
617  - 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
618  - 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.
619                        you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
620  - 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.
621                        this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
622                      - 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.
623                      - the signature of the io.RenderDrawListsFn handler has changed!
624                        old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
625                        new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
626                          parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
627                          ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
628                          ImDrawCmd:  'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
629                      - 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.
630                      - 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!
631                      - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
632  - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
633  - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
634  - 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.
635  - 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
636  - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely used. Sorry!
637  - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
638  - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
639  - 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.
640  - 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.
641  - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
642  - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
643  - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
644  - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
645  - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
646  - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
647  - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
648  - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
649  - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
650  - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
651  - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
652  - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
653  - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
654  - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
655  - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
656  - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
657  - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
658  - 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
659                        - old:  const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..];
660                        - new:  unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->SetTexID(YourTexIdentifier);
661                        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.
662  - 2015/01/11 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to call io.Fonts->SetTexID()
663  - 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
664  - 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
665  - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
666  - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
667  - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
668  - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
669  - 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)
670  - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
671  - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
672  - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
673  - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
674  - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
675  - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
676 
677 
678  FREQUENTLY ASKED QUESTIONS (FAQ)
679  ================================
680 
681  Read all answers online:
682    https://www.dearimgui.org/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url)
683  Read all answers locally (with a text editor or ideally a Markdown viewer):
684    docs/FAQ.md
685  Some answers are copied down here to facilitate searching in code.
686 
687  Q&A: Basics
688  ===========
689 
690  Q: Where is the documentation?
691  A: This library is poorly documented at the moment and expects the user to be acquainted with C/C++.
692     - Run the examples/ and explore them.
693     - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function.
694     - The demo covers most features of Dear ImGui, so you can read the code and see its output.
695     - See documentation and comments at the top of imgui.cpp + effectively imgui.h.
696     - Dozens of standalone example applications using e.g. OpenGL/DirectX are provided in the
697       examples/ folder to explain how to integrate Dear ImGui with your own engine/application.
698     - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links.
699     - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful.
700     - Your programming IDE is your friend, find the type or function declaration to find comments
701       associated with it.
702 
703  Q: What is this library called?
704  Q: Which version should I get?
705  >> This library is called "Dear ImGui", please don't call it "ImGui" :)
706  >> See https://www.dearimgui.org/faq for details.
707 
708  Q&A: Integration
709  ================
710 
711  Q: How to get started?
712  A: Read 'PROGRAMMER GUIDE' above. Read examples/README.txt.
713 
714  Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?
715  A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
716  >> See https://www.dearimgui.org/faq for a fully detailed answer. You really want to read this.
717 
718  Q. How can I enable keyboard controls?
719  Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display)
720  Q: I integrated Dear ImGui in my engine and little squares are showing instead of text...
721  Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around...
722  Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries...
723  >> See https://www.dearimgui.org/faq
724 
725  Q&A: Usage
726  ----------
727 
728  Q: Why is my widget not reacting when I click on it?
729  Q: How can I have widgets with an empty label?
730  Q: How can I have multiple widgets with the same label?
731  Q: How can I display an image? What is ImTextureID, how does it works?
732  Q: How can I use my own math types instead of ImVec2/ImVec4?
733  Q: How can I interact with standard C++ types (such as std::string and std::vector)?
734  Q: How can I display custom shapes? (using low-level ImDrawList API)
735  >> See https://www.dearimgui.org/faq
736 
737  Q&A: Fonts, Text
738  ================
739 
740  Q: How should I handle DPI in my application?
741  Q: How can I load a different font than the default?
742  Q: How can I easily use icons in my application?
743  Q: How can I load multiple fonts?
744  Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
745  >> See https://www.dearimgui.org/faq and https://github.com/ocornut/imgui/edit/master/docs/FONTS.md
746 
747  Q&A: Concerns
748  =============
749 
750  Q: Who uses Dear ImGui?
751  Q: Can you create elaborate/serious tools with Dear ImGui?
752  Q: Can you reskin the look of Dear ImGui?
753  Q: Why using C++ (as opposed to C)?
754  >> See https://www.dearimgui.org/faq
755 
756  Q&A: Community
757  ==============
758 
759  Q: How can I help?
760  A: - Businesses: please reach out to "contact AT dearimgui.com" if you work in a place using Dear ImGui!
761       We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts.
762       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.
763     - Individuals: you can support continued development via PayPal donations. See README.
764     - If you are experienced with Dear ImGui and C++, look at the GitHub issues, look at the Wiki, read docs/TODO.txt
765       and see how you want to help and can help!
766     - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
767       You may post screenshot or links in the gallery threads. Visuals are ideal as they inspire other programmers.
768       But even without visuals, disclosing your use of dear imgui helps the library grow credibility, and help other teams and programmers with taking decisions.
769     - 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).
770 
771 */
772 
773 //-------------------------------------------------------------------------
774 // [SECTION] INCLUDES
775 //-------------------------------------------------------------------------
776 
777 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
778 #define _CRT_SECURE_NO_WARNINGS
779 #endif
780 
781 #include "imgui.h"
782 #ifndef IMGUI_DISABLE
783 
784 #ifndef IMGUI_DEFINE_MATH_OPERATORS
785 #define IMGUI_DEFINE_MATH_OPERATORS
786 #endif
787 #include "imgui_internal.h"
788 
789 // System includes
790 #include <ctype.h>      // toupper
791 #include <stdio.h>      // vsnprintf, sscanf, printf
792 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
793 #include <stddef.h>     // intptr_t
794 #else
795 #include <stdint.h>     // intptr_t
796 #endif
797 
798 // [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled
799 #if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
800 #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
801 #endif
802 
803 // [Windows] OS specific includes (optional)
804 #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)
805 #define IMGUI_DISABLE_WIN32_FUNCTIONS
806 #endif
807 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
808 #ifndef WIN32_LEAN_AND_MEAN
809 #define WIN32_LEAN_AND_MEAN
810 #endif
811 #ifndef NOMINMAX
812 #define NOMINMAX
813 #endif
814 #ifndef __MINGW32__
815 #include <Windows.h>        // _wfopen, OpenClipboard
816 #else
817 #include <windows.h>
818 #endif
819 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) // UWP doesn't have all Win32 functions
820 #define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
821 #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
822 #endif
823 #endif
824 
825 // [Apple] OS specific includes
826 #if defined(__APPLE__)
827 #include <TargetConditionals.h>
828 #endif
829 
830 // Visual Studio warnings
831 #ifdef _MSC_VER
832 #pragma warning (disable: 4127)             // condition expression is constant
833 #pragma warning (disable: 4996)             // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
834 #if defined(_MSC_VER) && _MSC_VER >= 1922   // MSVC 2019 16.2 or later
835 #pragma warning (disable: 5054)             // operator '|': deprecated between enumerations of different types
836 #endif
837 #pragma warning (disable: 26451)            // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
838 #pragma warning (disable: 26495)            // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).
839 #pragma warning (disable: 26812)            // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
840 #endif
841 
842 // Clang/GCC warnings with -Weverything
843 #if defined(__clang__)
844 #if __has_warning("-Wunknown-warning-option")
845 #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!
846 #endif
847 #pragma clang diagnostic ignored "-Wunknown-pragmas"                // warning: unknown warning group 'xxx'
848 #pragma clang diagnostic ignored "-Wold-style-cast"                 // warning: use of old-style cast                            // yes, they are more terse.
849 #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.
850 #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.
851 #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.
852 #pragma clang diagnostic ignored "-Wglobal-constructors"            // warning: declaration requires a global destructor         // similar to above, not sure what the exact difference is.
853 #pragma clang diagnostic ignored "-Wsign-conversion"                // warning: implicit conversion changes signedness
854 #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.
855 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast"       // warning: cast to 'void *' from smaller integer type 'int'
856 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"  // warning: zero as null pointer constant                    // some standard header variations use #define NULL 0
857 #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.
858 #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"  // warning: implicit conversion from 'xxx' to 'float' may lose precision
859 #elif defined(__GNUC__)
860 // We disable -Wpragmas because GCC doesn't provide an has_warning equivalent and some forks/patches may not following the warning/version association.
861 #pragma GCC diagnostic ignored "-Wpragmas"                  // warning: unknown option after '#pragma GCC diagnostic' kind
862 #pragma GCC diagnostic ignored "-Wunused-function"          // warning: 'xxxx' defined but not used
863 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"      // warning: cast to pointer from integer of different size
864 #pragma GCC diagnostic ignored "-Wformat"                   // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
865 #pragma GCC diagnostic ignored "-Wdouble-promotion"         // warning: implicit conversion from 'float' to 'double' when passing argument to function
866 #pragma GCC diagnostic ignored "-Wconversion"               // warning: conversion to 'xxxx' from 'xxxx' may alter its value
867 #pragma GCC diagnostic ignored "-Wformat-nonliteral"        // warning: format not a string literal, format string not checked
868 #pragma GCC diagnostic ignored "-Wstrict-overflow"          // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
869 #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
870 #endif
871 
872 // Debug options
873 #define IMGUI_DEBUG_NAV_SCORING     0   // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL
874 #define IMGUI_DEBUG_NAV_RECTS       0   // Display the reference navigation rectangle for each window
875 #define IMGUI_DEBUG_INI_SETTINGS    0   // Save additional comments in .ini file (particularly helps for Docking, but makes saving slower)
876 
877 // 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.
878 static const float NAV_WINDOWING_HIGHLIGHT_DELAY            = 0.20f;    // Time before the highlight and screen dimming starts fading in
879 static const float NAV_WINDOWING_LIST_APPEAR_DELAY          = 0.15f;    // Time before the window list starts to appear
880 
881 // Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by backend)
882 static const float WINDOWS_HOVER_PADDING                    = 4.0f;     // Extend outside window for hovering/resizing (maxxed with TouchPadding) and inside windows for borders. Affect FindHoveredWindow().
883 static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f;    // Reduce visual noise by only highlighting the border after a certain time.
884 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.
885 
886 // Docking
887 static const float DOCKING_TRANSPARENT_PAYLOAD_ALPHA        = 0.50f;    // For use with io.ConfigDockingTransparentPayload. Apply to Viewport _or_ WindowBg in host viewport.
888 static const float DOCKING_SPLITTER_SIZE                    = 2.0f;
889 
890 //-------------------------------------------------------------------------
891 // [SECTION] FORWARD DECLARATIONS
892 //-------------------------------------------------------------------------
893 
894 static void             SetCurrentWindow(ImGuiWindow* window);
895 static void             FindHoveredWindow();
896 static ImGuiWindow*     CreateNewWindow(const char* name, ImGuiWindowFlags flags);
897 static ImVec2           CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);
898 
899 static void             AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
900 static void             AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
901 
902 // Settings
903 static void             WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
904 static void*            WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
905 static void             WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
906 static void             WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);
907 static void             WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf);
908 
909 // Platform Dependents default implementation for IO functions
910 static const char*      GetClipboardTextFn_DefaultImpl(void* user_data);
911 static void             SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
912 
913 namespace ImGui
914 {
915 // Navigation
916 static void             NavUpdate();
917 static void             NavUpdateWindowing();
918 static void             NavUpdateWindowingOverlay();
919 static void             NavUpdateMoveResult();
920 static void             NavUpdateInitResult();
921 static float            NavUpdatePageUpPageDown();
922 static inline void      NavUpdateAnyRequestFlag();
923 static void             NavEndFrame();
924 static bool             NavScoreItem(ImGuiNavItemData* result, ImRect cand);
925 static void             NavApplyItemToResult(ImGuiNavItemData* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel);
926 static void             NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id);
927 static ImVec2           NavCalcPreferredRefPos();
928 static void             NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
929 static ImGuiWindow*     NavRestoreLastChildNavWindow(ImGuiWindow* window);
930 static void             NavRestoreLayer(ImGuiNavLayer layer);
931 static int              FindWindowFocusIndex(ImGuiWindow* window);
932 
933 // Error Checking
934 static void             ErrorCheckNewFrameSanityChecks();
935 static void             ErrorCheckEndFrameSanityChecks();
936 
937 // Misc
938 static void             UpdateSettings();
939 static void             UpdateMouseInputs();
940 static void             UpdateMouseWheel();
941 static void             UpdateTabFocus();
942 static void             UpdateDebugToolItemPicker();
943 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);
944 static void             RenderWindowOuterBorders(ImGuiWindow* window);
945 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);
946 static void             RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
947 static void             EndFrameDrawDimmedBackgrounds();
948 
949 // Viewports
950 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.
951 static ImGuiViewportP*  AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& platform_pos, const ImVec2& size, ImGuiViewportFlags flags);
952 static void             UpdateViewportsNewFrame();
953 static void             UpdateViewportsEndFrame();
954 static void             WindowSelectViewport(ImGuiWindow* window);
955 static void             WindowSyncOwnedViewport(ImGuiWindow* window, ImGuiWindow* parent_window_in_stack);
956 static bool             UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport);
957 static bool             UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window);
958 static bool             GetWindowAlwaysWantOwnViewport(ImGuiWindow* window);
959 static int              FindPlatformMonitorForPos(const ImVec2& pos);
960 static int              FindPlatformMonitorForRect(const ImRect& r);
961 static void             UpdateViewportPlatformMonitor(ImGuiViewportP* viewport);
962 
963 }
964 
965 //-----------------------------------------------------------------------------
966 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
967 //-----------------------------------------------------------------------------
968 
969 // DLL users:
970 // - Heaps and globals are not shared across DLL boundaries!
971 // - You will need to call SetCurrentContext() + SetAllocatorFunctions() for each static/DLL boundary you are calling from.
972 // - Same applies for hot-reloading mechanisms that are reliant on reloading DLL (note that many hot-reloading mechanisms work without DLL).
973 // - Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
974 // - Confused? In a debugger: add GImGui to your watch window and notice how its value changes depending on your current location (which DLL boundary you are in).
975 
976 // Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
977 // - ImGui::CreateContext() will automatically set this pointer if it is NULL.
978 //   Change to a different context by calling ImGui::SetCurrentContext().
979 // - Important: Dear ImGui functions are not thread-safe because of this pointer.
980 //   If you want thread-safety to allow N threads to access N different contexts:
981 //   - Change this variable to use thread local storage so each thread can refer to a different context, in your imconfig.h:
982 //         struct ImGuiContext;
983 //         extern thread_local ImGuiContext* MyImGuiTLS;
984 //         #define GImGui MyImGuiTLS
985 //     And then define MyImGuiTLS in one of your cpp files. Note that thread_local is a C++11 keyword, earlier C++ uses compiler-specific keyword.
986 //   - Future development aims to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
987 //   - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from a different namespace.
988 // - DLL users: read comments above.
989 #ifndef GImGui
990 ImGuiContext*   GImGui = NULL;
991 #endif
992 
993 // Memory Allocator functions. Use SetAllocatorFunctions() to change them.
994 // - You probably don't want to modify that mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction.
995 // - DLL users: read comments above.
996 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
MallocWrapper(size_t size,void * user_data)997 static void*   MallocWrapper(size_t size, void* user_data)    { IM_UNUSED(user_data); return malloc(size); }
FreeWrapper(void * ptr,void * user_data)998 static void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); free(ptr); }
999 #else
MallocWrapper(size_t size,void * user_data)1000 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)1001 static void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }
1002 #endif
1003 static ImGuiMemAllocFunc    GImAllocatorAllocFunc = MallocWrapper;
1004 static ImGuiMemFreeFunc     GImAllocatorFreeFunc = FreeWrapper;
1005 static void*                GImAllocatorUserData = NULL;
1006 
1007 //-----------------------------------------------------------------------------
1008 // [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
1009 //-----------------------------------------------------------------------------
1010 
ImGuiStyle()1011 ImGuiStyle::ImGuiStyle()
1012 {
1013     Alpha                   = 1.0f;             // Global alpha applies to everything in ImGui
1014     WindowPadding           = ImVec2(8,8);      // Padding within a window
1015     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.
1016     WindowBorderSize        = 1.0f;             // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1017     WindowMinSize           = ImVec2(32,32);    // Minimum window size
1018     WindowTitleAlign        = ImVec2(0.0f,0.5f);// Alignment for title bar text
1019     WindowMenuButtonPosition= ImGuiDir_Left;    // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.
1020     ChildRounding           = 0.0f;             // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
1021     ChildBorderSize         = 1.0f;             // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1022     PopupRounding           = 0.0f;             // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
1023     PopupBorderSize         = 1.0f;             // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1024     FramePadding            = ImVec2(4,3);      // Padding within a framed rectangle (used by most widgets)
1025     FrameRounding           = 0.0f;             // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
1026     FrameBorderSize         = 0.0f;             // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
1027     ItemSpacing             = ImVec2(8,4);      // Horizontal and vertical spacing between widgets/lines
1028     ItemInnerSpacing        = ImVec2(4,4);      // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
1029     CellPadding             = ImVec2(4,2);      // Padding within a table cell
1030     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!
1031     IndentSpacing           = 21.0f;            // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
1032     ColumnsMinSpacing       = 6.0f;             // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
1033     ScrollbarSize           = 14.0f;            // Width of the vertical scrollbar, Height of the horizontal scrollbar
1034     ScrollbarRounding       = 9.0f;             // Radius of grab corners rounding for scrollbar
1035     GrabMinSize             = 10.0f;            // Minimum width/height of a grab box for slider/scrollbar
1036     GrabRounding            = 0.0f;             // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
1037     LogSliderDeadzone       = 4.0f;             // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
1038     TabRounding             = 4.0f;             // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
1039     TabBorderSize           = 0.0f;             // Thickness of border around tabs.
1040     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.
1041     ColorButtonPosition     = ImGuiDir_Right;   // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
1042     ButtonTextAlign         = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
1043     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.
1044     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.
1045     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.
1046     MouseCursorScale        = 1.0f;             // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
1047     AntiAliasedLines        = true;             // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU.
1048     AntiAliasedLinesUseTex  = true;             // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering.
1049     AntiAliasedFill         = true;             // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).
1050     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.
1051     CircleTessellationMaxError = 0.30f;         // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
1052 
1053     // Default theme
1054     ImGui::StyleColorsDark(this);
1055 }
1056 
1057 // 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.
1058 // 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)1059 void ImGuiStyle::ScaleAllSizes(float scale_factor)
1060 {
1061     WindowPadding = ImFloor(WindowPadding * scale_factor);
1062     WindowRounding = ImFloor(WindowRounding * scale_factor);
1063     WindowMinSize = ImFloor(WindowMinSize * scale_factor);
1064     ChildRounding = ImFloor(ChildRounding * scale_factor);
1065     PopupRounding = ImFloor(PopupRounding * scale_factor);
1066     FramePadding = ImFloor(FramePadding * scale_factor);
1067     FrameRounding = ImFloor(FrameRounding * scale_factor);
1068     ItemSpacing = ImFloor(ItemSpacing * scale_factor);
1069     ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
1070     CellPadding = ImFloor(CellPadding * scale_factor);
1071     TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
1072     IndentSpacing = ImFloor(IndentSpacing * scale_factor);
1073     ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
1074     ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
1075     ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
1076     GrabMinSize = ImFloor(GrabMinSize * scale_factor);
1077     GrabRounding = ImFloor(GrabRounding * scale_factor);
1078     LogSliderDeadzone = ImFloor(LogSliderDeadzone * scale_factor);
1079     TabRounding = ImFloor(TabRounding * scale_factor);
1080     TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImFloor(TabMinWidthForCloseButton * scale_factor) : FLT_MAX;
1081     DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
1082     DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
1083     MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
1084 }
1085 
ImGuiIO()1086 ImGuiIO::ImGuiIO()
1087 {
1088     // Most fields are initialized with zero
1089     memset(this, 0, sizeof(*this));
1090     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.
1091 
1092     // Settings
1093     ConfigFlags = ImGuiConfigFlags_None;
1094     BackendFlags = ImGuiBackendFlags_None;
1095     DisplaySize = ImVec2(-1.0f, -1.0f);
1096     DeltaTime = 1.0f / 60.0f;
1097     IniSavingRate = 5.0f;
1098     IniFilename = "imgui.ini";
1099     LogFilename = "imgui_log.txt";
1100     MouseDoubleClickTime = 0.30f;
1101     MouseDoubleClickMaxDist = 6.0f;
1102     for (int i = 0; i < ImGuiKey_COUNT; i++)
1103         KeyMap[i] = -1;
1104     KeyRepeatDelay = 0.275f;
1105     KeyRepeatRate = 0.050f;
1106     UserData = NULL;
1107 
1108     Fonts = NULL;
1109     FontGlobalScale = 1.0f;
1110     FontDefault = NULL;
1111     FontAllowUserScaling = false;
1112     DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1113 
1114     // Docking options (when ImGuiConfigFlags_DockingEnable is set)
1115     ConfigDockingNoSplit = false;
1116     ConfigDockingAlwaysTabBar = false;
1117     ConfigDockingTransparentPayload = false;
1118 
1119     // Viewport options (when ImGuiConfigFlags_ViewportsEnable is set)
1120     ConfigViewportsNoAutoMerge = false;
1121     ConfigViewportsNoTaskBarIcon = false;
1122     ConfigViewportsNoDecoration = true;
1123     ConfigViewportsNoDefaultParent = false;
1124 
1125     // Miscellaneous options
1126     MouseDrawCursor = false;
1127 #ifdef __APPLE__
1128     ConfigMacOSXBehaviors = true;  // Set Mac OS X style defaults based on __APPLE__ compile time flag
1129 #else
1130     ConfigMacOSXBehaviors = false;
1131 #endif
1132     ConfigInputTextCursorBlink = true;
1133     ConfigWindowsResizeFromEdges = true;
1134     ConfigWindowsMoveFromTitleBarOnly = false;
1135     ConfigMemoryCompactTimer = 60.0f;
1136 
1137     // Platform Functions
1138     BackendPlatformName = BackendRendererName = NULL;
1139     BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1140     GetClipboardTextFn = GetClipboardTextFn_DefaultImpl;   // Platform dependent default implementations
1141     SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
1142     ClipboardUserData = NULL;
1143 
1144     // Input (NB: we already have memset zero the entire structure!)
1145     MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1146     MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1147     MouseDragThreshold = 6.0f;
1148     for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1149     for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i]  = KeysDownDurationPrev[i] = -1.0f;
1150     for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
1151 }
1152 
1153 // Pass in translated ASCII characters for text input.
1154 // - with glfw you can get those from the callback set in glfwSetCharCallback()
1155 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(unsigned int c)1156 void ImGuiIO::AddInputCharacter(unsigned int c)
1157 {
1158     if (c != 0)
1159         InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID);
1160 }
1161 
1162 // UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so
1163 // we should save the high surrogate.
AddInputCharacterUTF16(ImWchar16 c)1164 void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c)
1165 {
1166     if (c == 0 && InputQueueSurrogate == 0)
1167         return;
1168 
1169     if ((c & 0xFC00) == 0xD800) // High surrogate, must save
1170     {
1171         if (InputQueueSurrogate != 0)
1172             InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID);
1173         InputQueueSurrogate = c;
1174         return;
1175     }
1176 
1177     ImWchar cp = c;
1178     if (InputQueueSurrogate != 0)
1179     {
1180         if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate
1181         {
1182             InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID);
1183         }
1184         else
1185         {
1186 #if IM_UNICODE_CODEPOINT_MAX == 0xFFFF
1187             cp = IM_UNICODE_CODEPOINT_INVALID; // Codepoint will not fit in ImWchar
1188 #else
1189             cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000);
1190 #endif
1191         }
1192 
1193         InputQueueSurrogate = 0;
1194     }
1195     InputQueueCharacters.push_back(cp);
1196 }
1197 
AddInputCharactersUTF8(const char * utf8_chars)1198 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1199 {
1200     while (*utf8_chars != 0)
1201     {
1202         unsigned int c = 0;
1203         utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
1204         if (c != 0)
1205             InputQueueCharacters.push_back((ImWchar)c);
1206     }
1207 }
1208 
ClearInputCharacters()1209 void ImGuiIO::ClearInputCharacters()
1210 {
1211     InputQueueCharacters.resize(0);
1212 }
1213 
1214 //-----------------------------------------------------------------------------
1215 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
1216 //-----------------------------------------------------------------------------
1217 
ImBezierCubicClosestPoint(const ImVec2 & p1,const ImVec2 & p2,const ImVec2 & p3,const ImVec2 & p4,const ImVec2 & p,int num_segments)1218 ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments)
1219 {
1220     IM_ASSERT(num_segments > 0); // Use ImBezierCubicClosestPointCasteljau()
1221     ImVec2 p_last = p1;
1222     ImVec2 p_closest;
1223     float p_closest_dist2 = FLT_MAX;
1224     float t_step = 1.0f / (float)num_segments;
1225     for (int i_step = 1; i_step <= num_segments; i_step++)
1226     {
1227         ImVec2 p_current = ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step);
1228         ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1229         float dist2 = ImLengthSqr(p - p_line);
1230         if (dist2 < p_closest_dist2)
1231         {
1232             p_closest = p_line;
1233             p_closest_dist2 = dist2;
1234         }
1235         p_last = p_current;
1236     }
1237     return p_closest;
1238 }
1239 
1240 // 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)1241 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)
1242 {
1243     float dx = x4 - x1;
1244     float dy = y4 - y1;
1245     float d2 = ((x2 - x4) * dy - (y2 - y4) * dx);
1246     float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
1247     d2 = (d2 >= 0) ? d2 : -d2;
1248     d3 = (d3 >= 0) ? d3 : -d3;
1249     if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))
1250     {
1251         ImVec2 p_current(x4, y4);
1252         ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1253         float dist2 = ImLengthSqr(p - p_line);
1254         if (dist2 < p_closest_dist2)
1255         {
1256             p_closest = p_line;
1257             p_closest_dist2 = dist2;
1258         }
1259         p_last = p_current;
1260     }
1261     else if (level < 10)
1262     {
1263         float x12 = (x1 + x2)*0.5f,       y12 = (y1 + y2)*0.5f;
1264         float x23 = (x2 + x3)*0.5f,       y23 = (y2 + y3)*0.5f;
1265         float x34 = (x3 + x4)*0.5f,       y34 = (y3 + y4)*0.5f;
1266         float x123 = (x12 + x23)*0.5f,    y123 = (y12 + y23)*0.5f;
1267         float x234 = (x23 + x34)*0.5f,    y234 = (y23 + y34)*0.5f;
1268         float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f;
1269         ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);
1270         ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);
1271     }
1272 }
1273 
1274 // tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol
1275 // 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)1276 ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol)
1277 {
1278     IM_ASSERT(tess_tol > 0.0f);
1279     ImVec2 p_last = p1;
1280     ImVec2 p_closest;
1281     float p_closest_dist2 = FLT_MAX;
1282     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);
1283     return p_closest;
1284 }
1285 
ImLineClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & p)1286 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1287 {
1288     ImVec2 ap = p - a;
1289     ImVec2 ab_dir = b - a;
1290     float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1291     if (dot < 0.0f)
1292         return a;
1293     float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1294     if (dot > ab_len_sqr)
1295         return b;
1296     return a + ab_dir * dot / ab_len_sqr;
1297 }
1298 
ImTriangleContainsPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1299 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1300 {
1301     bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1302     bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1303     bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1304     return ((b1 == b2) && (b2 == b3));
1305 }
1306 
ImTriangleBarycentricCoords(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p,float & out_u,float & out_v,float & out_w)1307 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1308 {
1309     ImVec2 v0 = b - a;
1310     ImVec2 v1 = c - a;
1311     ImVec2 v2 = p - a;
1312     const float denom = v0.x * v1.y - v1.x * v0.y;
1313     out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1314     out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1315     out_u = 1.0f - out_v - out_w;
1316 }
1317 
ImTriangleClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1318 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1319 {
1320     ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1321     ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1322     ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1323     float dist2_ab = ImLengthSqr(p - proj_ab);
1324     float dist2_bc = ImLengthSqr(p - proj_bc);
1325     float dist2_ca = ImLengthSqr(p - proj_ca);
1326     float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1327     if (m == dist2_ab)
1328         return proj_ab;
1329     if (m == dist2_bc)
1330         return proj_bc;
1331     return proj_ca;
1332 }
1333 
1334 //-----------------------------------------------------------------------------
1335 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
1336 //-----------------------------------------------------------------------------
1337 
1338 // 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)1339 int ImStricmp(const char* str1, const char* str2)
1340 {
1341     int d;
1342     while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
1343     return d;
1344 }
1345 
ImStrnicmp(const char * str1,const char * str2,size_t count)1346 int ImStrnicmp(const char* str1, const char* str2, size_t count)
1347 {
1348     int d = 0;
1349     while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
1350     return d;
1351 }
1352 
ImStrncpy(char * dst,const char * src,size_t count)1353 void ImStrncpy(char* dst, const char* src, size_t count)
1354 {
1355     if (count < 1)
1356         return;
1357     if (count > 1)
1358         strncpy(dst, src, count - 1);
1359     dst[count - 1] = 0;
1360 }
1361 
ImStrdup(const char * str)1362 char* ImStrdup(const char* str)
1363 {
1364     size_t len = strlen(str);
1365     void* buf = IM_ALLOC(len + 1);
1366     return (char*)memcpy(buf, (const void*)str, len + 1);
1367 }
1368 
ImStrdupcpy(char * dst,size_t * p_dst_size,const char * src)1369 char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
1370 {
1371     size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1;
1372     size_t src_size = strlen(src) + 1;
1373     if (dst_buf_size < src_size)
1374     {
1375         IM_FREE(dst);
1376         dst = (char*)IM_ALLOC(src_size);
1377         if (p_dst_size)
1378             *p_dst_size = src_size;
1379     }
1380     return (char*)memcpy(dst, (const void*)src, src_size);
1381 }
1382 
ImStrchrRange(const char * str,const char * str_end,char c)1383 const char* ImStrchrRange(const char* str, const char* str_end, char c)
1384 {
1385     const char* p = (const char*)memchr(str, (int)c, str_end - str);
1386     return p;
1387 }
1388 
ImStrlenW(const ImWchar * str)1389 int ImStrlenW(const ImWchar* str)
1390 {
1391     //return (int)wcslen((const wchar_t*)str);  // FIXME-OPT: Could use this when wchar_t are 16-bit
1392     int n = 0;
1393     while (*str++) n++;
1394     return n;
1395 }
1396 
1397 // Find end-of-line. Return pointer will point to either first \n, either str_end.
ImStreolRange(const char * str,const char * str_end)1398 const char* ImStreolRange(const char* str, const char* str_end)
1399 {
1400     const char* p = (const char*)memchr(str, '\n', str_end - str);
1401     return p ? p : str_end;
1402 }
1403 
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)1404 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
1405 {
1406     while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1407         buf_mid_line--;
1408     return buf_mid_line;
1409 }
1410 
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)1411 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1412 {
1413     if (!needle_end)
1414         needle_end = needle + strlen(needle);
1415 
1416     const char un0 = (char)toupper(*needle);
1417     while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1418     {
1419         if (toupper(*haystack) == un0)
1420         {
1421             const char* b = needle + 1;
1422             for (const char* a = haystack + 1; b < needle_end; a++, b++)
1423                 if (toupper(*a) != toupper(*b))
1424                     break;
1425             if (b == needle_end)
1426                 return haystack;
1427         }
1428         haystack++;
1429     }
1430     return NULL;
1431 }
1432 
1433 // 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)1434 void ImStrTrimBlanks(char* buf)
1435 {
1436     char* p = buf;
1437     while (p[0] == ' ' || p[0] == '\t')     // Leading blanks
1438         p++;
1439     char* p_start = p;
1440     while (*p != 0)                         // Find end of string
1441         p++;
1442     while (p > p_start && (p[-1] == ' ' || p[-1] == '\t'))  // Trailing blanks
1443         p--;
1444     if (p_start != buf)                     // Copy memory if we had leading blanks
1445         memmove(buf, p_start, p - p_start);
1446     buf[p - p_start] = 0;                   // Zero terminate
1447 }
1448 
ImStrSkipBlank(const char * str)1449 const char* ImStrSkipBlank(const char* str)
1450 {
1451     while (str[0] == ' ' || str[0] == '\t')
1452         str++;
1453     return str;
1454 }
1455 
1456 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1457 // 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.
1458 // B) When buf==NULL vsnprintf() will return the output size.
1459 #ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1460 
1461 // We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h)
1462 // You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1463 // and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are
1464 // designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.)
1465 #ifdef IMGUI_USE_STB_SPRINTF
1466 #define STB_SPRINTF_IMPLEMENTATION
1467 #include "stb_sprintf.h"
1468 #endif
1469 
1470 #if defined(_MSC_VER) && !defined(vsnprintf)
1471 #define vsnprintf _vsnprintf
1472 #endif
1473 
ImFormatString(char * buf,size_t buf_size,const char * fmt,...)1474 int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1475 {
1476     va_list args;
1477     va_start(args, fmt);
1478 #ifdef IMGUI_USE_STB_SPRINTF
1479     int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1480 #else
1481     int w = vsnprintf(buf, buf_size, fmt, args);
1482 #endif
1483     va_end(args);
1484     if (buf == NULL)
1485         return w;
1486     if (w == -1 || w >= (int)buf_size)
1487         w = (int)buf_size - 1;
1488     buf[w] = 0;
1489     return w;
1490 }
1491 
ImFormatStringV(char * buf,size_t buf_size,const char * fmt,va_list args)1492 int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1493 {
1494 #ifdef IMGUI_USE_STB_SPRINTF
1495     int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1496 #else
1497     int w = vsnprintf(buf, buf_size, fmt, args);
1498 #endif
1499     if (buf == NULL)
1500         return w;
1501     if (w == -1 || w >= (int)buf_size)
1502         w = (int)buf_size - 1;
1503     buf[w] = 0;
1504     return w;
1505 }
1506 #endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1507 
1508 // CRC32 needs a 1KB lookup table (not cache friendly)
1509 // Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
1510 // - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
1511 static const ImU32 GCrc32LookupTable[256] =
1512 {
1513     0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
1514     0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
1515     0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
1516     0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
1517     0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
1518     0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
1519     0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
1520     0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
1521     0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
1522     0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
1523     0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
1524     0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
1525     0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
1526     0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
1527     0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
1528     0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
1529 };
1530 
1531 // Known size hash
1532 // It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
1533 // 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)1534 ImGuiID ImHashData(const void* data_p, size_t data_size, ImU32 seed)
1535 {
1536     ImU32 crc = ~seed;
1537     const unsigned char* data = (const unsigned char*)data_p;
1538     const ImU32* crc32_lut = GCrc32LookupTable;
1539     while (data_size-- != 0)
1540         crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
1541     return ~crc;
1542 }
1543 
1544 // Zero-terminated string hash, with support for ### to reset back to seed value
1545 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1546 // Because this syntax is rarely used we are optimizing for the common case.
1547 // - If we reach ### in the string we discard the hash so far and reset to the seed.
1548 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
1549 // 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)1550 ImGuiID ImHashStr(const char* data_p, size_t data_size, ImU32 seed)
1551 {
1552     seed = ~seed;
1553     ImU32 crc = seed;
1554     const unsigned char* data = (const unsigned char*)data_p;
1555     const ImU32* crc32_lut = GCrc32LookupTable;
1556     if (data_size != 0)
1557     {
1558         while (data_size-- != 0)
1559         {
1560             unsigned char c = *data++;
1561             if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')
1562                 crc = seed;
1563             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1564         }
1565     }
1566     else
1567     {
1568         while (unsigned char c = *data++)
1569         {
1570             if (c == '#' && data[0] == '#' && data[1] == '#')
1571                 crc = seed;
1572             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1573         }
1574     }
1575     return ~crc;
1576 }
1577 
1578 //-----------------------------------------------------------------------------
1579 // [SECTION] MISC HELPERS/UTILITIES (File functions)
1580 //-----------------------------------------------------------------------------
1581 
1582 // Default file functions
1583 #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
1584 
ImFileOpen(const char * filename,const char * mode)1585 ImFileHandle ImFileOpen(const char* filename, const char* mode)
1586 {
1587 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__)
1588     // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.
1589     // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32!
1590     const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
1591     const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
1592     ImVector<ImWchar> buf;
1593     buf.resize(filename_wsize + mode_wsize);
1594     ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, (wchar_t*)&buf[0], filename_wsize);
1595     ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, (wchar_t*)&buf[filename_wsize], mode_wsize);
1596     return ::_wfopen((const wchar_t*)&buf[0], (const wchar_t*)&buf[filename_wsize]);
1597 #else
1598     return fopen(filename, mode);
1599 #endif
1600 }
1601 
1602 // 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)1603 bool    ImFileClose(ImFileHandle f)     { return fclose(f) == 0; }
ImFileGetSize(ImFileHandle f)1604 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)1605 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)1606 ImU64   ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f)    { return fwrite(data, (size_t)sz, (size_t)count, f); }
1607 #endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
1608 
1609 // Helper: Load file content into memory
1610 // Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree()
1611 // 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)1612 void*   ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes)
1613 {
1614     IM_ASSERT(filename && mode);
1615     if (out_file_size)
1616         *out_file_size = 0;
1617 
1618     ImFileHandle f;
1619     if ((f = ImFileOpen(filename, mode)) == NULL)
1620         return NULL;
1621 
1622     size_t file_size = (size_t)ImFileGetSize(f);
1623     if (file_size == (size_t)-1)
1624     {
1625         ImFileClose(f);
1626         return NULL;
1627     }
1628 
1629     void* file_data = IM_ALLOC(file_size + padding_bytes);
1630     if (file_data == NULL)
1631     {
1632         ImFileClose(f);
1633         return NULL;
1634     }
1635     if (ImFileRead(file_data, 1, file_size, f) != file_size)
1636     {
1637         ImFileClose(f);
1638         IM_FREE(file_data);
1639         return NULL;
1640     }
1641     if (padding_bytes > 0)
1642         memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
1643 
1644     ImFileClose(f);
1645     if (out_file_size)
1646         *out_file_size = file_size;
1647 
1648     return file_data;
1649 }
1650 
1651 //-----------------------------------------------------------------------------
1652 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
1653 //-----------------------------------------------------------------------------
1654 
1655 // Convert UTF-8 to 32-bit character, process single character input.
1656 // A nearly-branchless UTF-8 decoder, based on work of Christopher Wellons (https://github.com/skeeto/branchless-utf8).
1657 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)1658 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1659 {
1660     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 };
1661     static const int masks[]  = { 0x00, 0x7f, 0x1f, 0x0f, 0x07 };
1662     static const uint32_t mins[] = { 0x400000, 0, 0x80, 0x800, 0x10000 };
1663     static const int shiftc[] = { 0, 18, 12, 6, 0 };
1664     static const int shifte[] = { 0, 6, 4, 2, 0 };
1665     int len = lengths[*(const unsigned char*)in_text >> 3];
1666     int wanted = len + !len;
1667 
1668     if (in_text_end == NULL)
1669         in_text_end = in_text + wanted; // Max length, nulls will be taken into account.
1670 
1671     // Copy at most 'len' bytes, stop copying at 0 or past in_text_end. Branch predictor does a good job here,
1672     // so it is fast even with excessive branching.
1673     unsigned char s[4];
1674     s[0] = in_text + 0 < in_text_end ? in_text[0] : 0;
1675     s[1] = in_text + 1 < in_text_end ? in_text[1] : 0;
1676     s[2] = in_text + 2 < in_text_end ? in_text[2] : 0;
1677     s[3] = in_text + 3 < in_text_end ? in_text[3] : 0;
1678 
1679     // Assume a four-byte character and load four bytes. Unused bits are shifted out.
1680     *out_char  = (uint32_t)(s[0] & masks[len]) << 18;
1681     *out_char |= (uint32_t)(s[1] & 0x3f) << 12;
1682     *out_char |= (uint32_t)(s[2] & 0x3f) <<  6;
1683     *out_char |= (uint32_t)(s[3] & 0x3f) <<  0;
1684     *out_char >>= shiftc[len];
1685 
1686     // Accumulate the various error conditions.
1687     int e = 0;
1688     e  = (*out_char < mins[len]) << 6; // non-canonical encoding
1689     e |= ((*out_char >> 11) == 0x1b) << 7;  // surrogate half?
1690     e |= (*out_char > IM_UNICODE_CODEPOINT_MAX) << 8;  // out of range?
1691     e |= (s[1] & 0xc0) >> 2;
1692     e |= (s[2] & 0xc0) >> 4;
1693     e |= (s[3]       ) >> 6;
1694     e ^= 0x2a; // top two bits of each tail byte correct?
1695     e >>= shifte[len];
1696 
1697     if (e)
1698     {
1699         // No bytes are consumed when *in_text == 0 || in_text == in_text_end.
1700         // One byte is consumed in case of invalid first byte of in_text.
1701         // All available bytes (at most `len` bytes) are consumed on incomplete/invalid second to last bytes.
1702         // Invalid or incomplete input may consume less bytes than wanted, therefore every byte has to be inspected in s.
1703         wanted = ImMin(wanted, !!s[0] + !!s[1] + !!s[2] + !!s[3]);
1704         *out_char = IM_UNICODE_CODEPOINT_INVALID;
1705     }
1706 
1707     return wanted;
1708 }
1709 
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1710 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1711 {
1712     ImWchar* buf_out = buf;
1713     ImWchar* buf_end = buf + buf_size;
1714     while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
1715     {
1716         unsigned int c;
1717         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1718         if (c == 0)
1719             break;
1720         *buf_out++ = (ImWchar)c;
1721     }
1722     *buf_out = 0;
1723     if (in_text_remaining)
1724         *in_text_remaining = in_text;
1725     return (int)(buf_out - buf);
1726 }
1727 
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1728 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1729 {
1730     int char_count = 0;
1731     while ((!in_text_end || in_text < in_text_end) && *in_text)
1732     {
1733         unsigned int c;
1734         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1735         if (c == 0)
1736             break;
1737         char_count++;
1738     }
1739     return char_count;
1740 }
1741 
1742 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8_inline(char * buf,int buf_size,unsigned int c)1743 static inline int ImTextCharToUtf8_inline(char* buf, int buf_size, unsigned int c)
1744 {
1745     if (c < 0x80)
1746     {
1747         buf[0] = (char)c;
1748         return 1;
1749     }
1750     if (c < 0x800)
1751     {
1752         if (buf_size < 2) return 0;
1753         buf[0] = (char)(0xc0 + (c >> 6));
1754         buf[1] = (char)(0x80 + (c & 0x3f));
1755         return 2;
1756     }
1757     if (c < 0x10000)
1758     {
1759         if (buf_size < 3) return 0;
1760         buf[0] = (char)(0xe0 + (c >> 12));
1761         buf[1] = (char)(0x80 + ((c >> 6) & 0x3f));
1762         buf[2] = (char)(0x80 + ((c ) & 0x3f));
1763         return 3;
1764     }
1765     if (c <= 0x10FFFF)
1766     {
1767         if (buf_size < 4) return 0;
1768         buf[0] = (char)(0xf0 + (c >> 18));
1769         buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1770         buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1771         buf[3] = (char)(0x80 + ((c ) & 0x3f));
1772         return 4;
1773     }
1774     // Invalid code point, the max unicode is 0x10FFFF
1775     return 0;
1776 }
1777 
ImTextCharToUtf8(char out_buf[5],unsigned int c)1778 const char* ImTextCharToUtf8(char out_buf[5], unsigned int c)
1779 {
1780     int count = ImTextCharToUtf8_inline(out_buf, 5, c);
1781     out_buf[count] = 0;
1782     return out_buf;
1783 }
1784 
1785 // Not optimal but we very rarely use this function.
ImTextCountUtf8BytesFromChar(const char * in_text,const char * in_text_end)1786 int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
1787 {
1788     unsigned int unused = 0;
1789     return ImTextCharFromUtf8(&unused, in_text, in_text_end);
1790 }
1791 
ImTextCountUtf8BytesFromChar(unsigned int c)1792 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1793 {
1794     if (c < 0x80) return 1;
1795     if (c < 0x800) return 2;
1796     if (c < 0x10000) return 3;
1797     if (c <= 0x10FFFF) return 4;
1798     return 3;
1799 }
1800 
ImTextStrToUtf8(char * out_buf,int out_buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1801 int ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1802 {
1803     char* buf_p = out_buf;
1804     const char* buf_end = out_buf + out_buf_size;
1805     while (buf_p < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
1806     {
1807         unsigned int c = (unsigned int)(*in_text++);
1808         if (c < 0x80)
1809             *buf_p++ = (char)c;
1810         else
1811             buf_p += ImTextCharToUtf8_inline(buf_p, (int)(buf_end - buf_p - 1), c);
1812     }
1813     *buf_p = 0;
1814     return (int)(buf_p - out_buf);
1815 }
1816 
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1817 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1818 {
1819     int bytes_count = 0;
1820     while ((!in_text_end || in_text < in_text_end) && *in_text)
1821     {
1822         unsigned int c = (unsigned int)(*in_text++);
1823         if (c < 0x80)
1824             bytes_count++;
1825         else
1826             bytes_count += ImTextCountUtf8BytesFromChar(c);
1827     }
1828     return bytes_count;
1829 }
1830 
1831 //-----------------------------------------------------------------------------
1832 // [SECTION] MISC HELPERS/UTILITIES (Color functions)
1833 // Note: The Convert functions are early design which are not consistent with other API.
1834 //-----------------------------------------------------------------------------
1835 
ImAlphaBlendColors(ImU32 col_a,ImU32 col_b)1836 IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b)
1837 {
1838     float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;
1839     int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);
1840     int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);
1841     int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);
1842     return IM_COL32(r, g, b, 0xFF);
1843 }
1844 
ColorConvertU32ToFloat4(ImU32 in)1845 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1846 {
1847     float s = 1.0f / 255.0f;
1848     return ImVec4(
1849         ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1850         ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1851         ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1852         ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1853 }
1854 
ColorConvertFloat4ToU32(const ImVec4 & in)1855 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1856 {
1857     ImU32 out;
1858     out  = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1859     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1860     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1861     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1862     return out;
1863 }
1864 
1865 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1866 // 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)1867 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1868 {
1869     float K = 0.f;
1870     if (g < b)
1871     {
1872         ImSwap(g, b);
1873         K = -1.f;
1874     }
1875     if (r < g)
1876     {
1877         ImSwap(r, g);
1878         K = -2.f / 6.f - K;
1879     }
1880 
1881     const float chroma = r - (g < b ? g : b);
1882     out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
1883     out_s = chroma / (r + 1e-20f);
1884     out_v = r;
1885 }
1886 
1887 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1888 // 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)1889 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1890 {
1891     if (s == 0.0f)
1892     {
1893         // gray
1894         out_r = out_g = out_b = v;
1895         return;
1896     }
1897 
1898     h = ImFmod(h, 1.0f) / (60.0f / 360.0f);
1899     int   i = (int)h;
1900     float f = h - (float)i;
1901     float p = v * (1.0f - s);
1902     float q = v * (1.0f - s * f);
1903     float t = v * (1.0f - s * (1.0f - f));
1904 
1905     switch (i)
1906     {
1907     case 0: out_r = v; out_g = t; out_b = p; break;
1908     case 1: out_r = q; out_g = v; out_b = p; break;
1909     case 2: out_r = p; out_g = v; out_b = t; break;
1910     case 3: out_r = p; out_g = q; out_b = v; break;
1911     case 4: out_r = t; out_g = p; out_b = v; break;
1912     case 5: default: out_r = v; out_g = p; out_b = q; break;
1913     }
1914 }
1915 
1916 //-----------------------------------------------------------------------------
1917 // [SECTION] ImGuiStorage
1918 // Helper: Key->value storage
1919 //-----------------------------------------------------------------------------
1920 
1921 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair> & data,ImGuiID key)1922 static ImGuiStorage::ImGuiStoragePair* LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair>& data, ImGuiID key)
1923 {
1924     ImGuiStorage::ImGuiStoragePair* first = data.Data;
1925     ImGuiStorage::ImGuiStoragePair* last = data.Data + data.Size;
1926     size_t count = (size_t)(last - first);
1927     while (count > 0)
1928     {
1929         size_t count2 = count >> 1;
1930         ImGuiStorage::ImGuiStoragePair* mid = first + count2;
1931         if (mid->key < key)
1932         {
1933             first = ++mid;
1934             count -= count2 + 1;
1935         }
1936         else
1937         {
1938             count = count2;
1939         }
1940     }
1941     return first;
1942 }
1943 
1944 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
BuildSortByKey()1945 void ImGuiStorage::BuildSortByKey()
1946 {
1947     struct StaticFunc
1948     {
1949         static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)
1950         {
1951             // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1952             if (((const ImGuiStoragePair*)lhs)->key > ((const ImGuiStoragePair*)rhs)->key) return +1;
1953             if (((const ImGuiStoragePair*)lhs)->key < ((const ImGuiStoragePair*)rhs)->key) return -1;
1954             return 0;
1955         }
1956     };
1957     if (Data.Size > 1)
1958         ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairCompareByID);
1959 }
1960 
GetInt(ImGuiID key,int default_val) const1961 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1962 {
1963     ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1964     if (it == Data.end() || it->key != key)
1965         return default_val;
1966     return it->val_i;
1967 }
1968 
GetBool(ImGuiID key,bool default_val) const1969 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1970 {
1971     return GetInt(key, default_val ? 1 : 0) != 0;
1972 }
1973 
GetFloat(ImGuiID key,float default_val) const1974 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1975 {
1976     ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1977     if (it == Data.end() || it->key != key)
1978         return default_val;
1979     return it->val_f;
1980 }
1981 
GetVoidPtr(ImGuiID key) const1982 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1983 {
1984     ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1985     if (it == Data.end() || it->key != key)
1986         return NULL;
1987     return it->val_p;
1988 }
1989 
1990 // 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)1991 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1992 {
1993     ImGuiStoragePair* it = LowerBound(Data, key);
1994     if (it == Data.end() || it->key != key)
1995         it = Data.insert(it, ImGuiStoragePair(key, default_val));
1996     return &it->val_i;
1997 }
1998 
GetBoolRef(ImGuiID key,bool default_val)1999 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
2000 {
2001     return (bool*)GetIntRef(key, default_val ? 1 : 0);
2002 }
2003 
GetFloatRef(ImGuiID key,float default_val)2004 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
2005 {
2006     ImGuiStoragePair* it = LowerBound(Data, key);
2007     if (it == Data.end() || it->key != key)
2008         it = Data.insert(it, ImGuiStoragePair(key, default_val));
2009     return &it->val_f;
2010 }
2011 
GetVoidPtrRef(ImGuiID key,void * default_val)2012 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
2013 {
2014     ImGuiStoragePair* it = LowerBound(Data, key);
2015     if (it == Data.end() || it->key != key)
2016         it = Data.insert(it, ImGuiStoragePair(key, default_val));
2017     return &it->val_p;
2018 }
2019 
2020 // 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)2021 void ImGuiStorage::SetInt(ImGuiID key, int val)
2022 {
2023     ImGuiStoragePair* it = LowerBound(Data, key);
2024     if (it == Data.end() || it->key != key)
2025     {
2026         Data.insert(it, ImGuiStoragePair(key, val));
2027         return;
2028     }
2029     it->val_i = val;
2030 }
2031 
SetBool(ImGuiID key,bool val)2032 void ImGuiStorage::SetBool(ImGuiID key, bool val)
2033 {
2034     SetInt(key, val ? 1 : 0);
2035 }
2036 
SetFloat(ImGuiID key,float val)2037 void ImGuiStorage::SetFloat(ImGuiID key, float val)
2038 {
2039     ImGuiStoragePair* it = LowerBound(Data, key);
2040     if (it == Data.end() || it->key != key)
2041     {
2042         Data.insert(it, ImGuiStoragePair(key, val));
2043         return;
2044     }
2045     it->val_f = val;
2046 }
2047 
SetVoidPtr(ImGuiID key,void * val)2048 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
2049 {
2050     ImGuiStoragePair* it = LowerBound(Data, key);
2051     if (it == Data.end() || it->key != key)
2052     {
2053         Data.insert(it, ImGuiStoragePair(key, val));
2054         return;
2055     }
2056     it->val_p = val;
2057 }
2058 
SetAllInt(int v)2059 void ImGuiStorage::SetAllInt(int v)
2060 {
2061     for (int i = 0; i < Data.Size; i++)
2062         Data[i].val_i = v;
2063 }
2064 
2065 //-----------------------------------------------------------------------------
2066 // [SECTION] ImGuiTextFilter
2067 //-----------------------------------------------------------------------------
2068 
2069 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)2070 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
2071 {
2072     if (default_filter)
2073     {
2074         ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
2075         Build();
2076     }
2077     else
2078     {
2079         InputBuf[0] = 0;
2080         CountGrep = 0;
2081     }
2082 }
2083 
Draw(const char * label,float width)2084 bool ImGuiTextFilter::Draw(const char* label, float width)
2085 {
2086     if (width != 0.0f)
2087         ImGui::SetNextItemWidth(width);
2088     bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
2089     if (value_changed)
2090         Build();
2091     return value_changed;
2092 }
2093 
split(char separator,ImVector<ImGuiTextRange> * out) const2094 void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const
2095 {
2096     out->resize(0);
2097     const char* wb = b;
2098     const char* we = wb;
2099     while (we < e)
2100     {
2101         if (*we == separator)
2102         {
2103             out->push_back(ImGuiTextRange(wb, we));
2104             wb = we + 1;
2105         }
2106         we++;
2107     }
2108     if (wb != we)
2109         out->push_back(ImGuiTextRange(wb, we));
2110 }
2111 
Build()2112 void ImGuiTextFilter::Build()
2113 {
2114     Filters.resize(0);
2115     ImGuiTextRange input_range(InputBuf, InputBuf + strlen(InputBuf));
2116     input_range.split(',', &Filters);
2117 
2118     CountGrep = 0;
2119     for (int i = 0; i != Filters.Size; i++)
2120     {
2121         ImGuiTextRange& f = Filters[i];
2122         while (f.b < f.e && ImCharIsBlankA(f.b[0]))
2123             f.b++;
2124         while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
2125             f.e--;
2126         if (f.empty())
2127             continue;
2128         if (Filters[i].b[0] != '-')
2129             CountGrep += 1;
2130     }
2131 }
2132 
PassFilter(const char * text,const char * text_end) const2133 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
2134 {
2135     if (Filters.empty())
2136         return true;
2137 
2138     if (text == NULL)
2139         text = "";
2140 
2141     for (int i = 0; i != Filters.Size; i++)
2142     {
2143         const ImGuiTextRange& f = Filters[i];
2144         if (f.empty())
2145             continue;
2146         if (f.b[0] == '-')
2147         {
2148             // Subtract
2149             if (ImStristr(text, text_end, f.b + 1, f.e) != NULL)
2150                 return false;
2151         }
2152         else
2153         {
2154             // Grep
2155             if (ImStristr(text, text_end, f.b, f.e) != NULL)
2156                 return true;
2157         }
2158     }
2159 
2160     // Implicit * grep
2161     if (CountGrep == 0)
2162         return true;
2163 
2164     return false;
2165 }
2166 
2167 //-----------------------------------------------------------------------------
2168 // [SECTION] ImGuiTextBuffer
2169 //-----------------------------------------------------------------------------
2170 
2171 // On some platform vsnprintf() takes va_list by reference and modifies it.
2172 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
2173 #ifndef va_copy
2174 #if defined(__GNUC__) || defined(__clang__)
2175 #define va_copy(dest, src) __builtin_va_copy(dest, src)
2176 #else
2177 #define va_copy(dest, src) (dest = src)
2178 #endif
2179 #endif
2180 
2181 char ImGuiTextBuffer::EmptyString[1] = { 0 };
2182 
append(const char * str,const char * str_end)2183 void ImGuiTextBuffer::append(const char* str, const char* str_end)
2184 {
2185     int len = str_end ? (int)(str_end - str) : (int)strlen(str);
2186 
2187     // Add zero-terminator the first time
2188     const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2189     const int needed_sz = write_off + len;
2190     if (write_off + len >= Buf.Capacity)
2191     {
2192         int new_capacity = Buf.Capacity * 2;
2193         Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2194     }
2195 
2196     Buf.resize(needed_sz);
2197     memcpy(&Buf[write_off - 1], str, (size_t)len);
2198     Buf[write_off - 1 + len] = 0;
2199 }
2200 
appendf(const char * fmt,...)2201 void ImGuiTextBuffer::appendf(const char* fmt, ...)
2202 {
2203     va_list args;
2204     va_start(args, fmt);
2205     appendfv(fmt, args);
2206     va_end(args);
2207 }
2208 
2209 // Helper: Text buffer for logging/accumulating text
appendfv(const char * fmt,va_list args)2210 void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
2211 {
2212     va_list args_copy;
2213     va_copy(args_copy, args);
2214 
2215     int len = ImFormatStringV(NULL, 0, fmt, args);         // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
2216     if (len <= 0)
2217     {
2218         va_end(args_copy);
2219         return;
2220     }
2221 
2222     // Add zero-terminator the first time
2223     const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2224     const int needed_sz = write_off + len;
2225     if (write_off + len >= Buf.Capacity)
2226     {
2227         int new_capacity = Buf.Capacity * 2;
2228         Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2229     }
2230 
2231     Buf.resize(needed_sz);
2232     ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
2233     va_end(args_copy);
2234 }
2235 
2236 //-----------------------------------------------------------------------------
2237 // [SECTION] ImGuiListClipper
2238 // This is currently not as flexible/powerful as it should be and really confusing/spaghetti, mostly because we changed
2239 // the API mid-way through development and support two ways to using the clipper, needs some rework (see TODO)
2240 //-----------------------------------------------------------------------------
2241 
2242 // FIXME-TABLE: This prevents us from using ImGuiListClipper _inside_ a table cell.
2243 // The problem we have is that without a Begin/End scheme for rows using the clipper is ambiguous.
GetSkipItemForListClipping()2244 static bool GetSkipItemForListClipping()
2245 {
2246     ImGuiContext& g = *GImGui;
2247     return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems);
2248 }
2249 
2250 // Helper to calculate coarse clipping of large list of evenly sized items.
2251 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
2252 // 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)2253 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
2254 {
2255     ImGuiContext& g = *GImGui;
2256     ImGuiWindow* window = g.CurrentWindow;
2257     if (g.LogEnabled)
2258     {
2259         // If logging is active, do not perform any clipping
2260         *out_items_display_start = 0;
2261         *out_items_display_end = items_count;
2262         return;
2263     }
2264     if (GetSkipItemForListClipping())
2265     {
2266         *out_items_display_start = *out_items_display_end = 0;
2267         return;
2268     }
2269 
2270     // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect
2271     ImRect unclipped_rect = window->ClipRect;
2272     if (g.NavMoveRequest)
2273         unclipped_rect.Add(g.NavScoringRect);
2274     if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId)
2275         unclipped_rect.Add(ImRect(window->Pos + window->NavRectRel[0].Min, window->Pos + window->NavRectRel[0].Max));
2276 
2277     const ImVec2 pos = window->DC.CursorPos;
2278     int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
2279     int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
2280 
2281     // When performing a navigation request, ensure we have one item extra in the direction we are moving to
2282     if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up)
2283         start--;
2284     if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down)
2285         end++;
2286 
2287     start = ImClamp(start, 0, items_count);
2288     end = ImClamp(end + 1, start, items_count);
2289     *out_items_display_start = start;
2290     *out_items_display_end = end;
2291 }
2292 
SetCursorPosYAndSetupForPrevLine(float pos_y,float line_height)2293 static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height)
2294 {
2295     // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
2296     // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
2297     // The clipper should probably have a 4th step to display the last item in a regular manner.
2298     ImGuiContext& g = *GImGui;
2299     ImGuiWindow* window = g.CurrentWindow;
2300     float off_y = pos_y - window->DC.CursorPos.y;
2301     window->DC.CursorPos.y = pos_y;
2302     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y);
2303     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.
2304     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.
2305     if (ImGuiOldColumns* columns = window->DC.CurrentColumns)
2306         columns->LineMinY = window->DC.CursorPos.y;                         // Setting this so that cell Y position are set properly
2307     if (ImGuiTable* table = g.CurrentTable)
2308     {
2309         if (table->IsInsideRow)
2310             ImGui::TableEndRow(table);
2311         table->RowPosY2 = window->DC.CursorPos.y;
2312         const int row_increase = (int)((off_y / line_height) + 0.5f);
2313         //table->CurrentRow += row_increase; // Can't do without fixing TableEndRow()
2314         table->RowBgColorCounter += row_increase;
2315     }
2316 }
2317 
ImGuiListClipper()2318 ImGuiListClipper::ImGuiListClipper()
2319 {
2320     memset(this, 0, sizeof(*this));
2321     ItemsCount = -1;
2322 }
2323 
~ImGuiListClipper()2324 ImGuiListClipper::~ImGuiListClipper()
2325 {
2326     IM_ASSERT(ItemsCount == -1 && "Forgot to call End(), or to Step() until false?");
2327 }
2328 
2329 // Use case A: Begin() called from constructor with items_height<0, then called again from Step() in StepNo 1
2330 // Use case B: Begin() called from constructor with items_height>0
2331 // 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)2332 void ImGuiListClipper::Begin(int items_count, float items_height)
2333 {
2334     ImGuiContext& g = *GImGui;
2335     ImGuiWindow* window = g.CurrentWindow;
2336 
2337     if (ImGuiTable* table = g.CurrentTable)
2338         if (table->IsInsideRow)
2339             ImGui::TableEndRow(table);
2340 
2341     StartPosY = window->DC.CursorPos.y;
2342     ItemsHeight = items_height;
2343     ItemsCount = items_count;
2344     ItemsFrozen = 0;
2345     StepNo = 0;
2346     DisplayStart = -1;
2347     DisplayEnd = 0;
2348 }
2349 
End()2350 void ImGuiListClipper::End()
2351 {
2352     if (ItemsCount < 0) // Already ended
2353         return;
2354 
2355     // 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.
2356     if (ItemsCount < INT_MAX && DisplayStart >= 0)
2357         SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight);
2358     ItemsCount = -1;
2359     StepNo = 3;
2360 }
2361 
Step()2362 bool ImGuiListClipper::Step()
2363 {
2364     ImGuiContext& g = *GImGui;
2365     ImGuiWindow* window = g.CurrentWindow;
2366 
2367     ImGuiTable* table = g.CurrentTable;
2368     if (table && table->IsInsideRow)
2369         ImGui::TableEndRow(table);
2370 
2371     // No items
2372     if (ItemsCount == 0 || GetSkipItemForListClipping())
2373     {
2374         End();
2375         return false;
2376     }
2377 
2378     // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height)
2379     if (StepNo == 0)
2380     {
2381         // While we are in frozen row state, keep displaying items one by one, unclipped
2382         // FIXME: Could be stored as a table-agnostic state.
2383         if (table != NULL && !table->IsUnfrozenRows)
2384         {
2385             DisplayStart = ItemsFrozen;
2386             DisplayEnd = ItemsFrozen + 1;
2387             ItemsFrozen++;
2388             return true;
2389         }
2390 
2391         StartPosY = window->DC.CursorPos.y;
2392         if (ItemsHeight <= 0.0f)
2393         {
2394             // Submit the first item so we can measure its height (generally it is 0..1)
2395             DisplayStart = ItemsFrozen;
2396             DisplayEnd = ItemsFrozen + 1;
2397             StepNo = 1;
2398             return true;
2399         }
2400 
2401         // Already has item height (given by user in Begin): skip to calculating step
2402         DisplayStart = DisplayEnd;
2403         StepNo = 2;
2404     }
2405 
2406     // Step 1: the clipper infer height from first element
2407     if (StepNo == 1)
2408     {
2409         IM_ASSERT(ItemsHeight <= 0.0f);
2410         if (table)
2411         {
2412             const float pos_y1 = table->RowPosY1;   // Using this instead of StartPosY to handle clipper straddling the frozen row
2413             const float pos_y2 = table->RowPosY2;   // Using this instead of CursorPos.y to take account of tallest cell.
2414             ItemsHeight = pos_y2 - pos_y1;
2415             window->DC.CursorPos.y = pos_y2;
2416         }
2417         else
2418         {
2419             ItemsHeight = window->DC.CursorPos.y - StartPosY;
2420         }
2421         IM_ASSERT(ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!");
2422         StepNo = 2;
2423     }
2424 
2425     // Reached end of list
2426     if (DisplayEnd >= ItemsCount)
2427     {
2428         End();
2429         return false;
2430     }
2431 
2432     // Step 2: calculate the actual range of elements to display, and position the cursor before the first element
2433     if (StepNo == 2)
2434     {
2435         IM_ASSERT(ItemsHeight > 0.0f);
2436 
2437         int already_submitted = DisplayEnd;
2438         ImGui::CalcListClipping(ItemsCount - already_submitted, ItemsHeight, &DisplayStart, &DisplayEnd);
2439         DisplayStart += already_submitted;
2440         DisplayEnd += already_submitted;
2441 
2442         // Seek cursor
2443         if (DisplayStart > already_submitted)
2444             SetCursorPosYAndSetupForPrevLine(StartPosY + (DisplayStart - ItemsFrozen) * ItemsHeight, ItemsHeight);
2445 
2446         StepNo = 3;
2447         return true;
2448     }
2449 
2450     // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd),
2451     // Advance the cursor to the end of the list and then returns 'false' to end the loop.
2452     if (StepNo == 3)
2453     {
2454         // Seek cursor
2455         if (ItemsCount < INT_MAX)
2456             SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight); // advance cursor
2457         ItemsCount = -1;
2458         return false;
2459     }
2460 
2461     IM_ASSERT(0);
2462     return false;
2463 }
2464 
2465 //-----------------------------------------------------------------------------
2466 // [SECTION] STYLING
2467 //-----------------------------------------------------------------------------
2468 
GetStyle()2469 ImGuiStyle& ImGui::GetStyle()
2470 {
2471     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
2472     return GImGui->Style;
2473 }
2474 
GetColorU32(ImGuiCol idx,float alpha_mul)2475 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
2476 {
2477     ImGuiStyle& style = GImGui->Style;
2478     ImVec4 c = style.Colors[idx];
2479     c.w *= style.Alpha * alpha_mul;
2480     return ColorConvertFloat4ToU32(c);
2481 }
2482 
GetColorU32(const ImVec4 & col)2483 ImU32 ImGui::GetColorU32(const ImVec4& col)
2484 {
2485     ImGuiStyle& style = GImGui->Style;
2486     ImVec4 c = col;
2487     c.w *= style.Alpha;
2488     return ColorConvertFloat4ToU32(c);
2489 }
2490 
GetStyleColorVec4(ImGuiCol idx)2491 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
2492 {
2493     ImGuiStyle& style = GImGui->Style;
2494     return style.Colors[idx];
2495 }
2496 
GetColorU32(ImU32 col)2497 ImU32 ImGui::GetColorU32(ImU32 col)
2498 {
2499     ImGuiStyle& style = GImGui->Style;
2500     if (style.Alpha >= 1.0f)
2501         return col;
2502     ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
2503     a = (ImU32)(a * style.Alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
2504     return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
2505 }
2506 
2507 // 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)2508 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
2509 {
2510     ImGuiContext& g = *GImGui;
2511     ImGuiColorMod backup;
2512     backup.Col = idx;
2513     backup.BackupValue = g.Style.Colors[idx];
2514     g.ColorStack.push_back(backup);
2515     g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
2516 }
2517 
PushStyleColor(ImGuiCol idx,const ImVec4 & col)2518 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
2519 {
2520     ImGuiContext& g = *GImGui;
2521     ImGuiColorMod backup;
2522     backup.Col = idx;
2523     backup.BackupValue = g.Style.Colors[idx];
2524     g.ColorStack.push_back(backup);
2525     g.Style.Colors[idx] = col;
2526 }
2527 
PopStyleColor(int count)2528 void ImGui::PopStyleColor(int count)
2529 {
2530     ImGuiContext& g = *GImGui;
2531     while (count > 0)
2532     {
2533         ImGuiColorMod& backup = g.ColorStack.back();
2534         g.Style.Colors[backup.Col] = backup.BackupValue;
2535         g.ColorStack.pop_back();
2536         count--;
2537     }
2538 }
2539 
2540 struct ImGuiStyleVarInfo
2541 {
2542     ImGuiDataType   Type;
2543     ImU32           Count;
2544     ImU32           Offset;
GetVarPtrImGuiStyleVarInfo2545     void*           GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
2546 };
2547 
2548 static const ImGuiCol GWindowDockStyleColors[ImGuiWindowDockStyleCol_COUNT] =
2549 {
2550     ImGuiCol_Text, ImGuiCol_Tab, ImGuiCol_TabHovered, ImGuiCol_TabActive, ImGuiCol_TabUnfocused, ImGuiCol_TabUnfocusedActive
2551 };
2552 
2553 static const ImGuiStyleVarInfo GStyleVarInfo[] =
2554 {
2555     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) },               // ImGuiStyleVar_Alpha
2556     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) },       // ImGuiStyleVar_WindowPadding
2557     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) },      // ImGuiStyleVar_WindowRounding
2558     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) },    // ImGuiStyleVar_WindowBorderSize
2559     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) },       // ImGuiStyleVar_WindowMinSize
2560     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) },    // ImGuiStyleVar_WindowTitleAlign
2561     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) },       // ImGuiStyleVar_ChildRounding
2562     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) },     // ImGuiStyleVar_ChildBorderSize
2563     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) },       // ImGuiStyleVar_PopupRounding
2564     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) },     // ImGuiStyleVar_PopupBorderSize
2565     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) },        // ImGuiStyleVar_FramePadding
2566     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) },       // ImGuiStyleVar_FrameRounding
2567     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) },     // ImGuiStyleVar_FrameBorderSize
2568     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) },         // ImGuiStyleVar_ItemSpacing
2569     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) },    // ImGuiStyleVar_ItemInnerSpacing
2570     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) },       // ImGuiStyleVar_IndentSpacing
2571     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, CellPadding) },         // ImGuiStyleVar_CellPadding
2572     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) },       // ImGuiStyleVar_ScrollbarSize
2573     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) },   // ImGuiStyleVar_ScrollbarRounding
2574     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) },         // ImGuiStyleVar_GrabMinSize
2575     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) },        // ImGuiStyleVar_GrabRounding
2576     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) },         // ImGuiStyleVar_TabRounding
2577     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) },     // ImGuiStyleVar_ButtonTextAlign
2578     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
2579 };
2580 
GetStyleVarInfo(ImGuiStyleVar idx)2581 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
2582 {
2583     IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
2584     IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
2585     return &GStyleVarInfo[idx];
2586 }
2587 
PushStyleVar(ImGuiStyleVar idx,float val)2588 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
2589 {
2590     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
2591     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
2592     {
2593         ImGuiContext& g = *GImGui;
2594         float* pvar = (float*)var_info->GetVarPtr(&g.Style);
2595         g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
2596         *pvar = val;
2597         return;
2598     }
2599     IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!");
2600 }
2601 
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)2602 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
2603 {
2604     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
2605     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
2606     {
2607         ImGuiContext& g = *GImGui;
2608         ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
2609         g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
2610         *pvar = val;
2611         return;
2612     }
2613     IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!");
2614 }
2615 
PopStyleVar(int count)2616 void ImGui::PopStyleVar(int count)
2617 {
2618     ImGuiContext& g = *GImGui;
2619     while (count > 0)
2620     {
2621         // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
2622         ImGuiStyleMod& backup = g.StyleVarStack.back();
2623         const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
2624         void* data = info->GetVarPtr(&g.Style);
2625         if (info->Type == ImGuiDataType_Float && info->Count == 1)      { ((float*)data)[0] = backup.BackupFloat[0]; }
2626         else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
2627         g.StyleVarStack.pop_back();
2628         count--;
2629     }
2630 }
2631 
GetStyleColorName(ImGuiCol idx)2632 const char* ImGui::GetStyleColorName(ImGuiCol idx)
2633 {
2634     // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
2635     switch (idx)
2636     {
2637     case ImGuiCol_Text: return "Text";
2638     case ImGuiCol_TextDisabled: return "TextDisabled";
2639     case ImGuiCol_WindowBg: return "WindowBg";
2640     case ImGuiCol_ChildBg: return "ChildBg";
2641     case ImGuiCol_PopupBg: return "PopupBg";
2642     case ImGuiCol_Border: return "Border";
2643     case ImGuiCol_BorderShadow: return "BorderShadow";
2644     case ImGuiCol_FrameBg: return "FrameBg";
2645     case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
2646     case ImGuiCol_FrameBgActive: return "FrameBgActive";
2647     case ImGuiCol_TitleBg: return "TitleBg";
2648     case ImGuiCol_TitleBgActive: return "TitleBgActive";
2649     case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
2650     case ImGuiCol_MenuBarBg: return "MenuBarBg";
2651     case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
2652     case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
2653     case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
2654     case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
2655     case ImGuiCol_CheckMark: return "CheckMark";
2656     case ImGuiCol_SliderGrab: return "SliderGrab";
2657     case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
2658     case ImGuiCol_Button: return "Button";
2659     case ImGuiCol_ButtonHovered: return "ButtonHovered";
2660     case ImGuiCol_ButtonActive: return "ButtonActive";
2661     case ImGuiCol_Header: return "Header";
2662     case ImGuiCol_HeaderHovered: return "HeaderHovered";
2663     case ImGuiCol_HeaderActive: return "HeaderActive";
2664     case ImGuiCol_Separator: return "Separator";
2665     case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
2666     case ImGuiCol_SeparatorActive: return "SeparatorActive";
2667     case ImGuiCol_ResizeGrip: return "ResizeGrip";
2668     case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
2669     case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
2670     case ImGuiCol_Tab: return "Tab";
2671     case ImGuiCol_TabHovered: return "TabHovered";
2672     case ImGuiCol_TabActive: return "TabActive";
2673     case ImGuiCol_TabUnfocused: return "TabUnfocused";
2674     case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive";
2675     case ImGuiCol_DockingPreview: return "DockingPreview";
2676     case ImGuiCol_DockingEmptyBg: return "DockingEmptyBg";
2677     case ImGuiCol_PlotLines: return "PlotLines";
2678     case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
2679     case ImGuiCol_PlotHistogram: return "PlotHistogram";
2680     case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
2681     case ImGuiCol_TableHeaderBg: return "TableHeaderBg";
2682     case ImGuiCol_TableBorderStrong: return "TableBorderStrong";
2683     case ImGuiCol_TableBorderLight: return "TableBorderLight";
2684     case ImGuiCol_TableRowBg: return "TableRowBg";
2685     case ImGuiCol_TableRowBgAlt: return "TableRowBgAlt";
2686     case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
2687     case ImGuiCol_DragDropTarget: return "DragDropTarget";
2688     case ImGuiCol_NavHighlight: return "NavHighlight";
2689     case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
2690     case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
2691     case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
2692     }
2693     IM_ASSERT(0);
2694     return "Unknown";
2695 }
2696 
2697 
2698 //-----------------------------------------------------------------------------
2699 // [SECTION] RENDER HELPERS
2700 // Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change,
2701 // we need a nicer separation between low-level functions and high-level functions relying on the ImGui context.
2702 // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context.
2703 //-----------------------------------------------------------------------------
2704 
FindRenderedTextEnd(const char * text,const char * text_end)2705 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2706 {
2707     const char* text_display_end = text;
2708     if (!text_end)
2709         text_end = (const char*)-1;
2710 
2711     while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2712         text_display_end++;
2713     return text_display_end;
2714 }
2715 
2716 // Internal ImGui functions to render text
2717 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)2718 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2719 {
2720     ImGuiContext& g = *GImGui;
2721     ImGuiWindow* window = g.CurrentWindow;
2722 
2723     // Hide anything after a '##' string
2724     const char* text_display_end;
2725     if (hide_text_after_hash)
2726     {
2727         text_display_end = FindRenderedTextEnd(text, text_end);
2728     }
2729     else
2730     {
2731         if (!text_end)
2732             text_end = text + strlen(text); // FIXME-OPT
2733         text_display_end = text_end;
2734     }
2735 
2736     if (text != text_display_end)
2737     {
2738         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2739         if (g.LogEnabled)
2740             LogRenderedText(&pos, text, text_display_end);
2741     }
2742 }
2743 
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)2744 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2745 {
2746     ImGuiContext& g = *GImGui;
2747     ImGuiWindow* window = g.CurrentWindow;
2748 
2749     if (!text_end)
2750         text_end = text + strlen(text); // FIXME-OPT
2751 
2752     if (text != text_end)
2753     {
2754         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2755         if (g.LogEnabled)
2756             LogRenderedText(&pos, text, text_end);
2757     }
2758 }
2759 
2760 // Default clip_rect uses (pos_min,pos_max)
2761 // 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)2762 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)
2763 {
2764     // Perform CPU side clipping for single clipped element to avoid using scissor state
2765     ImVec2 pos = pos_min;
2766     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
2767 
2768     const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
2769     const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
2770     bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2771     if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
2772         need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2773 
2774     // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
2775     if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
2776     if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
2777 
2778     // Render
2779     if (need_clipping)
2780     {
2781         ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2782         draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2783     }
2784     else
2785     {
2786         draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2787     }
2788 }
2789 
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)2790 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)
2791 {
2792     // Hide anything after a '##' string
2793     const char* text_display_end = FindRenderedTextEnd(text, text_end);
2794     const int text_len = (int)(text_display_end - text);
2795     if (text_len == 0)
2796         return;
2797 
2798     ImGuiContext& g = *GImGui;
2799     ImGuiWindow* window = g.CurrentWindow;
2800     RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
2801     if (g.LogEnabled)
2802         LogRenderedText(&pos_min, text, text_display_end);
2803 }
2804 
2805 
2806 // Another overly complex function until we reorganize everything into a nice all-in-one helper.
2807 // 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.
2808 // 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)2809 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)
2810 {
2811     ImGuiContext& g = *GImGui;
2812     if (text_end_full == NULL)
2813         text_end_full = FindRenderedTextEnd(text);
2814     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);
2815 
2816     //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));
2817     //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));
2818     //draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255));
2819     // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
2820     if (text_size.x > pos_max.x - pos_min.x)
2821     {
2822         // Hello wo...
2823         // |       |   |
2824         // min   max   ellipsis_max
2825         //          <-> this is generally some padding value
2826 
2827         const ImFont* font = draw_list->_Data->Font;
2828         const float font_size = draw_list->_Data->FontSize;
2829         const char* text_end_ellipsis = NULL;
2830 
2831         ImWchar ellipsis_char = font->EllipsisChar;
2832         int ellipsis_char_count = 1;
2833         if (ellipsis_char == (ImWchar)-1)
2834         {
2835             ellipsis_char = font->DotChar;
2836             ellipsis_char_count = 3;
2837         }
2838         const ImFontGlyph* glyph = font->FindGlyph(ellipsis_char);
2839 
2840         float ellipsis_glyph_width = glyph->X1;                 // Width of the glyph with no padding on either side
2841         float ellipsis_total_width = ellipsis_glyph_width;      // Full width of entire ellipsis
2842 
2843         if (ellipsis_char_count > 1)
2844         {
2845             // Full ellipsis size without free spacing after it.
2846             const float spacing_between_dots = 1.0f * (draw_list->_Data->FontSize / font->FontSize);
2847             ellipsis_glyph_width = glyph->X1 - glyph->X0 + spacing_between_dots;
2848             ellipsis_total_width = ellipsis_glyph_width * (float)ellipsis_char_count - spacing_between_dots;
2849         }
2850 
2851         // We can now claim the space between pos_max.x and ellipsis_max.x
2852         const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_total_width) - pos_min.x, 1.0f);
2853         float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
2854         if (text == text_end_ellipsis && text_end_ellipsis < text_end_full)
2855         {
2856             // Always display at least 1 character if there's no room for character + ellipsis
2857             text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full);
2858             text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x;
2859         }
2860         while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1]))
2861         {
2862             // 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)
2863             text_end_ellipsis--;
2864             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
2865         }
2866 
2867         // Render text, render ellipsis
2868         RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
2869         float ellipsis_x = pos_min.x + text_size_clipped_x;
2870         if (ellipsis_x + ellipsis_total_width <= ellipsis_max_x)
2871             for (int i = 0; i < ellipsis_char_count; i++)
2872             {
2873                 font->RenderChar(draw_list, font_size, ImVec2(ellipsis_x, pos_min.y), GetColorU32(ImGuiCol_Text), ellipsis_char);
2874                 ellipsis_x += ellipsis_glyph_width;
2875             }
2876     }
2877     else
2878     {
2879         RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));
2880     }
2881 
2882     if (g.LogEnabled)
2883         LogRenderedText(&pos_min, text, text_end_full);
2884 }
2885 
2886 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)2887 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2888 {
2889     ImGuiContext& g = *GImGui;
2890     ImGuiWindow* window = g.CurrentWindow;
2891     window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2892     const float border_size = g.Style.FrameBorderSize;
2893     if (border && border_size > 0.0f)
2894     {
2895         window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);
2896         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
2897     }
2898 }
2899 
RenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding)2900 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
2901 {
2902     ImGuiContext& g = *GImGui;
2903     ImGuiWindow* window = g.CurrentWindow;
2904     const float border_size = g.Style.FrameBorderSize;
2905     if (border_size > 0.0f)
2906     {
2907         window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);
2908         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
2909     }
2910 }
2911 
RenderNavHighlight(const ImRect & bb,ImGuiID id,ImGuiNavHighlightFlags flags)2912 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
2913 {
2914     ImGuiContext& g = *GImGui;
2915     if (id != g.NavId)
2916         return;
2917     if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
2918         return;
2919     ImGuiWindow* window = g.CurrentWindow;
2920     if (window->DC.NavHideHighlightOneFrame)
2921         return;
2922 
2923     float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
2924     ImRect display_rect = bb;
2925     display_rect.ClipWith(window->ClipRect);
2926     if (flags & ImGuiNavHighlightFlags_TypeDefault)
2927     {
2928         const float THICKNESS = 2.0f;
2929         const float DISTANCE = 3.0f + THICKNESS * 0.5f;
2930         display_rect.Expand(ImVec2(DISTANCE, DISTANCE));
2931         bool fully_visible = window->ClipRect.Contains(display_rect);
2932         if (!fully_visible)
2933             window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
2934         window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f), display_rect.Max - ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f), GetColorU32(ImGuiCol_NavHighlight), rounding, 0, THICKNESS);
2935         if (!fully_visible)
2936             window->DrawList->PopClipRect();
2937     }
2938     if (flags & ImGuiNavHighlightFlags_TypeThin)
2939     {
2940         window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, 0, 1.0f);
2941     }
2942 }
2943 
2944 //-----------------------------------------------------------------------------
2945 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
2946 //-----------------------------------------------------------------------------
2947 
2948 // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
ImGuiWindow(ImGuiContext * context,const char * name)2949 ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) : DrawListInst(NULL)
2950 {
2951     memset(this, 0, sizeof(*this));
2952     Name = ImStrdup(name);
2953     NameBufLen = (int)strlen(name) + 1;
2954     ID = ImHashStr(name);
2955     IDStack.push_back(ID);
2956     ViewportAllowPlatformMonitorExtend = -1;
2957     ViewportPos = ImVec2(FLT_MAX, FLT_MAX);
2958     MoveId = GetID("#MOVE");
2959     ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
2960     ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
2961     AutoFitFramesX = AutoFitFramesY = -1;
2962     AutoPosLastDirection = ImGuiDir_None;
2963     SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = SetWindowDockAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
2964     SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
2965     LastFrameActive = -1;
2966     LastFrameJustFocused = -1;
2967     LastTimeActive = -1.0f;
2968     FontWindowScale = FontDpiScale = 1.0f;
2969     SettingsOffset = -1;
2970     DockOrder = -1;
2971     DrawList = &DrawListInst;
2972     DrawList->_Data = &context->DrawListSharedData;
2973     DrawList->_OwnerName = Name;
2974 }
2975 
~ImGuiWindow()2976 ImGuiWindow::~ImGuiWindow()
2977 {
2978     IM_ASSERT(DrawList == &DrawListInst);
2979     IM_DELETE(Name);
2980     ColumnsStorage.clear_destruct();
2981 }
2982 
GetID(const char * str,const char * str_end)2983 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
2984 {
2985     ImGuiID seed = IDStack.back();
2986     ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2987     ImGui::KeepAliveID(id);
2988 #ifdef IMGUI_ENABLE_TEST_ENGINE
2989     ImGuiContext& g = *GImGui;
2990     IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
2991 #endif
2992     return id;
2993 }
2994 
GetID(const void * ptr)2995 ImGuiID ImGuiWindow::GetID(const void* ptr)
2996 {
2997     ImGuiID seed = IDStack.back();
2998     ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
2999     ImGui::KeepAliveID(id);
3000 #ifdef IMGUI_ENABLE_TEST_ENGINE
3001     ImGuiContext& g = *GImGui;
3002     IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr);
3003 #endif
3004     return id;
3005 }
3006 
GetID(int n)3007 ImGuiID ImGuiWindow::GetID(int n)
3008 {
3009     ImGuiID seed = IDStack.back();
3010     ImGuiID id = ImHashData(&n, sizeof(n), seed);
3011     ImGui::KeepAliveID(id);
3012 #ifdef IMGUI_ENABLE_TEST_ENGINE
3013     ImGuiContext& g = *GImGui;
3014     IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n);
3015 #endif
3016     return id;
3017 }
3018 
GetIDNoKeepAlive(const char * str,const char * str_end)3019 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
3020 {
3021     ImGuiID seed = IDStack.back();
3022     ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
3023 #ifdef IMGUI_ENABLE_TEST_ENGINE
3024     ImGuiContext& g = *GImGui;
3025     IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
3026 #endif
3027     return id;
3028 }
3029 
GetIDNoKeepAlive(const void * ptr)3030 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
3031 {
3032     ImGuiID seed = IDStack.back();
3033     ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
3034 #ifdef IMGUI_ENABLE_TEST_ENGINE
3035     ImGuiContext& g = *GImGui;
3036     IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr);
3037 #endif
3038     return id;
3039 }
3040 
GetIDNoKeepAlive(int n)3041 ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n)
3042 {
3043     ImGuiID seed = IDStack.back();
3044     ImGuiID id = ImHashData(&n, sizeof(n), seed);
3045 #ifdef IMGUI_ENABLE_TEST_ENGINE
3046     ImGuiContext& g = *GImGui;
3047     IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n);
3048 #endif
3049     return id;
3050 }
3051 
3052 // This is only used in rare/specific situations to manufacture an ID out of nowhere.
GetIDFromRectangle(const ImRect & r_abs)3053 ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
3054 {
3055     ImGuiID seed = IDStack.back();
3056     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) };
3057     ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
3058     ImGui::KeepAliveID(id);
3059     return id;
3060 }
3061 
SetCurrentWindow(ImGuiWindow * window)3062 static void SetCurrentWindow(ImGuiWindow* window)
3063 {
3064     ImGuiContext& g = *GImGui;
3065     g.CurrentWindow = window;
3066     g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL;
3067     if (window)
3068         g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
3069 }
3070 
GcCompactTransientMiscBuffers()3071 void ImGui::GcCompactTransientMiscBuffers()
3072 {
3073     ImGuiContext& g = *GImGui;
3074     g.ItemFlagsStack.clear();
3075     g.GroupStack.clear();
3076     TableGcCompactSettings();
3077 }
3078 
3079 // Free up/compact internal window buffers, we can use this when a window becomes unused.
3080 // Not freed:
3081 // - ImGuiWindow, ImGuiWindowSettings, Name, StateStorage, ColumnsStorage (may hold useful data)
3082 // This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.
GcCompactTransientWindowBuffers(ImGuiWindow * window)3083 void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window)
3084 {
3085     window->MemoryCompacted = true;
3086     window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;
3087     window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;
3088     window->IDStack.clear();
3089     window->DrawList->_ClearFreeMemory();
3090     window->DC.ChildWindows.clear();
3091     window->DC.ItemWidthStack.clear();
3092     window->DC.TextWrapPosStack.clear();
3093 }
3094 
GcAwakeTransientWindowBuffers(ImGuiWindow * window)3095 void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window)
3096 {
3097     // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.
3098     // The other buffers tends to amortize much faster.
3099     window->MemoryCompacted = false;
3100     window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity);
3101     window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity);
3102     window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0;
3103 }
3104 
SetActiveID(ImGuiID id,ImGuiWindow * window)3105 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
3106 {
3107     ImGuiContext& g = *GImGui;
3108     g.ActiveIdIsJustActivated = (g.ActiveId != id);
3109     if (g.ActiveIdIsJustActivated)
3110     {
3111         g.ActiveIdTimer = 0.0f;
3112         g.ActiveIdHasBeenPressedBefore = false;
3113         g.ActiveIdHasBeenEditedBefore = false;
3114         g.ActiveIdMouseButton = -1;
3115         if (id != 0)
3116         {
3117             g.LastActiveId = id;
3118             g.LastActiveIdTimer = 0.0f;
3119         }
3120     }
3121     g.ActiveId = id;
3122     g.ActiveIdAllowOverlap = false;
3123     g.ActiveIdNoClearOnFocusLoss = false;
3124     g.ActiveIdWindow = window;
3125     g.ActiveIdHasBeenEditedThisFrame = false;
3126     if (id)
3127     {
3128         g.ActiveIdIsAlive = id;
3129         g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
3130     }
3131 
3132     // Clear declaration of inputs claimed by the widget
3133     // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
3134     g.ActiveIdUsingMouseWheel = false;
3135     g.ActiveIdUsingNavDirMask = 0x00;
3136     g.ActiveIdUsingNavInputMask = 0x00;
3137     g.ActiveIdUsingKeyInputMask = 0x00;
3138 }
3139 
ClearActiveID()3140 void ImGui::ClearActiveID()
3141 {
3142     SetActiveID(0, NULL); // g.ActiveId = 0;
3143 }
3144 
SetHoveredID(ImGuiID id)3145 void ImGui::SetHoveredID(ImGuiID id)
3146 {
3147     ImGuiContext& g = *GImGui;
3148     g.HoveredId = id;
3149     g.HoveredIdAllowOverlap = false;
3150     g.HoveredIdUsingMouseWheel = false;
3151     if (id != 0 && g.HoveredIdPreviousFrame != id)
3152         g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
3153 }
3154 
GetHoveredID()3155 ImGuiID ImGui::GetHoveredID()
3156 {
3157     ImGuiContext& g = *GImGui;
3158     return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
3159 }
3160 
KeepAliveID(ImGuiID id)3161 void ImGui::KeepAliveID(ImGuiID id)
3162 {
3163     ImGuiContext& g = *GImGui;
3164     if (g.ActiveId == id)
3165         g.ActiveIdIsAlive = id;
3166     if (g.ActiveIdPreviousFrame == id)
3167         g.ActiveIdPreviousFrameIsAlive = true;
3168 }
3169 
MarkItemEdited(ImGuiID id)3170 void ImGui::MarkItemEdited(ImGuiID id)
3171 {
3172     // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
3173     // 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.
3174     ImGuiContext& g = *GImGui;
3175     IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive);
3176     IM_UNUSED(id); // Avoid unused variable warnings when asserts are compiled out.
3177     //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
3178     g.ActiveIdHasBeenEditedThisFrame = true;
3179     g.ActiveIdHasBeenEditedBefore = true;
3180     g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
3181 }
3182 
IsWindowContentHoverable(ImGuiWindow * window,ImGuiHoveredFlags flags)3183 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
3184 {
3185     // An active popup disable hovering on other windows (apart from its own children)
3186     // FIXME-OPT: This could be cached/stored within the window.
3187     ImGuiContext& g = *GImGui;
3188     if (g.NavWindow)
3189         if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindowDockTree)
3190             if (focused_root_window->WasActive && focused_root_window != window->RootWindowDockTree)
3191             {
3192                 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
3193                 // NB: The order of those two tests is important because Modal windows are also Popups.
3194                 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
3195                     return false;
3196                 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
3197                     return false;
3198             }
3199 
3200     // Filter by viewport
3201     if (window->Viewport != g.MouseViewport)
3202         if (g.MovingWindow == NULL || window->RootWindowDockTree != g.MovingWindow->RootWindowDockTree)
3203             return false;
3204 
3205     return true;
3206 }
3207 
3208 // This is roughly matching the behavior of internal-facing ItemHoverable()
3209 // - 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()
3210 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
IsItemHovered(ImGuiHoveredFlags flags)3211 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
3212 {
3213     ImGuiContext& g = *GImGui;
3214     ImGuiWindow* window = g.CurrentWindow;
3215     if (g.NavDisableMouseHover && !g.NavDisableHighlight)
3216         return IsItemFocused();
3217 
3218     // Test for bounding box overlap, as updated as ItemAdd()
3219     ImGuiItemStatusFlags status_flags = window->DC.LastItemStatusFlags;
3220     if (!(status_flags & ImGuiItemStatusFlags_HoveredRect))
3221         return false;
3222     IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0);   // Flags not supported by this function
3223 
3224     // Test if we are hovering the right window (our window could be behind another window)
3225     // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851)
3226     // [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
3227     // to use IsItemHovered() after EndChild() itself. Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was
3228     // the test that has been running for a long while.
3229     if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0)
3230         if ((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0)
3231             return false;
3232 
3233     // Test if another item is active (e.g. being dragged)
3234     if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0)
3235         if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
3236             return false;
3237 
3238     // Test if interactions on this window are blocked by an active popup or modal.
3239     // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
3240     if (!IsWindowContentHoverable(window, flags))
3241         return false;
3242 
3243     // Test if the item is disabled
3244     if ((g.CurrentItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
3245         return false;
3246 
3247     // Special handling for calling after Begin() which represent the title bar or tab.
3248     // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
3249     if ((window->DC.LastItemId == window->ID || window->DC.LastItemId == window->MoveId) && window->WriteAccessed)
3250         return false;
3251     return true;
3252 }
3253 
3254 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
ItemHoverable(const ImRect & bb,ImGuiID id)3255 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
3256 {
3257     ImGuiContext& g = *GImGui;
3258     if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
3259         return false;
3260 
3261     ImGuiWindow* window = g.CurrentWindow;
3262     if (g.HoveredWindow != window)
3263         return false;
3264     if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
3265         return false;
3266     if (!IsMouseHoveringRect(bb.Min, bb.Max))
3267         return false;
3268     if (g.NavDisableMouseHover)
3269         return false;
3270     if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
3271     {
3272         g.HoveredIdDisabled = true;
3273         return false;
3274     }
3275 
3276     // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level
3277     // hover test in widgets code. We could also decide to split this function is two.
3278     if (id != 0)
3279         SetHoveredID(id);
3280 
3281     // When disabled we'll return false but still set HoveredId
3282     if (g.CurrentItemFlags & ImGuiItemFlags_Disabled)
3283     {
3284         g.HoveredIdDisabled = true;
3285         return false;
3286     }
3287 
3288     if (id != 0)
3289     {
3290         // [DEBUG] Item Picker tool!
3291         // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making
3292         // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered
3293         // items if we perform the test in ItemAdd(), but that would incur a small runtime cost.
3294         // #define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX in imconfig.h if you want this check to also be performed in ItemAdd().
3295         if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
3296             GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
3297         if (g.DebugItemPickerBreakId == id)
3298             IM_DEBUG_BREAK();
3299     }
3300 
3301     return true;
3302 }
3303 
IsClippedEx(const ImRect & bb,ImGuiID id,bool clip_even_when_logged)3304 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
3305 {
3306     ImGuiContext& g = *GImGui;
3307     ImGuiWindow* window = g.CurrentWindow;
3308     if (!bb.Overlaps(window->ClipRect))
3309         if (id == 0 || (id != g.ActiveId && id != g.NavId))
3310             if (clip_even_when_logged || !g.LogEnabled)
3311                 return true;
3312     return false;
3313 }
3314 
3315 // This is also inlined in ItemAdd()
3316 // 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)3317 void ImGui::SetLastItemData(ImGuiWindow* window, ImGuiID item_id, ImGuiItemStatusFlags item_flags, const ImRect& item_rect)
3318 {
3319     window->DC.LastItemId = item_id;
3320     window->DC.LastItemStatusFlags = item_flags;
3321     window->DC.LastItemRect = item_rect;
3322 }
3323 
3324 // Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out.
ItemFocusable(ImGuiWindow * window,ImGuiID id)3325 void ImGui::ItemFocusable(ImGuiWindow* window, ImGuiID id)
3326 {
3327     ImGuiContext& g = *GImGui;
3328     IM_ASSERT(id != 0 && id == window->DC.LastItemId);
3329 
3330     // Increment counters
3331     // FIXME: ImGuiItemFlags_Disabled should disable more.
3332     const bool is_tab_stop = (g.CurrentItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0;
3333     window->DC.FocusCounterRegular++;
3334     if (is_tab_stop)
3335     {
3336         window->DC.FocusCounterTabStop++;
3337         if (g.NavId == id)
3338             g.NavIdTabCounter = window->DC.FocusCounterTabStop;
3339     }
3340 
3341     // Process TAB/Shift-TAB to tab *OUT* of the currently focused item.
3342     // (Note that we can always TAB out of a widget that doesn't allow tabbing in)
3343     if (g.ActiveId == id && g.TabFocusPressed && !IsActiveIdUsingKey(ImGuiKey_Tab) && g.TabFocusRequestNextWindow == NULL)
3344     {
3345         g.TabFocusRequestNextWindow = window;
3346         g.TabFocusRequestNextCounterTabStop = window->DC.FocusCounterTabStop + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items.
3347     }
3348 
3349     // Handle focus requests
3350     if (g.TabFocusRequestCurrWindow == window)
3351     {
3352         if (window->DC.FocusCounterRegular == g.TabFocusRequestCurrCounterRegular)
3353         {
3354             window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_FocusedByCode;
3355             return;
3356         }
3357         if (is_tab_stop && window->DC.FocusCounterTabStop == g.TabFocusRequestCurrCounterTabStop)
3358         {
3359             g.NavJustTabbedId = id;
3360             window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_FocusedByTabbing;
3361             return;
3362         }
3363 
3364         // If another item is about to be focused, we clear our own active id
3365         if (g.ActiveId == id)
3366             ClearActiveID();
3367     }
3368 }
3369 
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)3370 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
3371 {
3372     if (wrap_pos_x < 0.0f)
3373         return 0.0f;
3374 
3375     ImGuiContext& g = *GImGui;
3376     ImGuiWindow* window = g.CurrentWindow;
3377     if (wrap_pos_x == 0.0f)
3378     {
3379         // We could decide to setup a default wrapping max point for auto-resizing windows,
3380         // or have auto-wrap (with unspecified wrapping pos) behave as a ContentSize extending function?
3381         //if (window->Hidden && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize))
3382         //    wrap_pos_x = ImMax(window->WorkRect.Min.x + g.FontSize * 10.0f, window->WorkRect.Max.x);
3383         //else
3384         wrap_pos_x = window->WorkRect.Max.x;
3385     }
3386     else if (wrap_pos_x > 0.0f)
3387     {
3388         wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
3389     }
3390 
3391     return ImMax(wrap_pos_x - pos.x, 1.0f);
3392 }
3393 
3394 // IM_ALLOC() == ImGui::MemAlloc()
MemAlloc(size_t size)3395 void* ImGui::MemAlloc(size_t size)
3396 {
3397     if (ImGuiContext* ctx = GImGui)
3398         ctx->IO.MetricsActiveAllocations++;
3399     return (*GImAllocatorAllocFunc)(size, GImAllocatorUserData);
3400 }
3401 
3402 // IM_FREE() == ImGui::MemFree()
MemFree(void * ptr)3403 void ImGui::MemFree(void* ptr)
3404 {
3405     if (ptr)
3406         if (ImGuiContext* ctx = GImGui)
3407             ctx->IO.MetricsActiveAllocations--;
3408     return (*GImAllocatorFreeFunc)(ptr, GImAllocatorUserData);
3409 }
3410 
GetClipboardText()3411 const char* ImGui::GetClipboardText()
3412 {
3413     ImGuiContext& g = *GImGui;
3414     return g.IO.GetClipboardTextFn ? g.IO.GetClipboardTextFn(g.IO.ClipboardUserData) : "";
3415 }
3416 
SetClipboardText(const char * text)3417 void ImGui::SetClipboardText(const char* text)
3418 {
3419     ImGuiContext& g = *GImGui;
3420     if (g.IO.SetClipboardTextFn)
3421         g.IO.SetClipboardTextFn(g.IO.ClipboardUserData, text);
3422 }
3423 
GetVersion()3424 const char* ImGui::GetVersion()
3425 {
3426     return IMGUI_VERSION;
3427 }
3428 
3429 // Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself
3430 // 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()3431 ImGuiContext* ImGui::GetCurrentContext()
3432 {
3433     return GImGui;
3434 }
3435 
SetCurrentContext(ImGuiContext * ctx)3436 void ImGui::SetCurrentContext(ImGuiContext* ctx)
3437 {
3438 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
3439     IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
3440 #else
3441     GImGui = ctx;
3442 #endif
3443 }
3444 
SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func,ImGuiMemFreeFunc free_func,void * user_data)3445 void ImGui::SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data)
3446 {
3447     GImAllocatorAllocFunc = alloc_func;
3448     GImAllocatorFreeFunc = free_func;
3449     GImAllocatorUserData = user_data;
3450 }
3451 
3452 // This is provided to facilitate copying allocators from one static/DLL boundary to another (e.g. retrieve default allocator of your executable address space)
GetAllocatorFunctions(ImGuiMemAllocFunc * p_alloc_func,ImGuiMemFreeFunc * p_free_func,void ** p_user_data)3453 void ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data)
3454 {
3455     *p_alloc_func = GImAllocatorAllocFunc;
3456     *p_free_func = GImAllocatorFreeFunc;
3457     *p_user_data = GImAllocatorUserData;
3458 }
3459 
CreateContext(ImFontAtlas * shared_font_atlas)3460 ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
3461 {
3462     ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
3463     if (GImGui == NULL)
3464         SetCurrentContext(ctx);
3465     Initialize(ctx);
3466     return ctx;
3467 }
3468 
DestroyContext(ImGuiContext * ctx)3469 void ImGui::DestroyContext(ImGuiContext* ctx)
3470 {
3471     if (ctx == NULL)
3472         ctx = GImGui;
3473     Shutdown(ctx);
3474     if (GImGui == ctx)
3475         SetCurrentContext(NULL);
3476     IM_DELETE(ctx);
3477 }
3478 
3479 // No specific ordering/dependency support, will see as needed
AddContextHook(ImGuiContext * ctx,const ImGuiContextHook * hook)3480 ImGuiID ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook)
3481 {
3482     ImGuiContext& g = *ctx;
3483     IM_ASSERT(hook->Callback != NULL && hook->HookId == 0 && hook->Type != ImGuiContextHookType_PendingRemoval_);
3484     g.Hooks.push_back(*hook);
3485     g.Hooks.back().HookId = ++g.HookIdNext;
3486     return g.HookIdNext;
3487 }
3488 
3489 // Deferred removal, avoiding issue with changing vector while iterating it
RemoveContextHook(ImGuiContext * ctx,ImGuiID hook_id)3490 void ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id)
3491 {
3492     ImGuiContext& g = *ctx;
3493     IM_ASSERT(hook_id != 0);
3494     for (int n = 0; n < g.Hooks.Size; n++)
3495         if (g.Hooks[n].HookId == hook_id)
3496             g.Hooks[n].Type = ImGuiContextHookType_PendingRemoval_;
3497 }
3498 
3499 // Call context hooks (used by e.g. test engine)
3500 // We assume a small number of hooks so all stored in same array
CallContextHooks(ImGuiContext * ctx,ImGuiContextHookType hook_type)3501 void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type)
3502 {
3503     ImGuiContext& g = *ctx;
3504     for (int n = 0; n < g.Hooks.Size; n++)
3505         if (g.Hooks[n].Type == hook_type)
3506             g.Hooks[n].Callback(&g, &g.Hooks[n]);
3507 }
3508 
GetIO()3509 ImGuiIO& ImGui::GetIO()
3510 {
3511     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3512     return GImGui->IO;
3513 }
3514 
GetPlatformIO()3515 ImGuiPlatformIO& ImGui::GetPlatformIO()
3516 {
3517     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
3518     return GImGui->PlatformIO;
3519 }
3520 
3521 // Pass this to your backend rendering function! Valid after Render() and until the next call to NewFrame()
GetDrawData()3522 ImDrawData* ImGui::GetDrawData()
3523 {
3524     ImGuiContext& g = *GImGui;
3525     ImGuiViewportP* viewport = g.Viewports[0];
3526     return viewport->DrawDataP.Valid ? &viewport->DrawDataP : NULL;
3527 }
3528 
GetTime()3529 double ImGui::GetTime()
3530 {
3531     return GImGui->Time;
3532 }
3533 
GetFrameCount()3534 int ImGui::GetFrameCount()
3535 {
3536     return GImGui->FrameCount;
3537 }
3538 
GetViewportDrawList(ImGuiViewportP * viewport,size_t drawlist_no,const char * drawlist_name)3539 static ImDrawList* GetViewportDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name)
3540 {
3541     // Create the draw list on demand, because they are not frequently used for all viewports
3542     ImGuiContext& g = *GImGui;
3543     IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->DrawLists));
3544     ImDrawList* draw_list = viewport->DrawLists[drawlist_no];
3545     if (draw_list == NULL)
3546     {
3547         draw_list = IM_NEW(ImDrawList)(&g.DrawListSharedData);
3548         draw_list->_OwnerName = drawlist_name;
3549         viewport->DrawLists[drawlist_no] = draw_list;
3550     }
3551 
3552     // Our ImDrawList system requires that there is always a command
3553     if (viewport->DrawListsLastFrame[drawlist_no] != g.FrameCount)
3554     {
3555         draw_list->_ResetForNewFrame();
3556         draw_list->PushTextureID(g.IO.Fonts->TexID);
3557         draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false);
3558         viewport->DrawListsLastFrame[drawlist_no] = g.FrameCount;
3559     }
3560     return draw_list;
3561 }
3562 
GetBackgroundDrawList(ImGuiViewport * viewport)3563 ImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport)
3564 {
3565     return GetViewportDrawList((ImGuiViewportP*)viewport, 0, "##Background");
3566 }
3567 
GetBackgroundDrawList()3568 ImDrawList* ImGui::GetBackgroundDrawList()
3569 {
3570     ImGuiContext& g = *GImGui;
3571     return GetBackgroundDrawList(g.CurrentWindow->Viewport);
3572 }
3573 
GetForegroundDrawList(ImGuiViewport * viewport)3574 ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport)
3575 {
3576     return GetViewportDrawList((ImGuiViewportP*)viewport, 1, "##Foreground");
3577 }
3578 
GetForegroundDrawList()3579 ImDrawList* ImGui::GetForegroundDrawList()
3580 {
3581     ImGuiContext& g = *GImGui;
3582     return GetForegroundDrawList(g.CurrentWindow->Viewport);
3583 }
3584 
GetDrawListSharedData()3585 ImDrawListSharedData* ImGui::GetDrawListSharedData()
3586 {
3587     return &GImGui->DrawListSharedData;
3588 }
3589 
StartMouseMovingWindow(ImGuiWindow * window)3590 void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
3591 {
3592     // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
3593     // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
3594     // This is because we want ActiveId to be set even when the window is not permitted to move.
3595     ImGuiContext& g = *GImGui;
3596     FocusWindow(window);
3597     SetActiveID(window->MoveId, window);
3598     g.NavDisableHighlight = true;
3599     g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindowDockTree->Pos;
3600     g.ActiveIdNoClearOnFocusLoss = true;
3601     SetActiveIdUsingNavAndKeys();
3602 
3603     bool can_move_window = true;
3604     if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindowDockTree->Flags & ImGuiWindowFlags_NoMove))
3605         can_move_window = false;
3606     if (ImGuiDockNode* node = window->DockNodeAsHost)
3607         if (node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove))
3608             can_move_window = false;
3609     if (can_move_window)
3610         g.MovingWindow = window;
3611 }
3612 
3613 // We use 'undock_floating_node == false' when dragging from title bar to allow moving groups of floating nodes without undocking them.
3614 // - undock_floating_node == true: when dragging from a floating node within a hierarchy, always undock the node.
3615 // - 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)3616 void ImGui::StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* node, bool undock_floating_node)
3617 {
3618     ImGuiContext& g = *GImGui;
3619     bool can_undock_node = false;
3620     if (node != NULL && node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove) == 0)
3621     {
3622         // Can undock if:
3623         // - part of a floating node hierarchy with more than one visible node (if only one is visible, we'll just move the whole hierarchy)
3624         // - part of a dockspace node hierarchy (trivia: undocking from a fixed/central node will create a new node and copy windows)
3625         ImGuiDockNode* root_node = DockNodeGetRootNode(node);
3626         if (root_node->OnlyNodeWithWindows != node || root_node->CentralNode != NULL)   // -V1051 PVS-Studio thinks node should be root_node and is wrong about that.
3627             if (undock_floating_node || root_node->IsDockSpace())
3628                 can_undock_node = true;
3629     }
3630 
3631     const bool clicked = IsMouseClicked(0);
3632     const bool dragging = IsMouseDragging(0, g.IO.MouseDragThreshold * 1.70f);
3633     if (can_undock_node && dragging)
3634         DockContextQueueUndockNode(&g, node); // Will lead to DockNodeStartMouseMovingWindow() -> StartMouseMovingWindow() being called next frame
3635     else if (!can_undock_node && (clicked || dragging) && g.MovingWindow != window)
3636         StartMouseMovingWindow(window);
3637 }
3638 
3639 // Handle mouse moving window
3640 // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
3641 // FIXME: We don't have strong guarantee that g.MovingWindow stay synched with g.ActiveId == g.MovingWindow->MoveId.
3642 // This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs,
3643 // but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other.
UpdateMouseMovingWindowNewFrame()3644 void ImGui::UpdateMouseMovingWindowNewFrame()
3645 {
3646     ImGuiContext& g = *GImGui;
3647     if (g.MovingWindow != NULL)
3648     {
3649         // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
3650         // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
3651         KeepAliveID(g.ActiveId);
3652         IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindowDockTree);
3653         ImGuiWindow* moving_window = g.MovingWindow->RootWindowDockTree;
3654         if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
3655         {
3656             ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
3657             if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
3658             {
3659                 MarkIniSettingsDirty(moving_window);
3660                 SetWindowPos(moving_window, pos, ImGuiCond_Always);
3661                 if (moving_window->ViewportOwned) // Synchronize viewport immediately because some overlays may relies on clipping rectangle before we Begin() into the window.
3662                 {
3663                     moving_window->Viewport->Pos = pos;
3664                     moving_window->Viewport->UpdateWorkRect();
3665                 }
3666             }
3667             FocusWindow(g.MovingWindow);
3668         }
3669         else
3670         {
3671             // Try to merge the window back into the main viewport.
3672             // This works because MouseViewport should be != MovingWindow->Viewport on release (as per code in UpdateViewports)
3673             if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)
3674                 UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseViewport);
3675 
3676             // Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button.
3677             if (!IsDragDropPayloadBeingAccepted())
3678                 g.MouseViewport = moving_window->Viewport;
3679 
3680             // Clear the NoInput window flag set by the Viewport system
3681             moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; // FIXME-VIEWPORT: Test engine managed to crash here because Viewport was NULL.
3682 
3683             g.MovingWindow = NULL;
3684             ClearActiveID();
3685         }
3686     }
3687     else
3688     {
3689         // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
3690         if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
3691         {
3692             KeepAliveID(g.ActiveId);
3693             if (!g.IO.MouseDown[0])
3694                 ClearActiveID();
3695         }
3696     }
3697 }
3698 
3699 // Initiate moving window when clicking on empty space or title bar.
3700 // Handle left-click and right-click focus.
UpdateMouseMovingWindowEndFrame()3701 void ImGui::UpdateMouseMovingWindowEndFrame()
3702 {
3703     ImGuiContext& g = *GImGui;
3704     if (g.ActiveId != 0 || g.HoveredId != 0)
3705         return;
3706 
3707     // Unless we just made a window/popup appear
3708     if (g.NavWindow && g.NavWindow->Appearing)
3709         return;
3710 
3711     // Click on empty space to focus window and start moving
3712     // (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!)
3713     if (g.IO.MouseClicked[0])
3714     {
3715         // Handle the edge case of a popup being closed while clicking in its empty space.
3716         // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more.
3717         ImGuiWindow* root_window = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
3718         const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(root_window->PopupId, ImGuiPopupFlags_AnyPopupLevel);
3719 
3720         if (root_window != NULL && !is_closed_popup)
3721         {
3722             StartMouseMovingWindow(g.HoveredWindow); //-V595
3723 
3724             // Cancel moving if clicked outside of title bar
3725             if (g.IO.ConfigWindowsMoveFromTitleBarOnly)
3726                 if (!(root_window->Flags & ImGuiWindowFlags_NoTitleBar) || root_window->DockIsActive)
3727                     if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
3728                         g.MovingWindow = NULL;
3729 
3730             // Cancel moving if clicked over an item which was disabled or inhibited by popups (note that we know HoveredId == 0 already)
3731             if (g.HoveredIdDisabled)
3732                 g.MovingWindow = NULL;
3733         }
3734         else if (root_window == NULL && g.NavWindow != NULL && GetTopMostPopupModal() == NULL)
3735         {
3736             // Clicking on void disable focus
3737             FocusWindow(NULL);
3738         }
3739     }
3740 
3741     // With right mouse button we close popups without changing focus based on where the mouse is aimed
3742     // Instead, focus will be restored to the window under the bottom-most closed popup.
3743     // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
3744     if (g.IO.MouseClicked[1])
3745     {
3746         // Find the top-most window between HoveredWindow and the top-most Modal Window.
3747         // This is where we can trim the popup stack.
3748         ImGuiWindow* modal = GetTopMostPopupModal();
3749         bool hovered_window_above_modal = g.HoveredWindow && IsWindowAbove(g.HoveredWindow, modal);
3750         ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true);
3751     }
3752 }
3753 
TranslateWindow(ImGuiWindow * window,const ImVec2 & delta)3754 static void TranslateWindow(ImGuiWindow* window, const ImVec2& delta)
3755 {
3756     window->Pos += delta;
3757     window->ClipRect.Translate(delta);
3758     window->OuterRectClipped.Translate(delta);
3759     window->InnerRect.Translate(delta);
3760     window->DC.CursorPos += delta;
3761     window->DC.CursorStartPos += delta;
3762     window->DC.CursorMaxPos += delta;
3763     window->DC.LastItemRect.Translate(delta);
3764     window->DC.LastItemDisplayRect.Translate(delta);
3765 }
3766 
ScaleWindow(ImGuiWindow * window,float scale)3767 static void ScaleWindow(ImGuiWindow* window, float scale)
3768 {
3769     ImVec2 origin = window->Viewport->Pos;
3770     window->Pos = ImFloor((window->Pos - origin) * scale + origin);
3771     window->Size = ImFloor(window->Size * scale);
3772     window->SizeFull = ImFloor(window->SizeFull * scale);
3773     window->ContentSize = ImFloor(window->ContentSize * scale);
3774 }
3775 
IsWindowActiveAndVisible(ImGuiWindow * window)3776 static bool IsWindowActiveAndVisible(ImGuiWindow* window)
3777 {
3778     return (window->Active) && (!window->Hidden);
3779 }
3780 
UpdateMouseInputs()3781 static void ImGui::UpdateMouseInputs()
3782 {
3783     ImGuiContext& g = *GImGui;
3784 
3785     // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
3786     if (IsMousePosValid(&g.IO.MousePos))
3787         g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos);
3788 
3789     // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
3790     if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
3791         g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
3792     else
3793         g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
3794     if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
3795         g.NavDisableMouseHover = false;
3796 
3797     g.IO.MousePosPrev = g.IO.MousePos;
3798     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3799     {
3800         g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
3801         g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
3802         g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
3803         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;
3804         g.IO.MouseDoubleClicked[i] = false;
3805         if (g.IO.MouseClicked[i])
3806         {
3807             if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime)
3808             {
3809                 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3810                 if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
3811                     g.IO.MouseDoubleClicked[i] = true;
3812                 g.IO.MouseClickedTime[i] = -g.IO.MouseDoubleClickTime * 2.0f; // Mark as "old enough" so the third click isn't turned into a double-click
3813             }
3814             else
3815             {
3816                 g.IO.MouseClickedTime[i] = g.Time;
3817             }
3818             g.IO.MouseClickedPos[i] = g.IO.MousePos;
3819             g.IO.MouseDownWasDoubleClick[i] = g.IO.MouseDoubleClicked[i];
3820             g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
3821             g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
3822         }
3823         else if (g.IO.MouseDown[i])
3824         {
3825             // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
3826             ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3827             g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
3828             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);
3829             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);
3830         }
3831         if (!g.IO.MouseDown[i] && !g.IO.MouseReleased[i])
3832             g.IO.MouseDownWasDoubleClick[i] = false;
3833         if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
3834             g.NavDisableMouseHover = false;
3835     }
3836 }
3837 
StartLockWheelingWindow(ImGuiWindow * window)3838 static void StartLockWheelingWindow(ImGuiWindow* window)
3839 {
3840     ImGuiContext& g = *GImGui;
3841     if (g.WheelingWindow == window)
3842         return;
3843     g.WheelingWindow = window;
3844     g.WheelingWindowRefMousePos = g.IO.MousePos;
3845     g.WheelingWindowTimer = WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER;
3846 }
3847 
UpdateMouseWheel()3848 void ImGui::UpdateMouseWheel()
3849 {
3850     ImGuiContext& g = *GImGui;
3851 
3852     // Reset the locked window if we move the mouse or after the timer elapses
3853     if (g.WheelingWindow != NULL)
3854     {
3855         g.WheelingWindowTimer -= g.IO.DeltaTime;
3856         if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
3857             g.WheelingWindowTimer = 0.0f;
3858         if (g.WheelingWindowTimer <= 0.0f)
3859         {
3860             g.WheelingWindow = NULL;
3861             g.WheelingWindowTimer = 0.0f;
3862         }
3863     }
3864 
3865     if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f)
3866         return;
3867 
3868     if ((g.ActiveId != 0 && g.ActiveIdUsingMouseWheel) || (g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrameUsingMouseWheel))
3869         return;
3870 
3871     ImGuiWindow* window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
3872     if (!window || window->Collapsed)
3873         return;
3874 
3875     // Zoom / Scale window
3876     // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
3877     if (g.IO.MouseWheel != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
3878     {
3879         StartLockWheelingWindow(window);
3880         const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
3881         const float scale = new_font_scale / window->FontWindowScale;
3882         window->FontWindowScale = new_font_scale;
3883         if (window == window->RootWindow)
3884         {
3885             const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
3886             SetWindowPos(window, window->Pos + offset, 0);
3887             window->Size = ImFloor(window->Size * scale);
3888             window->SizeFull = ImFloor(window->SizeFull * scale);
3889         }
3890         return;
3891     }
3892 
3893     // Mouse wheel scrolling
3894     // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent
3895     if (g.IO.KeyCtrl)
3896         return;
3897 
3898     // As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead
3899     // (we avoid doing it on OSX as it the OS input layer handles this already)
3900     const bool swap_axis = g.IO.KeyShift && !g.IO.ConfigMacOSXBehaviors;
3901     const float wheel_y = swap_axis ? 0.0f : g.IO.MouseWheel;
3902     const float wheel_x = swap_axis ? g.IO.MouseWheel : g.IO.MouseWheelH;
3903 
3904     // Vertical Mouse Wheel scrolling
3905     if (wheel_y != 0.0f)
3906     {
3907         StartLockWheelingWindow(window);
3908         while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3909             window = window->ParentWindow;
3910         if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3911         {
3912             float max_step = window->InnerRect.GetHeight() * 0.67f;
3913             float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step));
3914             SetScrollY(window, window->Scroll.y - wheel_y * scroll_step);
3915         }
3916     }
3917 
3918     // Horizontal Mouse Wheel scrolling, or Vertical Mouse Wheel w/ Shift held
3919     if (wheel_x != 0.0f)
3920     {
3921         StartLockWheelingWindow(window);
3922         while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.x == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3923             window = window->ParentWindow;
3924         if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3925         {
3926             float max_step = window->InnerRect.GetWidth() * 0.67f;
3927             float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step));
3928             SetScrollX(window, window->Scroll.x - wheel_x * scroll_step);
3929         }
3930     }
3931 }
3932 
UpdateTabFocus()3933 void ImGui::UpdateTabFocus()
3934 {
3935     ImGuiContext& g = *GImGui;
3936 
3937     // Pressing TAB activate widget focus
3938     g.TabFocusPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab));
3939     if (g.ActiveId == 0 && g.TabFocusPressed)
3940     {
3941         // - This path is only taken when no widget are active/tabbed-into yet.
3942         //   Subsequent tabbing will be processed by FocusableItemRegister()
3943         // - Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also
3944         //   manipulate the Next fields here even though they will be turned into Curr fields below.
3945         g.TabFocusRequestNextWindow = g.NavWindow;
3946         g.TabFocusRequestNextCounterRegular = INT_MAX;
3947         if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
3948             g.TabFocusRequestNextCounterTabStop = g.NavIdTabCounter + (g.IO.KeyShift ? -1 : 0);
3949         else
3950             g.TabFocusRequestNextCounterTabStop = g.IO.KeyShift ? -1 : 0;
3951     }
3952 
3953     // Turn queued focus request into current one
3954     g.TabFocusRequestCurrWindow = NULL;
3955     g.TabFocusRequestCurrCounterRegular = g.TabFocusRequestCurrCounterTabStop = INT_MAX;
3956     if (g.TabFocusRequestNextWindow != NULL)
3957     {
3958         ImGuiWindow* window = g.TabFocusRequestNextWindow;
3959         g.TabFocusRequestCurrWindow = window;
3960         if (g.TabFocusRequestNextCounterRegular != INT_MAX && window->DC.FocusCounterRegular != -1)
3961             g.TabFocusRequestCurrCounterRegular = ImModPositive(g.TabFocusRequestNextCounterRegular, window->DC.FocusCounterRegular + 1);
3962         if (g.TabFocusRequestNextCounterTabStop != INT_MAX && window->DC.FocusCounterTabStop != -1)
3963             g.TabFocusRequestCurrCounterTabStop = ImModPositive(g.TabFocusRequestNextCounterTabStop, window->DC.FocusCounterTabStop + 1);
3964         g.TabFocusRequestNextWindow = NULL;
3965         g.TabFocusRequestNextCounterRegular = g.TabFocusRequestNextCounterTabStop = INT_MAX;
3966     }
3967 
3968     g.NavIdTabCounter = INT_MAX;
3969 }
3970 
3971 // 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()3972 void ImGui::UpdateHoveredWindowAndCaptureFlags()
3973 {
3974     ImGuiContext& g = *GImGui;
3975     g.WindowsHoverPadding = ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_HOVER_PADDING, WINDOWS_HOVER_PADDING));
3976 
3977     // Find the window hovered by mouse:
3978     // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3979     // - 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.
3980     // - 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.
3981     bool clear_hovered_windows = false;
3982     FindHoveredWindow();
3983     IM_ASSERT(g.HoveredWindow == NULL || g.HoveredWindow == g.MovingWindow || g.HoveredWindow->Viewport == g.MouseViewport);
3984 
3985     // Modal windows prevents mouse from hovering behind them.
3986     ImGuiWindow* modal_window = GetTopMostPopupModal();
3987     if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindowDockTree, modal_window))
3988         clear_hovered_windows = true;
3989 
3990     // Disabled mouse?
3991     if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
3992         clear_hovered_windows = true;
3993 
3994     // 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.
3995     int mouse_earliest_button_down = -1;
3996     bool mouse_any_down = false;
3997     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3998     {
3999         if (g.IO.MouseClicked[i])
4000             g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (g.OpenPopupStack.Size > 0);
4001         mouse_any_down |= g.IO.MouseDown[i];
4002         if (g.IO.MouseDown[i])
4003             if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
4004                 mouse_earliest_button_down = i;
4005     }
4006     const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
4007 
4008     // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
4009     // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
4010     const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
4011     if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
4012         clear_hovered_windows = true;
4013 
4014     if (clear_hovered_windows)
4015         g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
4016 
4017     // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app)
4018     if (g.WantCaptureMouseNextFrame != -1)
4019         g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
4020     else
4021         g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.OpenPopupStack.Size > 0);
4022 
4023     // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app)
4024     if (g.WantCaptureKeyboardNextFrame != -1)
4025         g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
4026     else
4027         g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
4028     if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
4029         g.IO.WantCaptureKeyboard = true;
4030 
4031     // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
4032     g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
4033 }
4034 
GetMergedKeyModFlags()4035 ImGuiKeyModFlags ImGui::GetMergedKeyModFlags()
4036 {
4037     ImGuiContext& g = *GImGui;
4038     ImGuiKeyModFlags key_mod_flags = ImGuiKeyModFlags_None;
4039     if (g.IO.KeyCtrl)   { key_mod_flags |= ImGuiKeyModFlags_Ctrl; }
4040     if (g.IO.KeyShift)  { key_mod_flags |= ImGuiKeyModFlags_Shift; }
4041     if (g.IO.KeyAlt)    { key_mod_flags |= ImGuiKeyModFlags_Alt; }
4042     if (g.IO.KeySuper)  { key_mod_flags |= ImGuiKeyModFlags_Super; }
4043     return key_mod_flags;
4044 }
4045 
NewFrame()4046 void ImGui::NewFrame()
4047 {
4048     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
4049     ImGuiContext& g = *GImGui;
4050 
4051     // Remove pending delete hooks before frame start.
4052     // This deferred removal avoid issues of removal while iterating the hook vector
4053     for (int n = g.Hooks.Size - 1; n >= 0; n--)
4054         if (g.Hooks[n].Type == ImGuiContextHookType_PendingRemoval_)
4055             g.Hooks.erase(&g.Hooks[n]);
4056 
4057     CallContextHooks(&g, ImGuiContextHookType_NewFramePre);
4058 
4059     // Check and assert for various common IO and Configuration mistakes
4060     g.ConfigFlagsLastFrame = g.ConfigFlagsCurrFrame;
4061     ErrorCheckNewFrameSanityChecks();
4062     g.ConfigFlagsCurrFrame = g.IO.ConfigFlags;
4063 
4064     // Load settings on first frame, save settings when modified (after a delay)
4065     UpdateSettings();
4066 
4067     g.Time += g.IO.DeltaTime;
4068     g.WithinFrameScope = true;
4069     g.FrameCount += 1;
4070     g.TooltipOverrideCount = 0;
4071     g.WindowsActiveCount = 0;
4072     g.MenusIdSubmittedThisFrame.resize(0);
4073 
4074     // Calculate frame-rate for the user, as a purely luxurious feature
4075     g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
4076     g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
4077     g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
4078     g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, IM_ARRAYSIZE(g.FramerateSecPerFrame));
4079     g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)g.FramerateSecPerFrameCount)) : FLT_MAX;
4080 
4081     UpdateViewportsNewFrame();
4082 
4083     // Setup current font and draw list shared data
4084     // FIXME-VIEWPORT: the concept of a single ClipRectFullscreen is not ideal!
4085     g.IO.Fonts->Locked = true;
4086     SetCurrentFont(GetDefaultFont());
4087     IM_ASSERT(g.Font->IsLoaded());
4088     ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
4089     for (int n = 0; n < g.Viewports.Size; n++)
4090         virtual_space.Add(g.Viewports[n]->GetMainRect());
4091     g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4();
4092     g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
4093     g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError);
4094     g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
4095     if (g.Style.AntiAliasedLines)
4096         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
4097     if (g.Style.AntiAliasedLinesUseTex && !(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines))
4098         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex;
4099     if (g.Style.AntiAliasedFill)
4100         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
4101     if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
4102         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
4103 
4104     // Mark rendering data as invalid to prevent user who may have a handle on it to use it.
4105     for (int n = 0; n < g.Viewports.Size; n++)
4106     {
4107         ImGuiViewportP* viewport = g.Viewports[n];
4108         viewport->DrawData = NULL;
4109         viewport->DrawDataP.Clear();
4110     }
4111 
4112     // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
4113     if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
4114         KeepAliveID(g.DragDropPayload.SourceId);
4115 
4116     // Update HoveredId data
4117     if (!g.HoveredIdPreviousFrame)
4118         g.HoveredIdTimer = 0.0f;
4119     if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
4120         g.HoveredIdNotActiveTimer = 0.0f;
4121     if (g.HoveredId)
4122         g.HoveredIdTimer += g.IO.DeltaTime;
4123     if (g.HoveredId && g.ActiveId != g.HoveredId)
4124         g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
4125     g.HoveredIdPreviousFrame = g.HoveredId;
4126     g.HoveredIdPreviousFrameUsingMouseWheel = g.HoveredIdUsingMouseWheel;
4127     g.HoveredId = 0;
4128     g.HoveredIdAllowOverlap = false;
4129     g.HoveredIdUsingMouseWheel = false;
4130     g.HoveredIdDisabled = false;
4131 
4132     // Update ActiveId data (clear reference to active widget if the widget isn't alive anymore)
4133     if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
4134         ClearActiveID();
4135     if (g.ActiveId)
4136         g.ActiveIdTimer += g.IO.DeltaTime;
4137     g.LastActiveIdTimer += g.IO.DeltaTime;
4138     g.ActiveIdPreviousFrame = g.ActiveId;
4139     g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
4140     g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore;
4141     g.ActiveIdIsAlive = 0;
4142     g.ActiveIdHasBeenEditedThisFrame = false;
4143     g.ActiveIdPreviousFrameIsAlive = false;
4144     g.ActiveIdIsJustActivated = false;
4145     if (g.TempInputId != 0 && g.ActiveId != g.TempInputId)
4146         g.TempInputId = 0;
4147     if (g.ActiveId == 0)
4148     {
4149         g.ActiveIdUsingNavDirMask = 0x00;
4150         g.ActiveIdUsingNavInputMask = 0x00;
4151         g.ActiveIdUsingKeyInputMask = 0x00;
4152     }
4153 
4154     // Drag and drop
4155     g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
4156     g.DragDropAcceptIdCurr = 0;
4157     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
4158     g.DragDropWithinSource = false;
4159     g.DragDropWithinTarget = false;
4160     g.DragDropHoldJustPressedId = 0;
4161 
4162     // Update keyboard input state
4163     // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools
4164     g.IO.KeyMods = GetMergedKeyModFlags();
4165     memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
4166     for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
4167         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;
4168 
4169     // Update gamepad/keyboard navigation
4170     NavUpdate();
4171 
4172     // Update mouse input state
4173     UpdateMouseInputs();
4174 
4175     // Undocking
4176     // (needs to be before UpdateMouseMovingWindowNewFrame so the window is already offset and following the mouse on the detaching frame)
4177     DockContextNewFrameUpdateUndocking(&g);
4178 
4179     // Find hovered window
4180     // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
4181     UpdateHoveredWindowAndCaptureFlags();
4182 
4183     // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
4184     UpdateMouseMovingWindowNewFrame();
4185 
4186     // Background darkening/whitening
4187     if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
4188         g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
4189     else
4190         g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
4191 
4192     g.MouseCursor = ImGuiMouseCursor_Arrow;
4193     g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
4194     g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
4195     g.PlatformImePosViewport = NULL;
4196 
4197     // Mouse wheel scrolling, scale
4198     UpdateMouseWheel();
4199 
4200     // Update legacy TAB focus
4201     UpdateTabFocus();
4202 
4203     // Mark all windows as not visible and compact unused memory.
4204     IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size);
4205     const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer;
4206     for (int i = 0; i != g.Windows.Size; i++)
4207     {
4208         ImGuiWindow* window = g.Windows[i];
4209         window->WasActive = window->Active;
4210         window->BeginCount = 0;
4211         window->Active = false;
4212         window->WriteAccessed = false;
4213 
4214         // Garbage collect transient buffers of recently unused windows
4215         if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
4216             GcCompactTransientWindowBuffers(window);
4217     }
4218 
4219     // Garbage collect transient buffers of recently unused tables
4220     for (int i = 0; i < g.TablesLastTimeActive.Size; i++)
4221         if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time)
4222             TableGcCompactTransientBuffers(g.Tables.GetByIndex(i));
4223     for (int i = 0; i < g.TablesTempDataStack.Size; i++)
4224         if (g.TablesTempDataStack[i].LastTimeActive >= 0.0f && g.TablesTempDataStack[i].LastTimeActive < memory_compact_start_time)
4225             TableGcCompactTransientBuffers(&g.TablesTempDataStack[i]);
4226     if (g.GcCompactAll)
4227         GcCompactTransientMiscBuffers();
4228     g.GcCompactAll = false;
4229 
4230     // Closing the focused window restore focus to the first active root window in descending z-order
4231     if (g.NavWindow && !g.NavWindow->WasActive)
4232         FocusTopMostWindowUnderOne(NULL, NULL);
4233 
4234     // No window should be open at the beginning of the frame.
4235     // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
4236     g.CurrentWindowStack.resize(0);
4237     g.BeginPopupStack.resize(0);
4238     g.ItemFlagsStack.resize(0);
4239     g.ItemFlagsStack.push_back(ImGuiItemFlags_None);
4240     g.GroupStack.resize(0);
4241     ClosePopupsOverWindow(g.NavWindow, false);
4242 
4243     // Docking
4244     DockContextNewFrameUpdateDocking(&g);
4245 
4246     // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
4247     UpdateDebugToolItemPicker();
4248 
4249     // Create implicit/fallback window - which we will only render it if the user has added something to it.
4250     // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
4251     // This fallback is particularly important as it avoid ImGui:: calls from crashing.
4252     g.WithinFrameScopeWithImplicitWindow = true;
4253     SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
4254     Begin("Debug##Default");
4255     IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true);
4256 
4257     CallContextHooks(&g, ImGuiContextHookType_NewFramePost);
4258 }
4259 
4260 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
UpdateDebugToolItemPicker()4261 void ImGui::UpdateDebugToolItemPicker()
4262 {
4263     ImGuiContext& g = *GImGui;
4264     g.DebugItemPickerBreakId = 0;
4265     if (g.DebugItemPickerActive)
4266     {
4267         const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
4268         SetMouseCursor(ImGuiMouseCursor_Hand);
4269         if (IsKeyPressedMap(ImGuiKey_Escape))
4270             g.DebugItemPickerActive = false;
4271         if (IsMouseClicked(0) && hovered_id)
4272         {
4273             g.DebugItemPickerBreakId = hovered_id;
4274             g.DebugItemPickerActive = false;
4275         }
4276         SetNextWindowBgAlpha(0.60f);
4277         BeginTooltip();
4278         Text("HoveredId: 0x%08X", hovered_id);
4279         Text("Press ESC to abort picking.");
4280         TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!");
4281         EndTooltip();
4282     }
4283 }
4284 
Initialize(ImGuiContext * context)4285 void ImGui::Initialize(ImGuiContext* context)
4286 {
4287     ImGuiContext& g = *context;
4288     IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
4289 
4290     // Add .ini handle for ImGuiWindow type
4291     {
4292         ImGuiSettingsHandler ini_handler;
4293         ini_handler.TypeName = "Window";
4294         ini_handler.TypeHash = ImHashStr("Window");
4295         ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll;
4296         ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen;
4297         ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;
4298         ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll;
4299         ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll;
4300         g.SettingsHandlers.push_back(ini_handler);
4301     }
4302 
4303     // Add .ini handle for ImGuiTable type
4304     TableSettingsInstallHandler(context);
4305 
4306     // Create default viewport
4307     ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)();
4308     viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID;
4309     viewport->Idx = 0;
4310     viewport->PlatformWindowCreated = true;
4311     viewport->Flags = ImGuiViewportFlags_OwnedByApp;
4312     g.Viewports.push_back(viewport);
4313     g.PlatformIO.Viewports.push_back(g.Viewports[0]);
4314 
4315 #ifdef IMGUI_HAS_DOCK
4316     // Initialize Docking
4317     DockContextInitialize(&g);
4318 #endif
4319 
4320     g.Initialized = true;
4321 }
4322 
4323 // This function is merely here to free heap allocations.
Shutdown(ImGuiContext * context)4324 void ImGui::Shutdown(ImGuiContext* context)
4325 {
4326     // 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)
4327     ImGuiContext& g = *context;
4328     if (g.IO.Fonts && g.FontAtlasOwnedByContext)
4329     {
4330         g.IO.Fonts->Locked = false;
4331         IM_DELETE(g.IO.Fonts);
4332     }
4333     g.IO.Fonts = NULL;
4334 
4335     // Cleanup of other data are conditional on actually having initialized Dear ImGui.
4336     if (!g.Initialized)
4337         return;
4338 
4339     // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
4340     if (g.SettingsLoaded && g.IO.IniFilename != NULL)
4341     {
4342         ImGuiContext* backup_context = GImGui;
4343         SetCurrentContext(&g);
4344         SaveIniSettingsToDisk(g.IO.IniFilename);
4345         SetCurrentContext(backup_context);
4346     }
4347 
4348     // Destroy platform windows
4349     ImGuiContext* backup_context = ImGui::GetCurrentContext();
4350     SetCurrentContext(context);
4351     DestroyPlatformWindows();
4352     SetCurrentContext(backup_context);
4353 
4354     // Shutdown extensions
4355     DockContextShutdown(&g);
4356 
4357     CallContextHooks(&g, ImGuiContextHookType_Shutdown);
4358 
4359     // Clear everything else
4360     g.Windows.clear_delete();
4361     g.WindowsFocusOrder.clear();
4362     g.WindowsTempSortBuffer.clear();
4363     g.CurrentWindow = NULL;
4364     g.CurrentWindowStack.clear();
4365     g.WindowsById.Clear();
4366     g.NavWindow = NULL;
4367     g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
4368     g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
4369     g.MovingWindow = NULL;
4370     g.ColorStack.clear();
4371     g.StyleVarStack.clear();
4372     g.FontStack.clear();
4373     g.OpenPopupStack.clear();
4374     g.BeginPopupStack.clear();
4375 
4376     g.CurrentViewport = g.MouseViewport = g.MouseLastHoveredViewport = NULL;
4377     g.Viewports.clear_delete();
4378 
4379     g.TabBars.Clear();
4380     g.CurrentTabBarStack.clear();
4381     g.ShrinkWidthBuffer.clear();
4382 
4383     g.Tables.Clear();
4384     g.TablesTempDataStack.clear_destruct();
4385     g.DrawChannelsTempMergeBuffer.clear();
4386 
4387     g.ClipboardHandlerData.clear();
4388     g.MenusIdSubmittedThisFrame.clear();
4389     g.InputTextState.ClearFreeMemory();
4390 
4391     g.SettingsWindows.clear();
4392     g.SettingsHandlers.clear();
4393 
4394     if (g.LogFile)
4395     {
4396 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
4397         if (g.LogFile != stdout)
4398 #endif
4399             ImFileClose(g.LogFile);
4400         g.LogFile = NULL;
4401     }
4402     g.LogBuffer.clear();
4403 
4404     g.Initialized = false;
4405 }
4406 
4407 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)4408 static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
4409 {
4410     const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
4411     const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
4412     if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
4413         return d;
4414     if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
4415         return d;
4416     return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
4417 }
4418 
AddWindowToSortBuffer(ImVector<ImGuiWindow * > * out_sorted_windows,ImGuiWindow * window)4419 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
4420 {
4421     out_sorted_windows->push_back(window);
4422     if (window->Active)
4423     {
4424         int count = window->DC.ChildWindows.Size;
4425         if (count > 1)
4426             ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
4427         for (int i = 0; i < count; i++)
4428         {
4429             ImGuiWindow* child = window->DC.ChildWindows[i];
4430             if (child->Active)
4431                 AddWindowToSortBuffer(out_sorted_windows, child);
4432         }
4433     }
4434 }
4435 
AddDrawListToDrawData(ImVector<ImDrawList * > * out_list,ImDrawList * draw_list)4436 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
4437 {
4438     // Remove trailing command if unused.
4439     // Technically we could return directly instead of popping, but this make things looks neat in Metrics/Debugger window as well.
4440     draw_list->_PopUnusedDrawCmd();
4441     if (draw_list->CmdBuffer.Size == 0)
4442         return;
4443 
4444     // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
4445     // May trigger for you if you are using PrimXXX functions incorrectly.
4446     IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
4447     IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
4448     if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset))
4449         IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
4450 
4451     // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
4452     // If this assert triggers because you are drawing lots of stuff manually:
4453     // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds.
4454     //   Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents.
4455     // - If you want large meshes with more than 64K vertices, you can either:
4456     //   (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'.
4457     //       Most example backends already support this from 1.71. Pre-1.71 backends won't.
4458     //       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.
4459     //   (B) Or handle 32-bit indices in your renderer backend, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h.
4460     //       Most example backends already support this. For example, the OpenGL example code detect index size at compile-time:
4461     //         glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
4462     //       Your own engine or render API may use different parameters or function calls to specify index sizes.
4463     //       2 and 4 bytes indices are generally supported by most graphics API.
4464     // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching
4465     //   the 64K limit to split your draw commands in multiple draw lists.
4466     if (sizeof(ImDrawIdx) == 2)
4467         IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
4468 
4469     out_list->push_back(draw_list);
4470 }
4471 
AddWindowToDrawData(ImGuiWindow * window,int layer)4472 static void AddWindowToDrawData(ImGuiWindow* window, int layer)
4473 {
4474     ImGuiContext& g = *GImGui;
4475     ImGuiViewportP* viewport = window->Viewport;
4476     g.IO.MetricsRenderWindows++;
4477     AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[layer], window->DrawList);
4478     for (int i = 0; i < window->DC.ChildWindows.Size; i++)
4479     {
4480         ImGuiWindow* child = window->DC.ChildWindows[i];
4481         if (IsWindowActiveAndVisible(child)) // Clipped children may have been marked not active
4482             AddWindowToDrawData(child, layer);
4483     }
4484 }
4485 
4486 // Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)
AddRootWindowToDrawData(ImGuiWindow * window)4487 static void AddRootWindowToDrawData(ImGuiWindow* window)
4488 {
4489     int layer = (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0;
4490     AddWindowToDrawData(window, layer);
4491 }
4492 
FlattenIntoSingleLayer()4493 void ImDrawDataBuilder::FlattenIntoSingleLayer()
4494 {
4495     int n = Layers[0].Size;
4496     int size = n;
4497     for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
4498         size += Layers[i].Size;
4499     Layers[0].resize(size);
4500     for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
4501     {
4502         ImVector<ImDrawList*>& layer = Layers[layer_n];
4503         if (layer.empty())
4504             continue;
4505         memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
4506         n += layer.Size;
4507         layer.resize(0);
4508     }
4509 }
4510 
SetupViewportDrawData(ImGuiViewportP * viewport,ImVector<ImDrawList * > * draw_lists)4511 static void SetupViewportDrawData(ImGuiViewportP* viewport, ImVector<ImDrawList*>* draw_lists)
4512 {
4513     // When minimized, we report draw_data->DisplaySize as zero to be consistent with non-viewport mode,
4514     // and to allow applications/backends to easily skip rendering.
4515     // FIXME: Note that we however do NOT attempt to report "zero drawlist / vertices" into the ImDrawData structure.
4516     // This is because the work has been done already, and its wasted! We should fix that and add optimizations for
4517     // it earlier in the pipeline, rather than pretend to hide the data at the end of the pipeline.
4518     const bool is_minimized = (viewport->Flags & ImGuiViewportFlags_Minimized) != 0;
4519 
4520     ImGuiIO& io = ImGui::GetIO();
4521     ImDrawData* draw_data = &viewport->DrawDataP;
4522     viewport->DrawData = draw_data; // Make publicly accessible
4523     draw_data->Valid = true;
4524     draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
4525     draw_data->CmdListsCount = draw_lists->Size;
4526     draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
4527     draw_data->DisplayPos = viewport->Pos;
4528     draw_data->DisplaySize = is_minimized ? ImVec2(0.0f, 0.0f) : viewport->Size;
4529     draw_data->FramebufferScale = io.DisplayFramebufferScale; // FIXME-VIEWPORT: This may vary on a per-monitor/viewport basis?
4530     draw_data->OwnerViewport = viewport;
4531     for (int n = 0; n < draw_lists->Size; n++)
4532     {
4533         draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size;
4534         draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size;
4535     }
4536 }
4537 
4538 // Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering.
4539 // - When using this function it is sane to ensure that float are perfectly rounded to integer values,
4540 //   so that e.g. (int)(max.x-min.x) in user's render produce correct result.
4541 // - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect():
4542 //   some frequently called functions which to modify both channels and clipping simultaneously tend to use the
4543 //   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)4544 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
4545 {
4546     ImGuiWindow* window = GetCurrentWindow();
4547     window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
4548     window->ClipRect = window->DrawList->_ClipRectStack.back();
4549 }
4550 
PopClipRect()4551 void ImGui::PopClipRect()
4552 {
4553     ImGuiWindow* window = GetCurrentWindow();
4554     window->DrawList->PopClipRect();
4555     window->ClipRect = window->DrawList->_ClipRectStack.back();
4556 }
4557 
FindFrontMostVisibleChildWindow(ImGuiWindow * window)4558 static ImGuiWindow* FindFrontMostVisibleChildWindow(ImGuiWindow* window)
4559 {
4560     for (int n = window->DC.ChildWindows.Size - 1; n >= 0; n--)
4561         if (IsWindowActiveAndVisible(window->DC.ChildWindows[n]))
4562             return FindFrontMostVisibleChildWindow(window->DC.ChildWindows[n]);
4563     return window;
4564 }
4565 
EndFrameDrawDimmedBackgrounds()4566 static void ImGui::EndFrameDrawDimmedBackgrounds()
4567 {
4568     ImGuiContext& g = *GImGui;
4569 
4570     // Draw modal whitening background on _other_ viewports than the one the modal is one
4571     ImGuiWindow* modal_window = GetTopMostPopupModal();
4572     const bool dim_bg_for_modal = (modal_window != NULL);
4573     const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL);
4574     if (dim_bg_for_modal || dim_bg_for_window_list)
4575         for (int viewport_n = 0; viewport_n < g.Viewports.Size; viewport_n++)
4576         {
4577             ImGuiViewportP* viewport = g.Viewports[viewport_n];
4578             if (modal_window && viewport == modal_window->Viewport)
4579                 continue;
4580             if (g.NavWindowingListWindow && viewport == g.NavWindowingListWindow->Viewport)
4581                 continue;
4582             if (g.NavWindowingTargetAnim && viewport == g.NavWindowingTargetAnim->Viewport)
4583                 continue;
4584             if (viewport->Window && modal_window && IsWindowAbove(viewport->Window, modal_window))
4585                 continue;
4586             ImDrawList* draw_list = GetForegroundDrawList(viewport);
4587             const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
4588             draw_list->AddRectFilled(viewport->Pos, viewport->Pos + viewport->Size, dim_bg_col);
4589         }
4590 
4591     // Draw modal whitening background between CTRL-TAB list
4592     if (dim_bg_for_window_list && g.NavWindowingTargetAnim->Active)
4593     {
4594         // Choose a draw list that will be front-most across all our children
4595         // In the unlikely case that the window wasn't made active we can't rely on its drawlist and skip rendering all-together.
4596         ImGuiWindow* window = g.NavWindowingTargetAnim;
4597         ImDrawList* draw_list = FindFrontMostVisibleChildWindow(window->RootWindowDockTree)->DrawList;
4598         draw_list->PushClipRectFullScreen();
4599 
4600         // Docking: draw modal whitening background on other nodes of a same dock tree
4601         // For CTRL+TAB within a docking node we need to render the dimming background in 8 steps
4602         // (Because the root node renders the background in one shot, in order to avoid flickering when a child dock node is not submitted)
4603         if (window->RootWindow->DockIsActive)
4604             if (window->RootWindowDockTree != window->RootWindow)
4605                 RenderRectFilledWithHole(draw_list, window->RootWindowDockTree->Rect(), window->RootWindow->Rect(), GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio), g.Style.WindowRounding);
4606 
4607         // Draw navigation selection/windowing rectangle border
4608         float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
4609         ImRect bb = window->Rect();
4610         bb.Expand(g.FontSize);
4611         if (bb.Contains(window->Viewport->GetMainRect())) // If a window fits the entire viewport, adjust its highlight inward
4612         {
4613             bb.Expand(-g.FontSize - 1.0f);
4614             rounding = window->WindowRounding;
4615         }
4616         draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, 0, 3.0f);
4617         draw_list->PopClipRect();
4618     }
4619 }
4620 
4621 // 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()4622 void ImGui::EndFrame()
4623 {
4624     ImGuiContext& g = *GImGui;
4625     IM_ASSERT(g.Initialized);
4626 
4627     // Don't process EndFrame() multiple times.
4628     if (g.FrameCountEnded == g.FrameCount)
4629         return;
4630     IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?");
4631 
4632     CallContextHooks(&g, ImGuiContextHookType_EndFramePre);
4633 
4634     ErrorCheckEndFrameSanityChecks();
4635 
4636     // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
4637     if (g.PlatformIO.Platform_SetImeInputPos && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImePos - g.PlatformImeLastPos) > 0.0001f))
4638         if (g.PlatformImePosViewport && g.PlatformImePosViewport->PlatformWindowCreated)
4639         {
4640             g.PlatformIO.Platform_SetImeInputPos(g.PlatformImePosViewport, g.PlatformImePos);
4641             g.PlatformImeLastPos = g.PlatformImePos;
4642             g.PlatformImePosViewport = NULL;
4643         }
4644 
4645     // Hide implicit/fallback "Debug" window if it hasn't been used
4646     g.WithinFrameScopeWithImplicitWindow = false;
4647     if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
4648         g.CurrentWindow->Active = false;
4649     End();
4650 
4651     // Draw modal whitening background on _other_ viewports than the one the modal is one
4652     EndFrameDrawDimmedBackgrounds();
4653 
4654     // Update navigation: CTRL+Tab, wrap-around requests
4655     NavEndFrame();
4656 
4657     SetCurrentViewport(NULL, NULL);
4658 
4659     // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
4660     if (g.DragDropActive)
4661     {
4662         bool is_delivered = g.DragDropPayload.Delivery;
4663         bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton));
4664         if (is_delivered || is_elapsed)
4665             ClearDragDrop();
4666     }
4667 
4668     // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.
4669     if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
4670     {
4671         g.DragDropWithinSource = true;
4672         SetTooltip("...");
4673         g.DragDropWithinSource = false;
4674     }
4675 
4676     // End frame
4677     g.WithinFrameScope = false;
4678     g.FrameCountEnded = g.FrameCount;
4679 
4680     // Initiate moving window + handle left-click and right-click focus
4681     UpdateMouseMovingWindowEndFrame();
4682 
4683     // Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some)
4684     UpdateViewportsEndFrame();
4685 
4686     // Sort the window list so that all child windows are after their parent
4687     // We cannot do that on FocusWindow() because children may not exist yet
4688     g.WindowsTempSortBuffer.resize(0);
4689     g.WindowsTempSortBuffer.reserve(g.Windows.Size);
4690     for (int i = 0; i != g.Windows.Size; i++)
4691     {
4692         ImGuiWindow* window = g.Windows[i];
4693         if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow))       // if a child is active its parent will add it
4694             continue;
4695         AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window);
4696     }
4697 
4698     // 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.
4699     IM_ASSERT(g.Windows.Size == g.WindowsTempSortBuffer.Size);
4700     g.Windows.swap(g.WindowsTempSortBuffer);
4701     g.IO.MetricsActiveWindows = g.WindowsActiveCount;
4702 
4703     // Unlock font atlas
4704     g.IO.Fonts->Locked = false;
4705 
4706     // Clear Input data for next frame
4707     g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
4708     g.IO.InputQueueCharacters.resize(0);
4709     memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
4710 
4711     CallContextHooks(&g, ImGuiContextHookType_EndFramePost);
4712 }
4713 
4714 // Prepare the data for rendering so you can call GetDrawData()
4715 // (As with anything within the ImGui:: namspace this doesn't touch your GPU or graphics API at all:
4716 // it is the role of the ImGui_ImplXXXX_RenderDrawData() function provided by the renderer backend)
Render()4717 void ImGui::Render()
4718 {
4719     ImGuiContext& g = *GImGui;
4720     IM_ASSERT(g.Initialized);
4721 
4722     if (g.FrameCountEnded != g.FrameCount)
4723         EndFrame();
4724     g.FrameCountRendered = g.FrameCount;
4725     g.IO.MetricsRenderWindows = 0;
4726 
4727     CallContextHooks(&g, ImGuiContextHookType_RenderPre);
4728 
4729     // Add background ImDrawList (for each active viewport)
4730     for (int n = 0; n != g.Viewports.Size; n++)
4731     {
4732         ImGuiViewportP* viewport = g.Viewports[n];
4733         viewport->DrawDataBuilder.Clear();
4734         if (viewport->DrawLists[0] != NULL)
4735             AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport));
4736     }
4737 
4738     // Add ImDrawList to render
4739     ImGuiWindow* windows_to_render_top_most[2];
4740     windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindowDockTree : NULL;
4741     windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL);
4742     for (int n = 0; n != g.Windows.Size; n++)
4743     {
4744         ImGuiWindow* window = g.Windows[n];
4745         IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'"
4746         if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])
4747             AddRootWindowToDrawData(window);
4748     }
4749     for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++)
4750         if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window
4751             AddRootWindowToDrawData(windows_to_render_top_most[n]);
4752 
4753     ImVec2 mouse_cursor_offset, mouse_cursor_size, mouse_cursor_uv[4];
4754     if (g.IO.MouseDrawCursor && g.MouseCursor != ImGuiMouseCursor_None)
4755         g.IO.Fonts->GetMouseCursorTexData(g.MouseCursor, &mouse_cursor_offset, &mouse_cursor_size, &mouse_cursor_uv[0], &mouse_cursor_uv[2]);
4756 
4757     // Setup ImDrawData structures for end-user
4758     g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0;
4759     for (int n = 0; n < g.Viewports.Size; n++)
4760     {
4761         ImGuiViewportP* viewport = g.Viewports[n];
4762         viewport->DrawDataBuilder.FlattenIntoSingleLayer();
4763 
4764         // Draw software mouse cursor if requested by io.MouseDrawCursor flag
4765         // (note we scale cursor by current viewport/monitor, however Windows 10 for its own hardware cursor seems to be using a different scale factor)
4766         if (mouse_cursor_size.x > 0.0f && mouse_cursor_size.y > 0.0f)
4767         {
4768             float scale = g.Style.MouseCursorScale * viewport->DpiScale;
4769             if (viewport->GetMainRect().Overlaps(ImRect(g.IO.MousePos, g.IO.MousePos + ImVec2(mouse_cursor_size.x + 2, mouse_cursor_size.y + 2) * scale)))
4770                 RenderMouseCursor(GetForegroundDrawList(viewport), g.IO.MousePos, scale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
4771         }
4772 
4773         // Add foreground ImDrawList (for each active viewport)
4774         if (viewport->DrawLists[1] != NULL)
4775             AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport));
4776 
4777         SetupViewportDrawData(viewport, &viewport->DrawDataBuilder.Layers[0]);
4778         ImDrawData* draw_data = viewport->DrawData;
4779         g.IO.MetricsRenderVertices += draw_data->TotalVtxCount;
4780         g.IO.MetricsRenderIndices += draw_data->TotalIdxCount;
4781     }
4782 
4783     CallContextHooks(&g, ImGuiContextHookType_RenderPost);
4784 }
4785 
4786 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
4787 // 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)4788 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
4789 {
4790     ImGuiContext& g = *GImGui;
4791 
4792     const char* text_display_end;
4793     if (hide_text_after_double_hash)
4794         text_display_end = FindRenderedTextEnd(text, text_end);      // Hide anything after a '##' string
4795     else
4796         text_display_end = text_end;
4797 
4798     ImFont* font = g.Font;
4799     const float font_size = g.FontSize;
4800     if (text == text_display_end)
4801         return ImVec2(0.0f, font_size);
4802     ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
4803 
4804     // Round
4805     // FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out.
4806     // FIXME: Investigate using ceilf or e.g.
4807     // - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c
4808     // - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html
4809     text_size.x = IM_FLOOR(text_size.x + 0.99999f);
4810 
4811     return text_size;
4812 }
4813 
4814 // Find window given position, search front-to-back
4815 // FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically
4816 // with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
4817 // called, aka before the next Begin(). Moving window isn't affected.
FindHoveredWindow()4818 static void FindHoveredWindow()
4819 {
4820     ImGuiContext& g = *GImGui;
4821 
4822     // Special handling for the window being moved: Ignore the mouse viewport check (because it may reset/lose its viewport during the undocking frame)
4823     ImGuiViewportP* moving_window_viewport = g.MovingWindow ? g.MovingWindow->Viewport : NULL;
4824     if (g.MovingWindow)
4825         g.MovingWindow->Viewport = g.MouseViewport;
4826 
4827     ImGuiWindow* hovered_window = NULL;
4828     ImGuiWindow* hovered_window_ignoring_moving_window = NULL;
4829     if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
4830         hovered_window = g.MovingWindow;
4831 
4832     ImVec2 padding_regular = g.Style.TouchExtraPadding;
4833     ImVec2 padding_for_resize = g.IO.ConfigWindowsResizeFromEdges ? g.WindowsHoverPadding : padding_regular;
4834     for (int i = g.Windows.Size - 1; i >= 0; i--)
4835     {
4836         ImGuiWindow* window = g.Windows[i];
4837         IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
4838         if (!window->Active || window->Hidden)
4839             continue;
4840         if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
4841             continue;
4842         IM_ASSERT(window->Viewport);
4843         if (window->Viewport != g.MouseViewport)
4844             continue;
4845 
4846         // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
4847         ImRect bb(window->OuterRectClipped);
4848         if (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize))
4849             bb.Expand(padding_regular);
4850         else
4851             bb.Expand(padding_for_resize);
4852         if (!bb.Contains(g.IO.MousePos))
4853             continue;
4854 
4855         // Support for one rectangular hole in any given window
4856         // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512)
4857         if (window->HitTestHoleSize.x != 0)
4858         {
4859             ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y);
4860             ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y);
4861             if (ImRect(hole_pos, hole_pos + hole_size).Contains(g.IO.MousePos))
4862                 continue;
4863         }
4864 
4865         if (hovered_window == NULL)
4866             hovered_window = window;
4867         IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
4868         if (hovered_window_ignoring_moving_window == NULL && (!g.MovingWindow || window->RootWindowDockTree != g.MovingWindow->RootWindowDockTree))
4869             hovered_window_ignoring_moving_window = window;
4870         if (hovered_window && hovered_window_ignoring_moving_window)
4871             break;
4872     }
4873 
4874     g.HoveredWindow = hovered_window;
4875     g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window;
4876 
4877     if (g.MovingWindow)
4878         g.MovingWindow->Viewport = moving_window_viewport;
4879 }
4880 
4881 // Test if mouse cursor is hovering given rectangle
4882 // NB- Rectangle is clipped by our current clip setting
4883 // 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)4884 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
4885 {
4886     ImGuiContext& g = *GImGui;
4887 
4888     // Clip
4889     ImRect rect_clipped(r_min, r_max);
4890     if (clip)
4891         rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
4892 
4893     // Expand for touch input
4894     const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
4895     if (!rect_for_touch.Contains(g.IO.MousePos))
4896         return false;
4897     if (!g.MouseViewport->GetMainRect().Overlaps(rect_clipped))
4898         return false;
4899     return true;
4900 }
4901 
GetKeyIndex(ImGuiKey imgui_key)4902 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
4903 {
4904     IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
4905     ImGuiContext& g = *GImGui;
4906     return g.IO.KeyMap[imgui_key];
4907 }
4908 
4909 // Note that dear imgui doesn't know the semantic of each entry of io.KeysDown[]!
4910 // Use your own indices/enums according to how your backend/engine stored them into io.KeysDown[]!
IsKeyDown(int user_key_index)4911 bool ImGui::IsKeyDown(int user_key_index)
4912 {
4913     if (user_key_index < 0)
4914         return false;
4915     ImGuiContext& g = *GImGui;
4916     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4917     return g.IO.KeysDown[user_key_index];
4918 }
4919 
4920 // t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
4921 // t1 = current time (e.g.: g.Time)
4922 // An event is triggered at:
4923 //  t = 0.0f     t = repeat_delay,    t = repeat_delay + repeat_rate*N
CalcTypematicRepeatAmount(float t0,float t1,float repeat_delay,float repeat_rate)4924 int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)
4925 {
4926     if (t1 == 0.0f)
4927         return 1;
4928     if (t0 >= t1)
4929         return 0;
4930     if (repeat_rate <= 0.0f)
4931         return (t0 < repeat_delay) && (t1 >= repeat_delay);
4932     const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);
4933     const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);
4934     const int count = count_t1 - count_t0;
4935     return count;
4936 }
4937 
GetKeyPressedAmount(int key_index,float repeat_delay,float repeat_rate)4938 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
4939 {
4940     ImGuiContext& g = *GImGui;
4941     if (key_index < 0)
4942         return 0;
4943     IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4944     const float t = g.IO.KeysDownDuration[key_index];
4945     return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
4946 }
4947 
IsKeyPressed(int user_key_index,bool repeat)4948 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
4949 {
4950     ImGuiContext& g = *GImGui;
4951     if (user_key_index < 0)
4952         return false;
4953     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4954     const float t = g.IO.KeysDownDuration[user_key_index];
4955     if (t == 0.0f)
4956         return true;
4957     if (repeat && t > g.IO.KeyRepeatDelay)
4958         return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
4959     return false;
4960 }
4961 
IsKeyReleased(int user_key_index)4962 bool ImGui::IsKeyReleased(int user_key_index)
4963 {
4964     ImGuiContext& g = *GImGui;
4965     if (user_key_index < 0) return false;
4966     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4967     return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
4968 }
4969 
IsMouseDown(ImGuiMouseButton button)4970 bool ImGui::IsMouseDown(ImGuiMouseButton button)
4971 {
4972     ImGuiContext& g = *GImGui;
4973     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4974     return g.IO.MouseDown[button];
4975 }
4976 
IsMouseClicked(ImGuiMouseButton button,bool repeat)4977 bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)
4978 {
4979     ImGuiContext& g = *GImGui;
4980     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4981     const float t = g.IO.MouseDownDuration[button];
4982     if (t == 0.0f)
4983         return true;
4984 
4985     if (repeat && t > g.IO.KeyRepeatDelay)
4986     {
4987         // 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.
4988         int amount = CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.50f);
4989         if (amount > 0)
4990             return true;
4991     }
4992     return false;
4993 }
4994 
IsMouseReleased(ImGuiMouseButton button)4995 bool ImGui::IsMouseReleased(ImGuiMouseButton button)
4996 {
4997     ImGuiContext& g = *GImGui;
4998     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4999     return g.IO.MouseReleased[button];
5000 }
5001 
IsMouseDoubleClicked(ImGuiMouseButton button)5002 bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
5003 {
5004     ImGuiContext& g = *GImGui;
5005     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
5006     return g.IO.MouseDoubleClicked[button];
5007 }
5008 
5009 // Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame.
5010 // [Internal] This doesn't test if the button is pressed
IsMouseDragPastThreshold(ImGuiMouseButton button,float lock_threshold)5011 bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold)
5012 {
5013     ImGuiContext& g = *GImGui;
5014     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
5015     if (lock_threshold < 0.0f)
5016         lock_threshold = g.IO.MouseDragThreshold;
5017     return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
5018 }
5019 
IsMouseDragging(ImGuiMouseButton button,float lock_threshold)5020 bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold)
5021 {
5022     ImGuiContext& g = *GImGui;
5023     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
5024     if (!g.IO.MouseDown[button])
5025         return false;
5026     return IsMouseDragPastThreshold(button, lock_threshold);
5027 }
5028 
GetMousePos()5029 ImVec2 ImGui::GetMousePos()
5030 {
5031     ImGuiContext& g = *GImGui;
5032     return g.IO.MousePos;
5033 }
5034 
5035 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()5036 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
5037 {
5038     ImGuiContext& g = *GImGui;
5039     if (g.BeginPopupStack.Size > 0)
5040         return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos;
5041     return g.IO.MousePos;
5042 }
5043 
5044 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
IsMousePosValid(const ImVec2 * mouse_pos)5045 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
5046 {
5047     // The assert is only to silence a false-positive in XCode Static Analysis.
5048     // 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).
5049     IM_ASSERT(GImGui != NULL);
5050     const float MOUSE_INVALID = -256000.0f;
5051     ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
5052     return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
5053 }
5054 
IsAnyMouseDown()5055 bool ImGui::IsAnyMouseDown()
5056 {
5057     ImGuiContext& g = *GImGui;
5058     for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
5059         if (g.IO.MouseDown[n])
5060             return true;
5061     return false;
5062 }
5063 
5064 // Return the delta from the initial clicking position while the mouse button is clicked or was just released.
5065 // This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
5066 // 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)5067 ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold)
5068 {
5069     ImGuiContext& g = *GImGui;
5070     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
5071     if (lock_threshold < 0.0f)
5072         lock_threshold = g.IO.MouseDragThreshold;
5073     if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])
5074         if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
5075             if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))
5076                 return g.IO.MousePos - g.IO.MouseClickedPos[button];
5077     return ImVec2(0.0f, 0.0f);
5078 }
5079 
ResetMouseDragDelta(ImGuiMouseButton button)5080 void ImGui::ResetMouseDragDelta(ImGuiMouseButton button)
5081 {
5082     ImGuiContext& g = *GImGui;
5083     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
5084     // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
5085     g.IO.MouseClickedPos[button] = g.IO.MousePos;
5086 }
5087 
GetMouseCursor()5088 ImGuiMouseCursor ImGui::GetMouseCursor()
5089 {
5090     return GImGui->MouseCursor;
5091 }
5092 
SetMouseCursor(ImGuiMouseCursor cursor_type)5093 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
5094 {
5095     GImGui->MouseCursor = cursor_type;
5096 }
5097 
CaptureKeyboardFromApp(bool capture)5098 void ImGui::CaptureKeyboardFromApp(bool capture)
5099 {
5100     GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
5101 }
5102 
CaptureMouseFromApp(bool capture)5103 void ImGui::CaptureMouseFromApp(bool capture)
5104 {
5105     GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
5106 }
5107 
IsItemActive()5108 bool ImGui::IsItemActive()
5109 {
5110     ImGuiContext& g = *GImGui;
5111     if (g.ActiveId)
5112     {
5113         ImGuiWindow* window = g.CurrentWindow;
5114         return g.ActiveId == window->DC.LastItemId;
5115     }
5116     return false;
5117 }
5118 
IsItemActivated()5119 bool ImGui::IsItemActivated()
5120 {
5121     ImGuiContext& g = *GImGui;
5122     if (g.ActiveId)
5123     {
5124         ImGuiWindow* window = g.CurrentWindow;
5125         if (g.ActiveId == window->DC.LastItemId && g.ActiveIdPreviousFrame != window->DC.LastItemId)
5126             return true;
5127     }
5128     return false;
5129 }
5130 
IsItemDeactivated()5131 bool ImGui::IsItemDeactivated()
5132 {
5133     ImGuiContext& g = *GImGui;
5134     ImGuiWindow* window = g.CurrentWindow;
5135     if (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDeactivated)
5136         return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Deactivated) != 0;
5137     return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId);
5138 }
5139 
IsItemDeactivatedAfterEdit()5140 bool ImGui::IsItemDeactivatedAfterEdit()
5141 {
5142     ImGuiContext& g = *GImGui;
5143     return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore));
5144 }
5145 
5146 // == GetItemID() == GetFocusID()
IsItemFocused()5147 bool ImGui::IsItemFocused()
5148 {
5149     ImGuiContext& g = *GImGui;
5150     ImGuiWindow* window = g.CurrentWindow;
5151 
5152     if (g.NavId != window->DC.LastItemId || g.NavId == 0)
5153         return false;
5154 
5155     // Special handling for the dummy item after Begin() which represent the title bar or tab.
5156     // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
5157     if (window->DC.LastItemId == window->ID && window->WriteAccessed)
5158         return false;
5159 
5160     return true;
5161 }
5162 
5163 // Important: this can be useful but it is NOT equivalent to the behavior of e.g.Button()!
5164 // Most widgets have specific reactions based on mouse-up/down state, mouse position etc.
IsItemClicked(ImGuiMouseButton mouse_button)5165 bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button)
5166 {
5167     return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
5168 }
5169 
IsItemToggledOpen()5170 bool ImGui::IsItemToggledOpen()
5171 {
5172     ImGuiContext& g = *GImGui;
5173     return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false;
5174 }
5175 
IsItemToggledSelection()5176 bool ImGui::IsItemToggledSelection()
5177 {
5178     ImGuiContext& g = *GImGui;
5179     return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;
5180 }
5181 
IsAnyItemHovered()5182 bool ImGui::IsAnyItemHovered()
5183 {
5184     ImGuiContext& g = *GImGui;
5185     return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
5186 }
5187 
IsAnyItemActive()5188 bool ImGui::IsAnyItemActive()
5189 {
5190     ImGuiContext& g = *GImGui;
5191     return g.ActiveId != 0;
5192 }
5193 
IsAnyItemFocused()5194 bool ImGui::IsAnyItemFocused()
5195 {
5196     ImGuiContext& g = *GImGui;
5197     return g.NavId != 0 && !g.NavDisableHighlight;
5198 }
5199 
IsItemVisible()5200 bool ImGui::IsItemVisible()
5201 {
5202     ImGuiWindow* window = GetCurrentWindowRead();
5203     return window->ClipRect.Overlaps(window->DC.LastItemRect);
5204 }
5205 
IsItemEdited()5206 bool ImGui::IsItemEdited()
5207 {
5208     ImGuiWindow* window = GetCurrentWindowRead();
5209     return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0;
5210 }
5211 
5212 // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
5213 // FIXME: Although this is exposed, its interaction and ideal idiom with using ImGuiButtonFlags_AllowItemOverlap flag are extremely confusing, need rework.
SetItemAllowOverlap()5214 void ImGui::SetItemAllowOverlap()
5215 {
5216     ImGuiContext& g = *GImGui;
5217     ImGuiID id = g.CurrentWindow->DC.LastItemId;
5218     if (g.HoveredId == id)
5219         g.HoveredIdAllowOverlap = true;
5220     if (g.ActiveId == id)
5221         g.ActiveIdAllowOverlap = true;
5222 }
5223 
SetItemUsingMouseWheel()5224 void ImGui::SetItemUsingMouseWheel()
5225 {
5226     ImGuiContext& g = *GImGui;
5227     ImGuiID id = g.CurrentWindow->DC.LastItemId;
5228     if (g.HoveredId == id)
5229         g.HoveredIdUsingMouseWheel = true;
5230     if (g.ActiveId == id)
5231         g.ActiveIdUsingMouseWheel = true;
5232 }
5233 
SetActiveIdUsingNavAndKeys()5234 void ImGui::SetActiveIdUsingNavAndKeys()
5235 {
5236     ImGuiContext& g = *GImGui;
5237     IM_ASSERT(g.ActiveId != 0);
5238     g.ActiveIdUsingNavDirMask = ~(ImU32)0;
5239     g.ActiveIdUsingNavInputMask = ~(ImU32)0;
5240     g.ActiveIdUsingKeyInputMask = ~(ImU64)0;
5241     NavMoveRequestCancel();
5242 }
5243 
GetItemRectMin()5244 ImVec2 ImGui::GetItemRectMin()
5245 {
5246     ImGuiWindow* window = GetCurrentWindowRead();
5247     return window->DC.LastItemRect.Min;
5248 }
5249 
GetItemRectMax()5250 ImVec2 ImGui::GetItemRectMax()
5251 {
5252     ImGuiWindow* window = GetCurrentWindowRead();
5253     return window->DC.LastItemRect.Max;
5254 }
5255 
GetItemRectSize()5256 ImVec2 ImGui::GetItemRectSize()
5257 {
5258     ImGuiWindow* window = GetCurrentWindowRead();
5259     return window->DC.LastItemRect.GetSize();
5260 }
5261 
BeginChildEx(const char * name,ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags flags)5262 bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags)
5263 {
5264     ImGuiContext& g = *GImGui;
5265     ImGuiWindow* parent_window = g.CurrentWindow;
5266 
5267     flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoDocking;
5268     flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove);  // Inherit the NoMove flag
5269 
5270     // Size
5271     const ImVec2 content_avail = GetContentRegionAvail();
5272     ImVec2 size = ImFloor(size_arg);
5273     const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
5274     if (size.x <= 0.0f)
5275         size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
5276     if (size.y <= 0.0f)
5277         size.y = ImMax(content_avail.y + size.y, 4.0f);
5278     SetNextWindowSize(size);
5279 
5280     // 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.
5281     if (name)
5282         ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%s/%s_%08X", parent_window->Name, name, id);
5283     else
5284         ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%s/%08X", parent_window->Name, id);
5285 
5286     const float backup_border_size = g.Style.ChildBorderSize;
5287     if (!border)
5288         g.Style.ChildBorderSize = 0.0f;
5289     bool ret = Begin(g.TempBuffer, NULL, flags);
5290     g.Style.ChildBorderSize = backup_border_size;
5291 
5292     ImGuiWindow* child_window = g.CurrentWindow;
5293     child_window->ChildId = id;
5294     child_window->AutoFitChildAxises = (ImS8)auto_fit_axises;
5295 
5296     // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
5297     // While this is not really documented/defined, it seems that the expected thing to do.
5298     if (child_window->BeginCount == 1)
5299         parent_window->DC.CursorPos = child_window->Pos;
5300 
5301     // Process navigation-in immediately so NavInit can run on first frame
5302     if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavHasScroll))
5303     {
5304         FocusWindow(child_window);
5305         NavInitWindow(child_window, false);
5306         SetActiveID(id + 1, child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item
5307         g.ActiveIdSource = ImGuiInputSource_Nav;
5308     }
5309     return ret;
5310 }
5311 
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)5312 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
5313 {
5314     ImGuiWindow* window = GetCurrentWindow();
5315     return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
5316 }
5317 
BeginChild(ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)5318 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
5319 {
5320     IM_ASSERT(id != 0);
5321     return BeginChildEx(NULL, id, size_arg, border, extra_flags);
5322 }
5323 
EndChild()5324 void ImGui::EndChild()
5325 {
5326     ImGuiContext& g = *GImGui;
5327     ImGuiWindow* window = g.CurrentWindow;
5328 
5329     IM_ASSERT(g.WithinEndChild == false);
5330     IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow);   // Mismatched BeginChild()/EndChild() calls
5331 
5332     g.WithinEndChild = true;
5333     if (window->BeginCount > 1)
5334     {
5335         End();
5336     }
5337     else
5338     {
5339         ImVec2 sz = window->Size;
5340         if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
5341             sz.x = ImMax(4.0f, sz.x);
5342         if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
5343             sz.y = ImMax(4.0f, sz.y);
5344         End();
5345 
5346         ImGuiWindow* parent_window = g.CurrentWindow;
5347         ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
5348         ItemSize(sz);
5349         if ((window->DC.NavLayersActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
5350         {
5351             ItemAdd(bb, window->ChildId);
5352             RenderNavHighlight(bb, window->ChildId);
5353 
5354             // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
5355             if (window->DC.NavLayersActiveMask == 0 && window == g.NavWindow)
5356                 RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
5357         }
5358         else
5359         {
5360             // Not navigable into
5361             ItemAdd(bb, 0);
5362         }
5363         if (g.HoveredWindow == window)
5364             parent_window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
5365     }
5366     g.WithinEndChild = false;
5367     g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
5368 }
5369 
5370 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)5371 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
5372 {
5373     ImGuiContext& g = *GImGui;
5374     const ImGuiStyle& style = g.Style;
5375     PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
5376     PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
5377     PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
5378     PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
5379     bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
5380     PopStyleVar(3);
5381     PopStyleColor();
5382     return ret;
5383 }
5384 
EndChildFrame()5385 void ImGui::EndChildFrame()
5386 {
5387     EndChild();
5388 }
5389 
SetWindowConditionAllowFlags(ImGuiWindow * window,ImGuiCond flags,bool enabled)5390 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
5391 {
5392     window->SetWindowPosAllowFlags       = enabled ? (window->SetWindowPosAllowFlags       | flags) : (window->SetWindowPosAllowFlags       & ~flags);
5393     window->SetWindowSizeAllowFlags      = enabled ? (window->SetWindowSizeAllowFlags      | flags) : (window->SetWindowSizeAllowFlags      & ~flags);
5394     window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
5395     window->SetWindowDockAllowFlags      = enabled ? (window->SetWindowDockAllowFlags      | flags) : (window->SetWindowDockAllowFlags      & ~flags);
5396 }
5397 
FindWindowByID(ImGuiID id)5398 ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
5399 {
5400     ImGuiContext& g = *GImGui;
5401     return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
5402 }
5403 
FindWindowByName(const char * name)5404 ImGuiWindow* ImGui::FindWindowByName(const char* name)
5405 {
5406     ImGuiID id = ImHashStr(name);
5407     return FindWindowByID(id);
5408 }
5409 
ApplyWindowSettings(ImGuiWindow * window,ImGuiWindowSettings * settings)5410 static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
5411 {
5412     const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
5413     window->ViewportPos = main_viewport->Pos;
5414     if (settings->ViewportId)
5415     {
5416         window->ViewportId = settings->ViewportId;
5417         window->ViewportPos = ImVec2(settings->ViewportPos.x, settings->ViewportPos.y);
5418     }
5419     window->Pos = ImFloor(ImVec2(settings->Pos.x + window->ViewportPos.x, settings->Pos.y + window->ViewportPos.y));
5420     if (settings->Size.x > 0 && settings->Size.y > 0)
5421         window->Size = window->SizeFull = ImFloor(ImVec2(settings->Size.x, settings->Size.y));
5422     window->Collapsed = settings->Collapsed;
5423     window->DockId = settings->DockId;
5424     window->DockOrder = settings->DockOrder;
5425 }
5426 
CreateNewWindow(const char * name,ImGuiWindowFlags flags)5427 static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags)
5428 {
5429     ImGuiContext& g = *GImGui;
5430     //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags);
5431 
5432     // Create window the first time
5433     ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
5434     window->Flags = flags;
5435     g.WindowsById.SetVoidPtr(window->ID, window);
5436 
5437     // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
5438     const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
5439     window->Pos = main_viewport->Pos + ImVec2(60, 60);
5440     window->ViewportPos = main_viewport->Pos;
5441 
5442     // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
5443     if (!(flags & ImGuiWindowFlags_NoSavedSettings))
5444         if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
5445         {
5446             // Retrieve settings from .ini file
5447             window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
5448             SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
5449             ApplyWindowSettings(window, settings);
5450         }
5451     window->DC.CursorStartPos = window->DC.CursorMaxPos = window->Pos; // So first call to CalcContentSize() doesn't return crazy values
5452 
5453     if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
5454     {
5455         window->AutoFitFramesX = window->AutoFitFramesY = 2;
5456         window->AutoFitOnlyGrows = false;
5457     }
5458     else
5459     {
5460         if (window->Size.x <= 0.0f)
5461             window->AutoFitFramesX = 2;
5462         if (window->Size.y <= 0.0f)
5463             window->AutoFitFramesY = 2;
5464         window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
5465     }
5466 
5467     if (!(flags & ImGuiWindowFlags_ChildWindow))
5468     {
5469         g.WindowsFocusOrder.push_back(window);
5470         window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1);
5471     }
5472 
5473     if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
5474         g.Windows.push_front(window); // Quite slow but rare and only once
5475     else
5476         g.Windows.push_back(window);
5477     return window;
5478 }
5479 
GetWindowForTitleDisplay(ImGuiWindow * window)5480 static ImGuiWindow* GetWindowForTitleDisplay(ImGuiWindow* window)
5481 {
5482     return window->DockNodeAsHost ? window->DockNodeAsHost->VisibleWindow : window;
5483 }
5484 
GetWindowForTitleAndMenuHeight(ImGuiWindow * window)5485 static ImGuiWindow* GetWindowForTitleAndMenuHeight(ImGuiWindow* window)
5486 {
5487     return (window->DockNodeAsHost && window->DockNodeAsHost->VisibleWindow) ? window->DockNodeAsHost->VisibleWindow : window;
5488 }
5489 
CalcWindowSizeAfterConstraint(ImGuiWindow * window,const ImVec2 & size_desired)5490 static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& size_desired)
5491 {
5492     ImGuiContext& g = *GImGui;
5493     ImVec2 new_size = size_desired;
5494     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)
5495     {
5496         // Using -1,-1 on either X/Y axis to preserve the current size.
5497         ImRect cr = g.NextWindowData.SizeConstraintRect;
5498         new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
5499         new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
5500         if (g.NextWindowData.SizeCallback)
5501         {
5502             ImGuiSizeCallbackData data;
5503             data.UserData = g.NextWindowData.SizeCallbackUserData;
5504             data.Pos = window->Pos;
5505             data.CurrentSize = window->SizeFull;
5506             data.DesiredSize = new_size;
5507             g.NextWindowData.SizeCallback(&data);
5508             new_size = data.DesiredSize;
5509         }
5510         new_size.x = IM_FLOOR(new_size.x);
5511         new_size.y = IM_FLOOR(new_size.y);
5512     }
5513 
5514     // Minimum size
5515     if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
5516     {
5517         ImGuiWindow* window_for_height = GetWindowForTitleAndMenuHeight(window);
5518         const float decoration_up_height = window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight();
5519         new_size = ImMax(new_size, g.Style.WindowMinSize);
5520         new_size.y = ImMax(new_size.y, decoration_up_height + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows
5521     }
5522     return new_size;
5523 }
5524 
CalcWindowContentSizes(ImGuiWindow * window,ImVec2 * content_size_current,ImVec2 * content_size_ideal)5525 static void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_current, ImVec2* content_size_ideal)
5526 {
5527     bool preserve_old_content_sizes = false;
5528     if (window->Collapsed && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5529         preserve_old_content_sizes = true;
5530     else if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0)
5531         preserve_old_content_sizes = true;
5532     if (preserve_old_content_sizes)
5533     {
5534         *content_size_current = window->ContentSize;
5535         *content_size_ideal = window->ContentSizeIdeal;
5536         return;
5537     }
5538 
5539     content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_FLOOR(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);
5540     content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_FLOOR(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);
5541     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);
5542     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);
5543 }
5544 
CalcWindowAutoFitSize(ImGuiWindow * window,const ImVec2 & size_contents)5545 static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents)
5546 {
5547     ImGuiContext& g = *GImGui;
5548     ImGuiStyle& style = g.Style;
5549     const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
5550     ImVec2 size_pad = window->WindowPadding * 2.0f;
5551     ImVec2 size_desired = size_contents + size_pad + ImVec2(0.0f, decoration_up_height);
5552     if (window->Flags & ImGuiWindowFlags_Tooltip)
5553     {
5554         // Tooltip always resize
5555         return size_desired;
5556     }
5557     else
5558     {
5559         // Maximum window size is determined by the viewport size or monitor size
5560         const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0;
5561         const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0;
5562         ImVec2 size_min = style.WindowMinSize;
5563         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)
5564             size_min = ImMin(size_min, ImVec2(4.0f, 4.0f));
5565 
5566         // FIXME-VIEWPORT-WORKAREA: May want to use GetWorkSize() instead of Size depending on the type of windows?
5567         ImVec2 avail_size = window->Viewport->Size;
5568         if (window->ViewportOwned)
5569             avail_size = ImVec2(FLT_MAX, FLT_MAX);
5570         const int monitor_idx = window->ViewportAllowPlatformMonitorExtend;
5571         if (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size)
5572             avail_size = g.PlatformIO.Monitors[monitor_idx].WorkSize;
5573         ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, avail_size - style.DisplaySafeAreaPadding * 2.0f));
5574 
5575         // When the window cannot fit all contents (either because of constraints, either because screen is too small),
5576         // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
5577         ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5578         bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - 0.0f                 < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar);
5579         bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - decoration_up_height < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar);
5580         if (will_have_scrollbar_x)
5581             size_auto_fit.y += style.ScrollbarSize;
5582         if (will_have_scrollbar_y)
5583             size_auto_fit.x += style.ScrollbarSize;
5584         return size_auto_fit;
5585     }
5586 }
5587 
CalcWindowNextAutoFitSize(ImGuiWindow * window)5588 ImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window)
5589 {
5590     ImVec2 size_contents_current;
5591     ImVec2 size_contents_ideal;
5592     CalcWindowContentSizes(window, &size_contents_current, &size_contents_ideal);
5593     ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents_ideal);
5594     ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5595     return size_final;
5596 }
5597 
GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)5598 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
5599 {
5600     if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
5601         return ImGuiCol_PopupBg;
5602     if (flags & ImGuiWindowFlags_ChildWindow)
5603         return ImGuiCol_ChildBg;
5604     return ImGuiCol_WindowBg;
5605 }
5606 
CalcResizePosSizeFromAnyCorner(ImGuiWindow * window,const ImVec2 & corner_target,const ImVec2 & corner_norm,ImVec2 * out_pos,ImVec2 * out_size)5607 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
5608 {
5609     ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm);                // Expected window upper-left
5610     ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
5611     ImVec2 size_expected = pos_max - pos_min;
5612     ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected);
5613     *out_pos = pos_min;
5614     if (corner_norm.x == 0.0f)
5615         out_pos->x -= (size_constrained.x - size_expected.x);
5616     if (corner_norm.y == 0.0f)
5617         out_pos->y -= (size_constrained.y - size_expected.y);
5618     *out_size = size_constrained;
5619 }
5620 
5621 // Data for resizing from corner
5622 struct ImGuiResizeGripDef
5623 {
5624     ImVec2  CornerPosN;
5625     ImVec2  InnerDir;
5626     int     AngleMin12, AngleMax12;
5627 };
5628 static const ImGuiResizeGripDef resize_grip_def[4] =
5629 {
5630     { ImVec2(1, 1), ImVec2(-1, -1), 0, 3 },  // Lower-right
5631     { ImVec2(0, 1), ImVec2(+1, -1), 3, 6 },  // Lower-left
5632     { ImVec2(0, 0), ImVec2(+1, +1), 6, 9 },  // Upper-left (Unused)
5633     { ImVec2(1, 0), ImVec2(-1, +1), 9, 12 }  // Upper-right (Unused)
5634 };
5635 
5636 // Data for resizing from borders
5637 struct ImGuiResizeBorderDef
5638 {
5639     ImVec2 InnerDir;
5640     ImVec2 SegmentN1, SegmentN2;
5641     float  OuterAngle;
5642 };
5643 static const ImGuiResizeBorderDef resize_border_def[4] =
5644 {
5645     { ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f }, // Left
5646     { ImVec2(-1, 0), ImVec2(1, 0), ImVec2(1, 1), IM_PI * 0.00f }, // Right
5647     { ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Up
5648     { ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f }  // Down
5649 };
5650 
GetResizeBorderRect(ImGuiWindow * window,int border_n,float perp_padding,float thickness)5651 static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
5652 {
5653     ImRect rect = window->Rect();
5654     if (thickness == 0.0f)
5655         rect.Max -= ImVec2(1, 1);
5656     if (border_n == ImGuiDir_Left)  { return ImRect(rect.Min.x - thickness,    rect.Min.y + perp_padding, rect.Min.x + thickness,    rect.Max.y - perp_padding); }
5657     if (border_n == ImGuiDir_Right) { return ImRect(rect.Max.x - thickness,    rect.Min.y + perp_padding, rect.Max.x + thickness,    rect.Max.y - perp_padding); }
5658     if (border_n == ImGuiDir_Up)    { return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness,    rect.Max.x - perp_padding, rect.Min.y + thickness);    }
5659     if (border_n == ImGuiDir_Down)  { return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness,    rect.Max.x - perp_padding, rect.Max.y + thickness);    }
5660     IM_ASSERT(0);
5661     return ImRect();
5662 }
5663 
5664 // 0..3: corners (Lower-right, Lower-left, Unused, Unused)
GetWindowResizeCornerID(ImGuiWindow * window,int n)5665 ImGuiID ImGui::GetWindowResizeCornerID(ImGuiWindow* window, int n)
5666 {
5667     IM_ASSERT(n >= 0 && n < 4);
5668     ImGuiID id = window->DockIsActive ? window->DockNode->HostWindow->ID : window->ID;
5669     id = ImHashStr("#RESIZE", 0, id);
5670     id = ImHashData(&n, sizeof(int), id);
5671     return id;
5672 }
5673 
5674 // Borders (Left, Right, Up, Down)
GetWindowResizeBorderID(ImGuiWindow * window,ImGuiDir dir)5675 ImGuiID ImGui::GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir)
5676 {
5677     IM_ASSERT(dir >= 0 && dir < 4);
5678     int n = (int)dir + 4;
5679     ImGuiID id = window->DockIsActive ? window->DockNode->HostWindow->ID : window->ID;
5680     id = ImHashStr("#RESIZE", 0, id);
5681     id = ImHashData(&n, sizeof(int), id);
5682     return id;
5683 }
5684 
5685 // Handle resize for: Resize Grips, Borders, Gamepad
5686 // 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)5687 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)
5688 {
5689     ImGuiContext& g = *GImGui;
5690     ImGuiWindowFlags flags = window->Flags;
5691 
5692     if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
5693         return false;
5694     if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window.
5695         return false;
5696 
5697     bool ret_auto_fit = false;
5698     const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0;
5699     const float grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
5700     const float grip_hover_inner_size = IM_FLOOR(grip_draw_size * 0.75f);
5701     const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_HOVER_PADDING : 0.0f;
5702 
5703     ImVec2 pos_target(FLT_MAX, FLT_MAX);
5704     ImVec2 size_target(FLT_MAX, FLT_MAX);
5705 
5706     // Clip mouse interaction rectangles within the viewport rectangle (in practice the narrowing is going to happen most of the time).
5707     // - Not narrowing would mostly benefit the situation where OS windows _without_ decoration have a threshold for hovering when outside their limits.
5708     //   This is however not the case with current backends under Win32, but a custom borderless window implementation would benefit from it.
5709     // - 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.
5710     // - Note that we are unable to tell if the platform setup allows hovering with a distance threshold (on Win32, decorated window have such threshold).
5711     // We only clip interaction so we overwrite window->ClipRect, cannot call PushClipRect() yet as DrawList is not yet setup.
5712     const bool clip_with_viewport_rect = !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) || (g.IO.MouseHoveredViewport != window->ViewportId) || !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration);
5713     if (clip_with_viewport_rect)
5714         window->ClipRect = window->Viewport->GetMainRect();
5715 
5716     // Resize grips and borders are on layer 1
5717     window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5718 
5719     // Manual resize grips
5720     PushID("#RESIZE");
5721     for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5722     {
5723         const ImGuiResizeGripDef& def = resize_grip_def[resize_grip_n];
5724         const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, def.CornerPosN);
5725 
5726         // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
5727         bool hovered, held;
5728         ImRect resize_rect(corner - def.InnerDir * grip_hover_outer_size, corner + def.InnerDir * grip_hover_inner_size);
5729         if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
5730         if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
5731         ImGuiID resize_grip_id = window->GetID(resize_grip_n); // == GetWindowResizeCornerID()
5732         ButtonBehavior(resize_rect, resize_grip_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
5733         //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
5734         if (hovered || held)
5735             g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
5736 
5737         if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
5738         {
5739             // Manual auto-fit when double-clicking
5740             size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5741             ret_auto_fit = true;
5742             ClearActiveID();
5743         }
5744         else if (held)
5745         {
5746             // Resize from any of the four corners
5747             // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
5748             ImVec2 clamp_min = ImVec2(def.CornerPosN.x == 1.0f ? visibility_rect.Min.x : -FLT_MAX, def.CornerPosN.y == 1.0f ? visibility_rect.Min.y : -FLT_MAX);
5749             ImVec2 clamp_max = ImVec2(def.CornerPosN.x == 0.0f ? visibility_rect.Max.x : +FLT_MAX, def.CornerPosN.y == 0.0f ? visibility_rect.Max.y : +FLT_MAX);
5750             ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(def.InnerDir * grip_hover_outer_size, def.InnerDir * -grip_hover_inner_size, def.CornerPosN); // Corner of the window corresponding to our corner grip
5751             corner_target = ImClamp(corner_target, clamp_min, clamp_max);
5752             CalcResizePosSizeFromAnyCorner(window, corner_target, def.CornerPosN, &pos_target, &size_target);
5753         }
5754 
5755         // Only lower-left grip is visible before hovering/activating
5756         if (resize_grip_n == 0 || held || hovered)
5757             resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
5758     }
5759     for (int border_n = 0; border_n < resize_border_count; border_n++)
5760     {
5761         const ImGuiResizeBorderDef& def = resize_border_def[border_n];
5762         const ImGuiAxis axis = (border_n == ImGuiDir_Left || border_n == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
5763 
5764         bool hovered, held;
5765         ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_HOVER_PADDING);
5766         ImGuiID border_id = window->GetID(border_n + 4); // == GetWindowResizeBorderID()
5767         ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren);
5768         //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
5769         if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held)
5770         {
5771             g.MouseCursor = (axis == ImGuiAxis_X) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
5772             if (held)
5773                 *border_held = border_n;
5774         }
5775         if (held)
5776         {
5777             ImVec2 clamp_min(border_n == ImGuiDir_Right ? visibility_rect.Min.x : -FLT_MAX, border_n == ImGuiDir_Down ? visibility_rect.Min.y : -FLT_MAX);
5778             ImVec2 clamp_max(border_n == ImGuiDir_Left  ? visibility_rect.Max.x : +FLT_MAX, border_n == ImGuiDir_Up   ? visibility_rect.Max.y : +FLT_MAX);
5779             ImVec2 border_target = window->Pos;
5780             border_target[axis] = g.IO.MousePos[axis] - g.ActiveIdClickOffset[axis] + WINDOWS_HOVER_PADDING;
5781             border_target = ImClamp(border_target, clamp_min, clamp_max);
5782             CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2), &pos_target, &size_target);
5783         }
5784     }
5785     PopID();
5786 
5787     // Restore nav layer
5788     window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5789 
5790     // Navigation resize (keyboard/gamepad)
5791     if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindowDockTree == window)
5792     {
5793         ImVec2 nav_resize_delta;
5794         if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift)
5795             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
5796         if (g.NavInputSource == ImGuiInputSource_Gamepad)
5797             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down);
5798         if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f)
5799         {
5800             const float NAV_RESIZE_SPEED = 600.0f;
5801             nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
5802             nav_resize_delta = ImMax(nav_resize_delta, visibility_rect.Min - window->Pos - window->Size);
5803             g.NavWindowingToggleLayer = false;
5804             g.NavDisableMouseHover = true;
5805             resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
5806             // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
5807             size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + nav_resize_delta);
5808         }
5809     }
5810 
5811     // Apply back modified position/size to window
5812     if (size_target.x != FLT_MAX)
5813     {
5814         window->SizeFull = size_target;
5815         MarkIniSettingsDirty(window);
5816     }
5817     if (pos_target.x != FLT_MAX)
5818     {
5819         window->Pos = ImFloor(pos_target);
5820         MarkIniSettingsDirty(window);
5821     }
5822 
5823     window->Size = window->SizeFull;
5824     return ret_auto_fit;
5825 }
5826 
ClampWindowRect(ImGuiWindow * window,const ImRect & visibility_rect)5827 static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& visibility_rect)
5828 {
5829     ImGuiContext& g = *GImGui;
5830     ImVec2 size_for_clamping = window->Size;
5831     if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
5832         size_for_clamping.y = window->TitleBarHeight();
5833     window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max);
5834 }
5835 
RenderWindowOuterBorders(ImGuiWindow * window)5836 static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
5837 {
5838     ImGuiContext& g = *GImGui;
5839     float rounding = window->WindowRounding;
5840     float border_size = window->WindowBorderSize;
5841     if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground))
5842         window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
5843 
5844     int border_held = window->ResizeBorderHeld;
5845     if (border_held != -1)
5846     {
5847         const ImGuiResizeBorderDef& def = resize_border_def[border_held];
5848         ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f);
5849         window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI * 0.25f, def.OuterAngle);
5850         window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f);
5851         window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), 0, ImMax(2.0f, border_size)); // Thicker than usual
5852     }
5853     if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
5854     {
5855         float y = window->Pos.y + window->TitleBarHeight() - 1;
5856         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);
5857     }
5858 }
5859 
5860 // Draw background and borders
5861 // 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)5862 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)
5863 {
5864     ImGuiContext& g = *GImGui;
5865     ImGuiStyle& style = g.Style;
5866     ImGuiWindowFlags flags = window->Flags;
5867 
5868     // Ensure that ScrollBar doesn't read last frame's SkipItems
5869     IM_ASSERT(window->BeginCount == 0);
5870     window->SkipItems = false;
5871 
5872     // Draw window + handle manual resize
5873     // 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.
5874     const float window_rounding = window->WindowRounding;
5875     const float window_border_size = window->WindowBorderSize;
5876     if (window->Collapsed)
5877     {
5878         // Title bar only
5879         float backup_border_size = style.FrameBorderSize;
5880         g.Style.FrameBorderSize = window->WindowBorderSize;
5881         ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
5882         RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
5883         g.Style.FrameBorderSize = backup_border_size;
5884     }
5885     else
5886     {
5887         // Window background
5888         if (!(flags & ImGuiWindowFlags_NoBackground))
5889         {
5890             bool is_docking_transparent_payload = false;
5891             if (g.DragDropActive && (g.FrameCount - g.DragDropAcceptFrameCount) <= 1 && g.IO.ConfigDockingTransparentPayload)
5892                 if (g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) && *(ImGuiWindow**)g.DragDropPayload.Data == window)
5893                     is_docking_transparent_payload = true;
5894 
5895             ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
5896             if (window->ViewportOwned)
5897             {
5898                 // No alpha
5899                 bg_col = (bg_col | IM_COL32_A_MASK);
5900                 if (is_docking_transparent_payload)
5901                     window->Viewport->Alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA;
5902             }
5903             else
5904             {
5905                 // Adjust alpha. For docking
5906                 bool override_alpha = false;
5907                 float alpha = 1.0f;
5908                 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha)
5909                 {
5910                     alpha = g.NextWindowData.BgAlphaVal;
5911                     override_alpha = true;
5912                 }
5913                 if (is_docking_transparent_payload)
5914                 {
5915                     alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA; // FIXME-DOCK: Should that be an override?
5916                     override_alpha = true;
5917                 }
5918                 if (override_alpha)
5919                     bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
5920             }
5921             window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom);
5922         }
5923 
5924         // Title bar
5925         // (when docked, DockNode are drawing their own title bar. Individual windows however do NOT set the _NoTitleBar flag,
5926         // in order for their pos/size to be matching their undocking state.)
5927         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
5928         {
5929             ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
5930             window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawFlags_RoundCornersTop);
5931         }
5932 
5933         // Menu bar
5934         if (flags & ImGuiWindowFlags_MenuBar)
5935         {
5936             ImRect menu_bar_rect = window->MenuBarRect();
5937             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.
5938             window->DrawList->AddRectFilled(menu_bar_rect.Min + ImVec2(window_border_size, 0), menu_bar_rect.Max - ImVec2(window_border_size, 0), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawFlags_RoundCornersTop);
5939             if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
5940                 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
5941         }
5942 
5943         // Docking: Unhide tab bar (small triangle in the corner), drag from small triangle to quickly undock
5944         ImGuiDockNode* node = window->DockNode;
5945         if (window->DockIsActive && node->IsHiddenTabBar() && !node->IsNoTabBar())
5946         {
5947             float unhide_sz_draw = ImFloor(g.FontSize * 0.70f);
5948             float unhide_sz_hit = ImFloor(g.FontSize * 0.55f);
5949             ImVec2 p = node->Pos;
5950             ImRect r(p, p + ImVec2(unhide_sz_hit, unhide_sz_hit));
5951             bool hovered, held;
5952             if (ButtonBehavior(r, window->GetID("#UNHIDE"), &hovered, &held, ImGuiButtonFlags_FlattenChildren))
5953                 node->WantHiddenTabBarToggle = true;
5954             else if (held && IsMouseDragging(0))
5955                 StartMouseMovingWindowOrNode(window, node, true);
5956 
5957             // FIXME-DOCK: Ideally we'd use ImGuiCol_TitleBgActive/ImGuiCol_TitleBg here, but neither is guaranteed to be visible enough at this sort of size..
5958             ImU32 col = GetColorU32(((held && hovered) || (node->IsFocused && !hovered)) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
5959             window->DrawList->AddTriangleFilled(p, p + ImVec2(unhide_sz_draw, 0.0f), p + ImVec2(0.0f, unhide_sz_draw), col);
5960         }
5961 
5962         // Scrollbars
5963         if (window->ScrollbarX)
5964             Scrollbar(ImGuiAxis_X);
5965         if (window->ScrollbarY)
5966             Scrollbar(ImGuiAxis_Y);
5967 
5968         // Render resize grips (after their input handling so we don't have a frame of latency)
5969         if (handle_borders_and_resize_grips && !(flags & ImGuiWindowFlags_NoResize))
5970         {
5971             for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5972             {
5973                 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5974                 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
5975                 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)));
5976                 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)));
5977                 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);
5978                 window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]);
5979             }
5980         }
5981 
5982         // Borders (for dock node host they will be rendered over after the tab bar)
5983         if (handle_borders_and_resize_grips && !window->DockNodeAsHost)
5984             RenderWindowOuterBorders(window);
5985     }
5986 }
5987 
5988 // Render title text, collapse button, close button
5989 // When inside a dock node, this is handled in DockNodeCalcTabBarLayout() instead.
RenderWindowTitleBarContents(ImGuiWindow * window,const ImRect & title_bar_rect,const char * name,bool * p_open)5990 void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)
5991 {
5992     ImGuiContext& g = *GImGui;
5993     ImGuiStyle& style = g.Style;
5994     ImGuiWindowFlags flags = window->Flags;
5995 
5996     const bool has_close_button = (p_open != NULL);
5997     const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None);
5998 
5999     // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)
6000     const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags;
6001     g.CurrentItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
6002     window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
6003 
6004     // Layout buttons
6005     // FIXME: Would be nice to generalize the subtleties expressed here into reusable code.
6006     float pad_l = style.FramePadding.x;
6007     float pad_r = style.FramePadding.x;
6008     float button_sz = g.FontSize;
6009     ImVec2 close_button_pos;
6010     ImVec2 collapse_button_pos;
6011     if (has_close_button)
6012     {
6013         pad_r += button_sz;
6014         close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
6015     }
6016     if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)
6017     {
6018         pad_r += button_sz;
6019         collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
6020     }
6021     if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)
6022     {
6023         collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l - style.FramePadding.x, title_bar_rect.Min.y);
6024         pad_l += button_sz;
6025     }
6026 
6027     // Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
6028     if (has_collapse_button)
6029         if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos, NULL))
6030             window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function
6031 
6032     // Close button
6033     if (has_close_button)
6034         if (CloseButton(window->GetID("#CLOSE"), close_button_pos))
6035             *p_open = false;
6036 
6037     window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
6038     g.CurrentItemFlags = item_flags_backup;
6039 
6040     // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
6041     // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
6042     const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? button_sz * 0.80f : 0.0f;
6043     const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
6044 
6045     // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
6046     // while uncentered title text will still reach edges correctly.
6047     if (pad_l > style.FramePadding.x)
6048         pad_l += g.Style.ItemInnerSpacing.x;
6049     if (pad_r > style.FramePadding.x)
6050         pad_r += g.Style.ItemInnerSpacing.x;
6051     if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f)
6052     {
6053         float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center
6054         float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x);
6055         pad_l = ImMax(pad_l, pad_extend * centerness);
6056         pad_r = ImMax(pad_r, pad_extend * centerness);
6057     }
6058 
6059     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);
6060     ImRect clip_r(layout_r.Min.x, layout_r.Min.y, ImMin(layout_r.Max.x + g.Style.ItemInnerSpacing.x, title_bar_rect.Max.x), layout_r.Max.y);
6061     if (flags & ImGuiWindowFlags_UnsavedDocument)
6062     {
6063         ImVec2 marker_pos;
6064         marker_pos.x = ImClamp(layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x + text_size.x, layout_r.Min.x, layout_r.Max.x);
6065         marker_pos.y = (layout_r.Min.y + layout_r.Max.y) * 0.5f;
6066         if (marker_pos.x > layout_r.Min.x)
6067         {
6068             RenderBullet(window->DrawList, marker_pos, GetColorU32(ImGuiCol_Text));
6069             clip_r.Max.x = ImMin(clip_r.Max.x, marker_pos.x - (int)(marker_size_x * 0.5f));
6070         }
6071     }
6072     //if (g.IO.KeyShift) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
6073     //if (g.IO.KeyCtrl) window->DrawList->AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
6074     RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r);
6075 }
6076 
UpdateWindowParentAndRootLinks(ImGuiWindow * window,ImGuiWindowFlags flags,ImGuiWindow * parent_window)6077 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
6078 {
6079     window->ParentWindow = parent_window;
6080     window->RootWindow = window->RootWindowDockTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
6081     if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
6082     {
6083         window->RootWindowDockTree = parent_window->RootWindowDockTree;
6084         if (!window->DockIsActive && !(parent_window->Flags & ImGuiWindowFlags_DockNodeHost))
6085             window->RootWindow = parent_window->RootWindow;
6086     }
6087     if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
6088         window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
6089     while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
6090     {
6091         IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);
6092         window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
6093     }
6094 }
6095 
6096 // Push a new Dear ImGui window to add widgets to.
6097 // - 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.
6098 // - Begin/End can be called multiple times during the frame with the same window name to append content.
6099 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
6100 //   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.
6101 // - 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.
6102 // - 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)6103 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
6104 {
6105     ImGuiContext& g = *GImGui;
6106     const ImGuiStyle& style = g.Style;
6107     IM_ASSERT(name != NULL && name[0] != '\0');     // Window name required
6108     IM_ASSERT(g.WithinFrameScope);                  // Forgot to call ImGui::NewFrame()
6109     IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
6110 
6111     // Find or create
6112     ImGuiWindow* window = FindWindowByName(name);
6113     const bool window_just_created = (window == NULL);
6114     if (window_just_created)
6115         window = CreateNewWindow(name, flags);
6116 
6117     // Automatically disable manual moving/resizing when NoInputs is set
6118     if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
6119         flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
6120 
6121     if (flags & ImGuiWindowFlags_NavFlattened)
6122         IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
6123 
6124     const int current_frame = g.FrameCount;
6125     const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
6126     window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow);
6127 
6128     // Update the Appearing flag (note: the BeginDocked() path may also set this to true later)
6129     bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
6130     if (flags & ImGuiWindowFlags_Popup)
6131     {
6132         ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
6133         window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
6134         window_just_activated_by_user |= (window != popup_ref.Window);
6135     }
6136 
6137     // Update Flags, LastFrameActive, BeginOrderXXX fields
6138     const bool window_was_appearing = window->Appearing;
6139     if (first_begin_of_the_frame)
6140     {
6141         window->Appearing = window_just_activated_by_user;
6142         if (window->Appearing)
6143             SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
6144 
6145         window->FlagsPreviousFrame = window->Flags;
6146         window->Flags = (ImGuiWindowFlags)flags;
6147         window->LastFrameActive = current_frame;
6148         window->LastTimeActive = (float)g.Time;
6149         window->BeginOrderWithinParent = 0;
6150         window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
6151     }
6152     else
6153     {
6154         flags = window->Flags;
6155     }
6156 
6157     // Docking
6158     // (NB: during the frame dock nodes are created, it is possible that (window->DockIsActive == false) even though (window->DockNode->Windows.Size > 1)
6159     IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL); // Cannot be both
6160     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasDock)
6161         SetWindowDock(window, g.NextWindowData.DockId, g.NextWindowData.DockCond);
6162     if (first_begin_of_the_frame)
6163     {
6164         bool has_dock_node = (window->DockId != 0 || window->DockNode != NULL);
6165         bool new_auto_dock_node = !has_dock_node && GetWindowAlwaysWantOwnTabBar(window);
6166         bool dock_node_was_visible = window->DockNodeIsVisible;
6167         bool dock_tab_was_visible = window->DockTabIsVisible;
6168         if (has_dock_node || new_auto_dock_node)
6169         {
6170             BeginDocked(window, p_open);
6171             flags = window->Flags;
6172             if (window->DockIsActive)
6173                 IM_ASSERT(window->DockNode != NULL);
6174 
6175             // Docking currently override constraints
6176             g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint;
6177 
6178             // Amend the Appearing flag
6179             if (window->DockTabIsVisible && !dock_tab_was_visible && dock_node_was_visible && !window->Appearing && !window_was_appearing)
6180             {
6181                 window->Appearing = true;
6182                 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
6183             }
6184         }
6185         else
6186         {
6187             window->DockIsActive = window->DockNodeIsVisible = window->DockTabIsVisible = false;
6188         }
6189     }
6190 
6191     // 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
6192     ImGuiWindow* parent_window_in_stack = window->DockIsActive ? window->DockNode->HostWindow : g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
6193     ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
6194     IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
6195 
6196     // We allow window memory to be compacted so recreate the base stack when needed.
6197     if (window->IDStack.Size == 0)
6198         window->IDStack.push_back(window->ID);
6199 
6200     // Add to stack
6201     // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
6202     g.CurrentWindowStack.push_back(window);
6203     g.CurrentWindow = window;
6204     window->DC.StackSizesOnBegin.SetToCurrentState();
6205     g.CurrentWindow = NULL;
6206 
6207     if (flags & ImGuiWindowFlags_Popup)
6208     {
6209         ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
6210         popup_ref.Window = window;
6211         g.BeginPopupStack.push_back(popup_ref);
6212         window->PopupId = popup_ref.PopupId;
6213     }
6214 
6215     // Update ->RootWindow and others pointers (before any possible call to FocusWindow)
6216     if (first_begin_of_the_frame)
6217         UpdateWindowParentAndRootLinks(window, flags, parent_window);
6218 
6219     // Process SetNextWindow***() calls
6220     // (FIXME: Consider splitting the HasXXX flags into X/Y components
6221     bool window_pos_set_by_api = false;
6222     bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
6223     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos)
6224     {
6225         window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
6226         if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
6227         {
6228             // May be processed on the next frame if this is our first frame and we are measuring size
6229             // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
6230             window->SetWindowPosVal = g.NextWindowData.PosVal;
6231             window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
6232             window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6233         }
6234         else
6235         {
6236             SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
6237         }
6238     }
6239     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)
6240     {
6241         window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
6242         window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
6243         SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
6244     }
6245     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasScroll)
6246     {
6247         if (g.NextWindowData.ScrollVal.x >= 0.0f)
6248         {
6249             window->ScrollTarget.x = g.NextWindowData.ScrollVal.x;
6250             window->ScrollTargetCenterRatio.x = 0.0f;
6251         }
6252         if (g.NextWindowData.ScrollVal.y >= 0.0f)
6253         {
6254             window->ScrollTarget.y = g.NextWindowData.ScrollVal.y;
6255             window->ScrollTargetCenterRatio.y = 0.0f;
6256         }
6257     }
6258     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize)
6259         window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;
6260     else if (first_begin_of_the_frame)
6261         window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);
6262     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasWindowClass)
6263         window->WindowClass = g.NextWindowData.WindowClass;
6264     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed)
6265         SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
6266     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus)
6267         FocusWindow(window);
6268     if (window->Appearing)
6269         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
6270 
6271     // When reusing window again multiple times a frame, just append content (don't need to setup again)
6272     if (first_begin_of_the_frame)
6273     {
6274         // Initialize
6275         const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
6276         window->Active = true;
6277         window->HasCloseButton = (p_open != NULL);
6278         window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX);
6279         window->IDStack.resize(1);
6280         window->DrawList->_ResetForNewFrame();
6281         window->DC.CurrentTableIdx = -1;
6282 
6283         // Restore buffer capacity when woken from a compacted state, to avoid
6284         if (window->MemoryCompacted)
6285             GcAwakeTransientWindowBuffers(window);
6286 
6287         // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
6288         // 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.
6289         bool window_title_visible_elsewhere = false;
6290         if ((window->Viewport && window->Viewport->Window == window) || (window->DockIsActive))
6291             window_title_visible_elsewhere = true;
6292         else if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0)   // Window titles visible when using CTRL+TAB
6293             window_title_visible_elsewhere = true;
6294         if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
6295         {
6296             size_t buf_len = (size_t)window->NameBufLen;
6297             window->Name = ImStrdupcpy(window->Name, &buf_len, name);
6298             window->NameBufLen = (int)buf_len;
6299         }
6300 
6301         // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
6302 
6303         // Update contents size from last frame for auto-fitting (or use explicit size)
6304         const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);
6305         CalcWindowContentSizes(window, &window->ContentSize, &window->ContentSizeIdeal);
6306 
6307         // FIXME: These flags are decremented before they are used. This means that in order to have these fields produce their intended behaviors
6308         // for one frame we must set them to at least 2, which is counter-intuitive. HiddenFramesCannotSkipItems is a more complicated case because
6309         // it has a single usage before this code block and may be set below before it is finally checked.
6310         if (window->HiddenFramesCanSkipItems > 0)
6311             window->HiddenFramesCanSkipItems--;
6312         if (window->HiddenFramesCannotSkipItems > 0)
6313             window->HiddenFramesCannotSkipItems--;
6314         if (window->HiddenFramesForRenderOnly > 0)
6315             window->HiddenFramesForRenderOnly--;
6316 
6317         // Hide new windows for one frame until they calculate their size
6318         if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
6319             window->HiddenFramesCannotSkipItems = 1;
6320 
6321         // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
6322         // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
6323         if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
6324         {
6325             window->HiddenFramesCannotSkipItems = 1;
6326             if (flags & ImGuiWindowFlags_AlwaysAutoResize)
6327             {
6328                 if (!window_size_x_set_by_api)
6329                     window->Size.x = window->SizeFull.x = 0.f;
6330                 if (!window_size_y_set_by_api)
6331                     window->Size.y = window->SizeFull.y = 0.f;
6332                 window->ContentSize = window->ContentSizeIdeal = ImVec2(0.f, 0.f);
6333             }
6334         }
6335 
6336         // SELECT VIEWPORT
6337         // We need to do this before using any style/font sizes, as viewport with a different DPI may affect font sizes.
6338 
6339         WindowSelectViewport(window);
6340         SetCurrentViewport(window, window->Viewport);
6341         window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f;
6342         SetCurrentWindow(window);
6343         flags = window->Flags;
6344 
6345         // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)
6346         // We read Style data after the call to UpdateSelectWindowViewport() which might be swapping the style.
6347 
6348         if (flags & ImGuiWindowFlags_ChildWindow)
6349             window->WindowBorderSize = style.ChildBorderSize;
6350         else
6351             window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
6352         if (!window->DockIsActive && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
6353             window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
6354         else
6355             window->WindowPadding = style.WindowPadding;
6356 
6357         // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size.
6358         window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
6359         window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
6360 
6361         // Collapse window by double-clicking on title bar
6362         // 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
6363         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse) && !window->DockIsActive)
6364         {
6365             // 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.
6366             ImRect title_bar_rect = window->TitleBarRect();
6367             if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
6368                 window->WantCollapseToggle = true;
6369             if (window->WantCollapseToggle)
6370             {
6371                 window->Collapsed = !window->Collapsed;
6372                 MarkIniSettingsDirty(window);
6373             }
6374         }
6375         else
6376         {
6377             window->Collapsed = false;
6378         }
6379         window->WantCollapseToggle = false;
6380 
6381         // SIZE
6382 
6383         // Calculate auto-fit size, handle automatic resize
6384         const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal);
6385         bool use_current_size_for_scrollbar_x = window_just_created;
6386         bool use_current_size_for_scrollbar_y = window_just_created;
6387         if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
6388         {
6389             // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
6390             if (!window_size_x_set_by_api)
6391             {
6392                 window->SizeFull.x = size_auto_fit.x;
6393                 use_current_size_for_scrollbar_x = true;
6394             }
6395             if (!window_size_y_set_by_api)
6396             {
6397                 window->SizeFull.y = size_auto_fit.y;
6398                 use_current_size_for_scrollbar_y = true;
6399             }
6400         }
6401         else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
6402         {
6403             // Auto-fit may only grow window during the first few frames
6404             // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
6405             if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
6406             {
6407                 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
6408                 use_current_size_for_scrollbar_x = true;
6409             }
6410             if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
6411             {
6412                 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
6413                 use_current_size_for_scrollbar_y = true;
6414             }
6415             if (!window->Collapsed)
6416                 MarkIniSettingsDirty(window);
6417         }
6418 
6419         // Apply minimum/maximum window size constraints and final size
6420         window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull);
6421         window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
6422 
6423         // Decoration size
6424         const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
6425 
6426         // POSITION
6427 
6428         // Popup latch its initial position, will position itself when it appears next frame
6429         if (window_just_activated_by_user)
6430         {
6431             window->AutoPosLastDirection = ImGuiDir_None;
6432             if ((flags & ImGuiWindowFlags_Popup) != 0 && !(flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api) // FIXME: BeginPopup() could use SetNextWindowPos()
6433                 window->Pos = g.BeginPopupStack.back().OpenPopupPos;
6434         }
6435 
6436         // Position child window
6437         if (flags & ImGuiWindowFlags_ChildWindow)
6438         {
6439             IM_ASSERT(parent_window && parent_window->Active);
6440             window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
6441             parent_window->DC.ChildWindows.push_back(window);
6442             if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
6443                 window->Pos = parent_window->DC.CursorPos;
6444         }
6445 
6446         const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0);
6447         if (window_pos_with_pivot)
6448             SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering)
6449         else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
6450             window->Pos = FindBestWindowPosForPopup(window);
6451         else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
6452             window->Pos = FindBestWindowPosForPopup(window);
6453         else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
6454             window->Pos = FindBestWindowPosForPopup(window);
6455 
6456         // Late create viewport if we don't fit within our current host viewport.
6457         if (window->ViewportAllowPlatformMonitorExtend >= 0 && !window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_Minimized))
6458             if (!window->Viewport->GetMainRect().Contains(window->Rect()))
6459             {
6460                 // This is based on the assumption that the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport)
6461                 //ImGuiViewport* old_viewport = window->Viewport;
6462                 window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing);
6463 
6464                 // FIXME-DPI
6465                 //IM_ASSERT(old_viewport->DpiScale == window->Viewport->DpiScale); // FIXME-DPI: Something went wrong
6466                 SetCurrentViewport(window, window->Viewport);
6467                 window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f;
6468                 SetCurrentWindow(window);
6469             }
6470 
6471         if (window->ViewportOwned)
6472             WindowSyncOwnedViewport(window, parent_window_in_stack);
6473 
6474         // Calculate the range of allowed position for that window (to be movable and visible past safe area padding)
6475         // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect.
6476         ImRect viewport_rect(window->Viewport->GetMainRect());
6477         ImRect viewport_work_rect(window->Viewport->GetWorkRect());
6478         ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
6479         ImRect visibility_rect(viewport_work_rect.Min + visibility_padding, viewport_work_rect.Max - visibility_padding);
6480 
6481         // Clamp position/size so window stays visible within its viewport or monitor
6482         // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
6483         // FIXME: Similar to code in GetWindowAllowedExtentRect()
6484         if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
6485         {
6486             if (!window->ViewportOwned && viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f)
6487             {
6488                 ClampWindowRect(window, visibility_rect);
6489             }
6490             else if (window->ViewportOwned && g.PlatformIO.Monitors.Size > 0)
6491             {
6492                 // Lost windows (e.g. a monitor disconnected) will naturally moved to the fallback/dummy monitor aka the main viewport.
6493                 const ImGuiPlatformMonitor* monitor = GetViewportPlatformMonitor(window->Viewport);
6494                 visibility_rect.Min = monitor->WorkPos + visibility_padding;
6495                 visibility_rect.Max = monitor->WorkPos + monitor->WorkSize - visibility_padding;
6496                 ClampWindowRect(window, visibility_rect);
6497             }
6498         }
6499         window->Pos = ImFloor(window->Pos);
6500 
6501         // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
6502         // Large values tend to lead to variety of artifacts and are not recommended.
6503         if (window->ViewportOwned || window->DockIsActive)
6504             window->WindowRounding = 0.0f;
6505         else
6506             window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
6507 
6508         // For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts.
6509         //if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar))
6510         //    window->WindowRounding = ImMin(window->WindowRounding, g.FontSize + style.FramePadding.y * 2.0f);
6511 
6512         // Apply window focus (new and reactivated windows are moved to front)
6513         bool want_focus = false;
6514         if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
6515         {
6516             if (flags & ImGuiWindowFlags_Popup)
6517                 want_focus = true;
6518             else if ((window->DockIsActive || (flags & ImGuiWindowFlags_ChildWindow) == 0) && !(flags & ImGuiWindowFlags_Tooltip))
6519                 want_focus = true;
6520         }
6521 
6522         // Decide if we are going to handle borders and resize grips
6523         const bool handle_borders_and_resize_grips = (window->DockNodeAsHost || !window->DockIsActive);
6524 
6525         // Handle manual resize: Resize Grips, Borders, Gamepad
6526         int border_held = -1;
6527         ImU32 resize_grip_col[4] = {};
6528         const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it.
6529         const float resize_grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
6530         if (handle_borders_and_resize_grips && !window->Collapsed)
6531             if (UpdateWindowManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect))
6532                 use_current_size_for_scrollbar_x = use_current_size_for_scrollbar_y = true;
6533         window->ResizeBorderHeld = (signed char)border_held;
6534 
6535         // Synchronize window --> viewport again and one last time (clamping and manual resize may have affected either)
6536         if (window->ViewportOwned)
6537         {
6538             if (!window->Viewport->PlatformRequestMove)
6539                 window->Viewport->Pos = window->Pos;
6540             if (!window->Viewport->PlatformRequestResize)
6541                 window->Viewport->Size = window->Size;
6542             window->Viewport->UpdateWorkRect();
6543             viewport_rect = window->Viewport->GetMainRect();
6544         }
6545 
6546         // Save last known viewport position within the window itself (so it can be saved in .ini file and restored)
6547         window->ViewportPos = window->Viewport->Pos;
6548 
6549         // SCROLLBAR VISIBILITY
6550 
6551         // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).
6552         if (!window->Collapsed)
6553         {
6554             // When reading the current size we need to read it after size constraints have been applied.
6555             // When we use InnerRect here we are intentionally reading last frame size, same for ScrollbarSizes values before we set them again.
6556             ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - decoration_up_height);
6557             ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + window->ScrollbarSizes;
6558             ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;
6559             float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;
6560             float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;
6561             //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?
6562             window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
6563             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));
6564             if (window->ScrollbarX && !window->ScrollbarY)
6565                 window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar);
6566             window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
6567         }
6568 
6569         // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)
6570         // Update various regions. Variables they depends on should be set above in this function.
6571         // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.
6572 
6573         // Outer rectangle
6574         // Not affected by window border size. Used by:
6575         // - FindHoveredWindow() (w/ extra padding when border resize is enabled)
6576         // - Begin() initial clipping rect for drawing window background and borders.
6577         // - Begin() clipping whole child
6578         const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;
6579         const ImRect outer_rect = window->Rect();
6580         const ImRect title_bar_rect = window->TitleBarRect();
6581         window->OuterRectClipped = outer_rect;
6582         if (window->DockIsActive)
6583             window->OuterRectClipped.Min.y += window->TitleBarHeight();
6584         window->OuterRectClipped.ClipWith(host_rect);
6585 
6586         // Inner rectangle
6587         // Not affected by window border size. Used by:
6588         // - InnerClipRect
6589         // - ScrollToBringRectIntoView()
6590         // - NavUpdatePageUpPageDown()
6591         // - Scrollbar()
6592         window->InnerRect.Min.x = window->Pos.x;
6593         window->InnerRect.Min.y = window->Pos.y + decoration_up_height;
6594         window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x;
6595         window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y;
6596 
6597         // Inner clipping rectangle.
6598         // Will extend a little bit outside the normal work region.
6599         // This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
6600         // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
6601         // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
6602         // Affected by window/frame border size. Used by:
6603         // - Begin() initial clip rect
6604         float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
6605         window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
6606         window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size);
6607         window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
6608         window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize);
6609         window->InnerClipRect.ClipWithFull(host_rect);
6610 
6611         // Default item width. Make it proportional to window size if window manually resizes
6612         if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
6613             window->ItemWidthDefault = ImFloor(window->Size.x * 0.65f);
6614         else
6615             window->ItemWidthDefault = ImFloor(g.FontSize * 16.0f);
6616 
6617         // SCROLLING
6618 
6619         // Lock down maximum scrolling
6620         // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate
6621         // for right/bottom aligned items without creating a scrollbar.
6622         window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth());
6623         window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());
6624 
6625         // Apply scrolling
6626         window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);
6627         window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
6628 
6629         // DRAWING
6630 
6631         // Setup draw list and outer clipping rectangle
6632         IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0);
6633         window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
6634         PushClipRect(host_rect.Min, host_rect.Max, false);
6635 
6636         // Draw modal or window list full viewport dimming background (for other viewports we'll render them in EndFrame)
6637         ImGuiWindow* window_window_list = g.NavWindowingListWindow;
6638         const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0;
6639         const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && ((window == g.NavWindowingTargetAnim->RootWindowDockTree) || (window == window_window_list && window_window_list->Viewport != g.NavWindowingTargetAnim->Viewport));
6640         if (dim_bg_for_modal || dim_bg_for_window_list)
6641         {
6642             const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
6643             window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col);
6644         }
6645 
6646         // Draw navigation selection/windowing rectangle background
6647         if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim)
6648         {
6649             ImRect bb = window->Rect();
6650             bb.Expand(g.FontSize);
6651             if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway
6652                 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
6653         }
6654 
6655         // Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call.
6656         // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
6657         // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child.
6658         // We also disabled this when we have dimming overlay behind this specific one child.
6659         // 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.
6660         const bool is_undocked_or_docked_visible = !window->DockIsActive || window->DockTabIsVisible;
6661         if (is_undocked_or_docked_visible)
6662         {
6663             bool render_decorations_in_parent = false;
6664             if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
6665                 if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0)
6666                     render_decorations_in_parent = true;
6667             if (render_decorations_in_parent)
6668                 window->DrawList = parent_window->DrawList;
6669 
6670             // Handle title bar, scrollbar, resize grips and resize borders
6671             const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
6672             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)));
6673             RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, handle_borders_and_resize_grips, resize_grip_count, resize_grip_col, resize_grip_draw_size);
6674 
6675             if (render_decorations_in_parent)
6676                 window->DrawList = &window->DrawListInst;
6677         }
6678 
6679         // Draw navigation selection/windowing rectangle border
6680         if (g.NavWindowingTargetAnim == window)
6681         {
6682             float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
6683             ImRect bb = window->Rect();
6684             bb.Expand(g.FontSize);
6685             if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward
6686             {
6687                 bb.Expand(-g.FontSize - 1.0f);
6688                 rounding = window->WindowRounding;
6689             }
6690             window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, 0, 3.0f);
6691         }
6692 
6693         // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
6694 
6695         // Work rectangle.
6696         // Affected by window padding and border size. Used by:
6697         // - Columns() for right-most edge
6698         // - TreeNode(), CollapsingHeader() for right-most edge
6699         // - BeginTabBar() for right-most edge
6700         const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);
6701         const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);
6702         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));
6703         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));
6704         window->WorkRect.Min.x = ImFloor(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));
6705         window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
6706         window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
6707         window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
6708         window->ParentWorkRect = window->WorkRect;
6709 
6710         // [LEGACY] Content Region
6711         // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
6712         // Used by:
6713         // - Mouse wheel scrolling + many other things
6714         window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
6715         window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + decoration_up_height;
6716         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));
6717         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));
6718 
6719         // Setup drawing context
6720         // (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.)
6721         window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x;
6722         window->DC.GroupOffset.x = 0.0f;
6723         window->DC.ColumnsOffset.x = 0.0f;
6724         window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, decoration_up_height + window->WindowPadding.y - window->Scroll.y);
6725         window->DC.CursorPos = window->DC.CursorStartPos;
6726         window->DC.CursorPosPrevLine = window->DC.CursorPos;
6727         window->DC.CursorMaxPos = window->DC.CursorStartPos;
6728         window->DC.IdealMaxPos = window->DC.CursorStartPos;
6729         window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
6730         window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
6731 
6732         window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
6733         window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext;
6734         window->DC.NavLayersActiveMaskNext = 0x00;
6735         window->DC.NavHideHighlightOneFrame = false;
6736         window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f);
6737 
6738         window->DC.MenuBarAppending = false;
6739         window->DC.MenuColumns.Update(style.ItemSpacing.x, window_just_activated_by_user);
6740         window->DC.TreeDepth = 0;
6741         window->DC.TreeJumpToParentOnPopMask = 0x00;
6742         window->DC.ChildWindows.resize(0);
6743         window->DC.StateStorage = &window->StateStorage;
6744         window->DC.CurrentColumns = NULL;
6745         window->DC.LayoutType = ImGuiLayoutType_Vertical;
6746         window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
6747         window->DC.FocusCounterRegular = window->DC.FocusCounterTabStop = -1;
6748 
6749         window->DC.ItemWidth = window->ItemWidthDefault;
6750         window->DC.TextWrapPos = -1.0f; // disabled
6751         window->DC.ItemWidthStack.resize(0);
6752         window->DC.TextWrapPosStack.resize(0);
6753 
6754         if (window->AutoFitFramesX > 0)
6755             window->AutoFitFramesX--;
6756         if (window->AutoFitFramesY > 0)
6757             window->AutoFitFramesY--;
6758 
6759         // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
6760         if (want_focus)
6761         {
6762             FocusWindow(window);
6763             NavInitWindow(window, false); // <-- this is in the way for us to be able to defer and sort reappearing FocusWindow() calls
6764         }
6765 
6766         // Close requested by platform window
6767         if (p_open != NULL && window->Viewport->PlatformRequestClose && window->Viewport != GetMainViewport())
6768         {
6769             if (!window->DockIsActive || window->DockTabIsVisible)
6770             {
6771                 window->Viewport->PlatformRequestClose = false;
6772                 g.NavWindowingToggleLayer = false; // Assume user mapped PlatformRequestClose on ALT-F4 so we disable ALT for menu toggle. False positive not an issue.
6773                 IMGUI_DEBUG_LOG_VIEWPORT("Window '%s' PlatformRequestClose\n", window->Name);
6774                 *p_open = false;
6775             }
6776         }
6777 
6778         // Title bar
6779         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
6780             RenderWindowTitleBarContents(window, ImRect(title_bar_rect.Min.x + window->WindowBorderSize, title_bar_rect.Min.y, title_bar_rect.Max.x - window->WindowBorderSize, title_bar_rect.Max.y), name, p_open);
6781 
6782         // Clear hit test shape every frame
6783         window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0;
6784 
6785         // Pressing CTRL+C while holding on a window copy its content to the clipboard
6786         // 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.
6787         // Maybe we can support CTRL+C on every element?
6788         /*
6789         //if (g.NavWindow == window && g.ActiveId == 0)
6790         if (g.ActiveId == window->MoveId)
6791             if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
6792                 LogToClipboard();
6793         */
6794 
6795         if (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)
6796         {
6797             // Docking: Dragging a dockable window (or any of its child) turns it into a drag and drop source.
6798             // We need to do this _before_ we overwrite window->DC.LastItemId below because BeginDockableDragDropSource() also overwrites it.
6799             if (g.MovingWindow == window && g.IO.KeyShift == false)
6800                 if ((window->RootWindowDockTree->Flags & ImGuiWindowFlags_NoDocking) == 0)
6801                     BeginDockableDragDropSource(window);
6802 
6803             // Docking: Any dockable window can act as a target. For dock node hosts we call BeginDockableDragDropTarget() in DockNodeUpdate() instead.
6804             if (g.DragDropActive && !(flags & ImGuiWindowFlags_NoDocking))
6805                 if (g.MovingWindow == NULL || g.MovingWindow->RootWindowDockTree != window)
6806                     if ((window == window->RootWindowDockTree) && !(window->Flags & ImGuiWindowFlags_DockNodeHost))
6807                         BeginDockableDragDropTarget(window);
6808         }
6809 
6810         // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
6811         // This is useful to allow creating context menus on title bar only, etc.
6812         if (window->DockIsActive)
6813             SetLastItemData(window, window->ID, window->DockTabItemStatusFlags, window->DockTabItemRect);
6814         else
6815             SetLastItemData(window, window->MoveId, IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, title_bar_rect);
6816 
6817 #ifdef IMGUI_ENABLE_TEST_ENGINE
6818         if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
6819             IMGUI_TEST_ENGINE_ITEM_ADD(window->DC.LastItemRect, window->DC.LastItemId);
6820 #endif
6821     }
6822     else
6823     {
6824         // Append
6825         SetCurrentViewport(window, window->Viewport);
6826         SetCurrentWindow(window);
6827     }
6828 
6829     // Pull/inherit current state
6830     g.CurrentItemFlags = g.ItemFlagsStack.back(); // Inherit from shared stack
6831     window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : window->GetID("#FOCUSSCOPE"); // Inherit from parent only // -V595
6832 
6833     if (!(flags & ImGuiWindowFlags_DockNodeHost))
6834         PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
6835 
6836     // 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)
6837     window->WriteAccessed = false;
6838     window->BeginCount++;
6839     g.NextWindowData.ClearFlags();
6840 
6841     // Update visibility
6842     if (first_begin_of_the_frame)
6843     {
6844         // When we are about to select this tab (which will only be visible on the _next frame_), flag it with a non-zero HiddenFramesCannotSkipItems.
6845         // This will have the important effect of actually returning true in Begin() and not setting SkipItems, allowing an earlier submission of the window contents.
6846         // This is analogous to regular windows being hidden from one frame.
6847         // 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.
6848         if (window->DockIsActive && !window->DockTabIsVisible)
6849         {
6850             if (window->LastFrameJustFocused == g.FrameCount)
6851                 window->HiddenFramesCannotSkipItems = 1;
6852             else
6853                 window->HiddenFramesCanSkipItems = 1;
6854         }
6855 
6856         if (flags & ImGuiWindowFlags_ChildWindow)
6857         {
6858             // Child window can be out of sight and have "negative" clip windows.
6859             // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
6860             IM_ASSERT((flags& ImGuiWindowFlags_NoTitleBar) != 0 || (window->DockIsActive));
6861             if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) // FIXME: Doesn't make sense for ChildWindow??
6862                 if (!g.LogEnabled)
6863                     if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
6864                         window->HiddenFramesCanSkipItems = 1;
6865 
6866             // Hide along with parent or if parent is collapsed
6867             if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))
6868                 window->HiddenFramesCanSkipItems = 1;
6869             if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0))
6870                 window->HiddenFramesCannotSkipItems = 1;
6871         }
6872 
6873         // 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)
6874         if (style.Alpha <= 0.0f)
6875             window->HiddenFramesCanSkipItems = 1;
6876 
6877         // Update the Hidden flag
6878         window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0) || (window->HiddenFramesForRenderOnly > 0);
6879 
6880         // Disable inputs for requested number of frames
6881         if (window->DisableInputsFrames > 0)
6882         {
6883             window->DisableInputsFrames--;
6884             window->Flags |= ImGuiWindowFlags_NoInputs;
6885         }
6886 
6887         // Update the SkipItems flag, used to early out of all items functions (no layout required)
6888         bool skip_items = false;
6889         if (window->Collapsed || !window->Active || window->Hidden)
6890             if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0)
6891                 skip_items = true;
6892         window->SkipItems = skip_items;
6893 
6894         // Sanity check: there are two spots which can set Appearing = true
6895         // - when 'window_just_activated_by_user' is set -> HiddenFramesCannotSkipItems is set -> SkipItems always false
6896         // - in BeginDocked() path when DockNodeIsVisible == DockTabIsVisible == true -> hidden _should_ be all zero // FIXME: Not formally proven, hence the assert.
6897         if (window->SkipItems && !window->Appearing)
6898             IM_ASSERT(window->Appearing == false); // Please report on GitHub if this triggers: https://github.com/ocornut/imgui/issues/4177
6899     }
6900 
6901     return !window->SkipItems;
6902 }
6903 
End()6904 void ImGui::End()
6905 {
6906     ImGuiContext& g = *GImGui;
6907     ImGuiWindow* window = g.CurrentWindow;
6908 
6909     // Error checking: verify that user hasn't called End() too many times!
6910     if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow)
6911     {
6912         IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!");
6913         return;
6914     }
6915     IM_ASSERT(g.CurrentWindowStack.Size > 0);
6916 
6917     // Error checking: verify that user doesn't directly call End() on a child window.
6918     if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_DockNodeHost) && !window->DockIsActive)
6919         IM_ASSERT_USER_ERROR(g.WithinEndChild, "Must call EndChild() and not End()!");
6920 
6921     // Close anything that is open
6922     if (window->DC.CurrentColumns)
6923         EndColumns();
6924     if (!(window->Flags & ImGuiWindowFlags_DockNodeHost))   // Pop inner window clip rectangle
6925         PopClipRect();
6926 
6927     // Stop logging
6928     if (!(window->Flags & ImGuiWindowFlags_ChildWindow))    // FIXME: add more options for scope of logging
6929         LogFinish();
6930 
6931     // Docking: report contents sizes to parent to allow for auto-resize
6932     if (window->DockNode && window->DockTabIsVisible)
6933         if (ImGuiWindow* host_window = window->DockNode->HostWindow)         // FIXME-DOCK
6934             host_window->DC.CursorMaxPos = window->DC.CursorMaxPos + window->WindowPadding - host_window->WindowPadding;
6935 
6936     // Pop from window stack
6937     g.CurrentWindowStack.pop_back();
6938     if (window->Flags & ImGuiWindowFlags_Popup)
6939         g.BeginPopupStack.pop_back();
6940     window->DC.StackSizesOnBegin.CompareWithCurrentState();
6941     SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
6942     if (g.CurrentWindow)
6943         SetCurrentViewport(g.CurrentWindow, g.CurrentWindow->Viewport);
6944 }
6945 
BringWindowToFocusFront(ImGuiWindow * window)6946 void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
6947 {
6948     ImGuiContext& g = *GImGui;
6949     IM_ASSERT(window == window->RootWindow);
6950 
6951     const int cur_order = window->FocusOrder;
6952     IM_ASSERT(g.WindowsFocusOrder[cur_order] == window);
6953     if (g.WindowsFocusOrder.back() == window)
6954         return;
6955 
6956     const int new_order = g.WindowsFocusOrder.Size - 1;
6957     for (int n = cur_order; n < new_order; n++)
6958     {
6959         g.WindowsFocusOrder[n] = g.WindowsFocusOrder[n + 1];
6960         g.WindowsFocusOrder[n]->FocusOrder--;
6961         IM_ASSERT(g.WindowsFocusOrder[n]->FocusOrder == n);
6962     }
6963     g.WindowsFocusOrder[new_order] = window;
6964     window->FocusOrder = (short)new_order;
6965 }
6966 
BringWindowToDisplayFront(ImGuiWindow * window)6967 void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
6968 {
6969     ImGuiContext& g = *GImGui;
6970     ImGuiWindow* current_front_window = g.Windows.back();
6971     if (current_front_window == window || current_front_window->RootWindowDockTree == window) // Cheap early out (could be better)
6972         return;
6973     for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window
6974         if (g.Windows[i] == window)
6975         {
6976             memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
6977             g.Windows[g.Windows.Size - 1] = window;
6978             break;
6979         }
6980 }
6981 
BringWindowToDisplayBack(ImGuiWindow * window)6982 void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
6983 {
6984     ImGuiContext& g = *GImGui;
6985     if (g.Windows[0] == window)
6986         return;
6987     for (int i = 0; i < g.Windows.Size; i++)
6988         if (g.Windows[i] == window)
6989         {
6990             memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
6991             g.Windows[0] = window;
6992             break;
6993         }
6994 }
6995 
6996 // Moving window to front of display and set focus (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)6997 void ImGui::FocusWindow(ImGuiWindow* window)
6998 {
6999     ImGuiContext& g = *GImGui;
7000 
7001     if (g.NavWindow != window)
7002     {
7003         g.NavWindow = window;
7004         if (window && g.NavDisableMouseHover)
7005             g.NavMousePosDirty = true;
7006         g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
7007         g.NavFocusScopeId = 0;
7008         g.NavIdIsAlive = false;
7009         g.NavLayer = ImGuiNavLayer_Main;
7010         g.NavInitRequest = g.NavMoveRequest = false;
7011         NavUpdateAnyRequestFlag();
7012         //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL);
7013     }
7014 
7015     // Close popups if any
7016     ClosePopupsOverWindow(window, false);
7017 
7018     // Move the root window to the top of the pile
7019     IM_ASSERT(window == NULL || window->RootWindowDockTree != NULL);
7020     ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL;
7021     ImGuiWindow* display_front_window = window ? window->RootWindowDockTree : NULL;
7022     ImGuiDockNode* dock_node = window ? window->DockNode : NULL;
7023     bool active_id_window_is_dock_node_host = (g.ActiveIdWindow && dock_node && dock_node->HostWindow == g.ActiveIdWindow);
7024 
7025     // Steal active widgets. Some of the cases it triggers includes:
7026     // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run.
7027     // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId)
7028     // - Using dock host items (tab, collapse button) can trigger this before we redirect the ActiveIdWindow toward the child window.
7029     if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window)
7030         if (!g.ActiveIdNoClearOnFocusLoss && !active_id_window_is_dock_node_host)
7031             ClearActiveID();
7032 
7033     // Passing NULL allow to disable keyboard focus
7034     if (!window)
7035         return;
7036     window->LastFrameJustFocused = g.FrameCount;
7037 
7038     // Select in dock node
7039     if (dock_node && dock_node->TabBar)
7040         dock_node->TabBar->SelectedTabId = dock_node->TabBar->NextSelectedTabId = window->ID;
7041 
7042     // Bring to front
7043     BringWindowToFocusFront(focus_front_window);
7044     if (((window->Flags | focus_front_window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
7045         BringWindowToDisplayFront(display_front_window);
7046 }
7047 
FocusTopMostWindowUnderOne(ImGuiWindow * under_this_window,ImGuiWindow * ignore_window)7048 void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window)
7049 {
7050     ImGuiContext& g = *GImGui;
7051 
7052     const int start_idx = ((under_this_window != NULL) ? FindWindowFocusIndex(under_this_window) : g.WindowsFocusOrder.Size) - 1;
7053     for (int i = start_idx; i >= 0; i--)
7054     {
7055         // 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.
7056         ImGuiWindow* window = g.WindowsFocusOrder[i];
7057         IM_ASSERT(window == window->RootWindow);
7058         if (window != ignore_window && window->WasActive)
7059             if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
7060             {
7061                 // FIXME-DOCK: This is failing (lagging by one frame) for docked windows.
7062                 // If A and B are docked into window and B disappear, at the NewFrame() call site window->NavLastChildNavWindow will still point to B.
7063                 // 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)
7064                 // to tell which is the "previous" window. Or we may leverage 'LastFrameFocused/LastFrameJustFocused' and have this function handle child window itself?
7065                 ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window);
7066                 FocusWindow(focus_window);
7067                 return;
7068             }
7069     }
7070     FocusWindow(NULL);
7071 }
7072 
7073 // Important: this alone doesn't alter current ImDrawList state. This is called by PushFont/PopFont only.
SetCurrentFont(ImFont * font)7074 void ImGui::SetCurrentFont(ImFont* font)
7075 {
7076     ImGuiContext& g = *GImGui;
7077     IM_ASSERT(font && font->IsLoaded());    // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
7078     IM_ASSERT(font->Scale > 0.0f);
7079     g.Font = font;
7080     g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale);
7081     g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
7082 
7083     ImFontAtlas* atlas = g.Font->ContainerAtlas;
7084     g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
7085     g.DrawListSharedData.TexUvLines = atlas->TexUvLines;
7086     g.DrawListSharedData.Font = g.Font;
7087     g.DrawListSharedData.FontSize = g.FontSize;
7088 }
7089 
PushFont(ImFont * font)7090 void ImGui::PushFont(ImFont* font)
7091 {
7092     ImGuiContext& g = *GImGui;
7093     if (!font)
7094         font = GetDefaultFont();
7095     SetCurrentFont(font);
7096     g.FontStack.push_back(font);
7097     g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
7098 }
7099 
PopFont()7100 void  ImGui::PopFont()
7101 {
7102     ImGuiContext& g = *GImGui;
7103     g.CurrentWindow->DrawList->PopTextureID();
7104     g.FontStack.pop_back();
7105     SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
7106 }
7107 
PushItemFlag(ImGuiItemFlags option,bool enabled)7108 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
7109 {
7110     ImGuiContext& g = *GImGui;
7111     ImGuiItemFlags item_flags = g.CurrentItemFlags;
7112     IM_ASSERT(item_flags == g.ItemFlagsStack.back());
7113     if (enabled)
7114         item_flags |= option;
7115     else
7116         item_flags &= ~option;
7117     g.CurrentItemFlags = item_flags;
7118     g.ItemFlagsStack.push_back(item_flags);
7119 }
7120 
PopItemFlag()7121 void ImGui::PopItemFlag()
7122 {
7123     ImGuiContext& g = *GImGui;
7124     IM_ASSERT(g.ItemFlagsStack.Size > 1); // Too many calls to PopItemFlag() - we always leave a 0 at the bottom of the stack.
7125     g.ItemFlagsStack.pop_back();
7126     g.CurrentItemFlags = g.ItemFlagsStack.back();
7127 }
7128 
7129 // PushDisabled()/PopDisabled()
7130 // - Those can be nested but this cannot be used to enable an already disabled section (a single PushDisabled(true) in the stack is enough to keep things disabled)
7131 // - Those are not yet exposed in imgui.h because we are unsure of how to alter the style in a way that works for everyone.
7132 //   We may rework this. Hypothetically, a future styling system may set a flag which make widgets use different colors.
7133 // - Feedback welcome at https://github.com/ocornut/imgui/issues/211
7134 // - You may trivially implement your own variation of this if needed.
7135 //   Here we test (CurrentItemFlags & ImGuiItemFlags_Disabled) to allow nested PushDisabled() calls.
PushDisabled(bool disabled)7136 void ImGui::PushDisabled(bool disabled)
7137 {
7138     ImGuiContext& g = *GImGui;
7139     bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
7140     if (!was_disabled && disabled)
7141         PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.6f);
7142     PushItemFlag(ImGuiItemFlags_Disabled, was_disabled || disabled);
7143 }
7144 
PopDisabled()7145 void ImGui::PopDisabled()
7146 {
7147     ImGuiContext& g = *GImGui;
7148     bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
7149     PopItemFlag();
7150     if (was_disabled && (g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0)
7151         PopStyleVar();
7152 }
7153 
7154 // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system.
PushAllowKeyboardFocus(bool allow_keyboard_focus)7155 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
7156 {
7157     PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus);
7158 }
7159 
PopAllowKeyboardFocus()7160 void ImGui::PopAllowKeyboardFocus()
7161 {
7162     PopItemFlag();
7163 }
7164 
PushButtonRepeat(bool repeat)7165 void ImGui::PushButtonRepeat(bool repeat)
7166 {
7167     PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
7168 }
7169 
PopButtonRepeat()7170 void ImGui::PopButtonRepeat()
7171 {
7172     PopItemFlag();
7173 }
7174 
PushTextWrapPos(float wrap_pos_x)7175 void ImGui::PushTextWrapPos(float wrap_pos_x)
7176 {
7177     ImGuiWindow* window = GetCurrentWindow();
7178     window->DC.TextWrapPosStack.push_back(window->DC.TextWrapPos);
7179     window->DC.TextWrapPos = wrap_pos_x;
7180 }
7181 
PopTextWrapPos()7182 void ImGui::PopTextWrapPos()
7183 {
7184     ImGuiWindow* window = GetCurrentWindow();
7185     window->DC.TextWrapPos = window->DC.TextWrapPosStack.back();
7186     window->DC.TextWrapPosStack.pop_back();
7187 }
7188 
7189 // FIXME: We are exposing the docking hierarchy to end-user here (via IsWindowHovered, IsWindowFocused) which is unusual.
IsWindowChildOf(ImGuiWindow * window,ImGuiWindow * potential_parent)7190 bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
7191 {
7192     if (window->RootWindowDockTree == potential_parent)
7193         return true;
7194     while (window != NULL)
7195     {
7196         if (window == potential_parent)
7197             return true;
7198         window = window->ParentWindow;
7199     }
7200     return false;
7201 }
7202 
IsWindowAbove(ImGuiWindow * potential_above,ImGuiWindow * potential_below)7203 bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below)
7204 {
7205     ImGuiContext& g = *GImGui;
7206     for (int i = g.Windows.Size - 1; i >= 0; i--)
7207     {
7208         ImGuiWindow* candidate_window = g.Windows[i];
7209         if (candidate_window == potential_above)
7210             return true;
7211         if (candidate_window == potential_below)
7212             return false;
7213     }
7214     return false;
7215 }
7216 
IsWindowHovered(ImGuiHoveredFlags flags)7217 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
7218 {
7219     IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0);   // Flags not supported by this function
7220     ImGuiContext& g = *GImGui;
7221     if (g.HoveredWindow == NULL)
7222         return false;
7223 
7224     if ((flags & ImGuiHoveredFlags_AnyWindow) == 0)
7225     {
7226         ImGuiWindow* window = g.CurrentWindow;
7227         switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
7228         {
7229         case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
7230             if (g.HoveredWindow->RootWindow != window->RootWindow)
7231                 return false;
7232             break;
7233         case ImGuiHoveredFlags_RootWindow:
7234             if (g.HoveredWindow != window->RootWindow)
7235                 return false;
7236             break;
7237         case ImGuiHoveredFlags_ChildWindows:
7238             if (!IsWindowChildOf(g.HoveredWindow, window))
7239                 return false;
7240             break;
7241         default:
7242             if (g.HoveredWindow != window)
7243                 return false;
7244             break;
7245         }
7246     }
7247 
7248     if (!IsWindowContentHoverable(g.HoveredWindow, flags))
7249         return false;
7250     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
7251         if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
7252             return false;
7253     return true;
7254 }
7255 
IsWindowFocused(ImGuiFocusedFlags flags)7256 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
7257 {
7258     ImGuiContext& g = *GImGui;
7259 
7260     if (flags & ImGuiFocusedFlags_AnyWindow)
7261         return g.NavWindow != NULL;
7262 
7263     IM_ASSERT(g.CurrentWindow);     // Not inside a Begin()/End()
7264     switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
7265     {
7266     case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
7267         return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
7268     case ImGuiFocusedFlags_RootWindow:
7269         return g.NavWindow == g.CurrentWindow->RootWindow;
7270     case ImGuiFocusedFlags_ChildWindows:
7271         return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
7272     default:
7273         return g.NavWindow == g.CurrentWindow;
7274     }
7275 }
7276 
GetWindowDockID()7277 ImGuiID ImGui::GetWindowDockID()
7278 {
7279     ImGuiContext& g = *GImGui;
7280     return g.CurrentWindow->DockId;
7281 }
7282 
IsWindowDocked()7283 bool ImGui::IsWindowDocked()
7284 {
7285     ImGuiContext& g = *GImGui;
7286     return g.CurrentWindow->DockIsActive;
7287 }
7288 
7289 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
7290 // Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmatically.
7291 // If you want a window to never be focused, you may use the e.g. NoInputs flag.
IsWindowNavFocusable(ImGuiWindow * window)7292 bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
7293 {
7294     return window->WasActive && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
7295 }
7296 
GetWindowWidth()7297 float ImGui::GetWindowWidth()
7298 {
7299     ImGuiWindow* window = GImGui->CurrentWindow;
7300     return window->Size.x;
7301 }
7302 
GetWindowHeight()7303 float ImGui::GetWindowHeight()
7304 {
7305     ImGuiWindow* window = GImGui->CurrentWindow;
7306     return window->Size.y;
7307 }
7308 
GetWindowPos()7309 ImVec2 ImGui::GetWindowPos()
7310 {
7311     ImGuiContext& g = *GImGui;
7312     ImGuiWindow* window = g.CurrentWindow;
7313     return window->Pos;
7314 }
7315 
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiCond cond)7316 void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
7317 {
7318     // Test condition (NB: bit 0 is always true) and clear flags for next time
7319     if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
7320         return;
7321 
7322     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
7323     window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
7324     window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
7325 
7326     // Set
7327     const ImVec2 old_pos = window->Pos;
7328     window->Pos = ImFloor(pos);
7329     ImVec2 offset = window->Pos - old_pos;
7330     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
7331     window->DC.CursorMaxPos += offset;      // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.
7332     window->DC.IdealMaxPos += offset;
7333     window->DC.CursorStartPos += offset;
7334 }
7335 
SetWindowPos(const ImVec2 & pos,ImGuiCond cond)7336 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
7337 {
7338     ImGuiWindow* window = GetCurrentWindowRead();
7339     SetWindowPos(window, pos, cond);
7340 }
7341 
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiCond cond)7342 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
7343 {
7344     if (ImGuiWindow* window = FindWindowByName(name))
7345         SetWindowPos(window, pos, cond);
7346 }
7347 
GetWindowSize()7348 ImVec2 ImGui::GetWindowSize()
7349 {
7350     ImGuiWindow* window = GetCurrentWindowRead();
7351     return window->Size;
7352 }
7353 
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiCond cond)7354 void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
7355 {
7356     // Test condition (NB: bit 0 is always true) and clear flags for next time
7357     if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
7358         return;
7359 
7360     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
7361     window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
7362 
7363     // Set
7364     if (size.x > 0.0f)
7365     {
7366         window->AutoFitFramesX = 0;
7367         window->SizeFull.x = IM_FLOOR(size.x);
7368     }
7369     else
7370     {
7371         window->AutoFitFramesX = 2;
7372         window->AutoFitOnlyGrows = false;
7373     }
7374     if (size.y > 0.0f)
7375     {
7376         window->AutoFitFramesY = 0;
7377         window->SizeFull.y = IM_FLOOR(size.y);
7378     }
7379     else
7380     {
7381         window->AutoFitFramesY = 2;
7382         window->AutoFitOnlyGrows = false;
7383     }
7384 }
7385 
SetWindowSize(const ImVec2 & size,ImGuiCond cond)7386 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
7387 {
7388     SetWindowSize(GImGui->CurrentWindow, size, cond);
7389 }
7390 
SetWindowSize(const char * name,const ImVec2 & size,ImGuiCond cond)7391 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
7392 {
7393     if (ImGuiWindow* window = FindWindowByName(name))
7394         SetWindowSize(window, size, cond);
7395 }
7396 
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiCond cond)7397 void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
7398 {
7399     // Test condition (NB: bit 0 is always true) and clear flags for next time
7400     if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
7401         return;
7402     window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
7403 
7404     // Set
7405     window->Collapsed = collapsed;
7406 }
7407 
SetWindowHitTestHole(ImGuiWindow * window,const ImVec2 & pos,const ImVec2 & size)7408 void ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size)
7409 {
7410     IM_ASSERT(window->HitTestHoleSize.x == 0);     // We don't support multiple holes/hit test filters
7411     window->HitTestHoleSize = ImVec2ih(size);
7412     window->HitTestHoleOffset = ImVec2ih(pos - window->Pos);
7413 }
7414 
SetWindowCollapsed(bool collapsed,ImGuiCond cond)7415 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
7416 {
7417     SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
7418 }
7419 
IsWindowCollapsed()7420 bool ImGui::IsWindowCollapsed()
7421 {
7422     ImGuiWindow* window = GetCurrentWindowRead();
7423     return window->Collapsed;
7424 }
7425 
IsWindowAppearing()7426 bool ImGui::IsWindowAppearing()
7427 {
7428     ImGuiWindow* window = GetCurrentWindowRead();
7429     return window->Appearing;
7430 }
7431 
SetWindowCollapsed(const char * name,bool collapsed,ImGuiCond cond)7432 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
7433 {
7434     if (ImGuiWindow* window = FindWindowByName(name))
7435         SetWindowCollapsed(window, collapsed, cond);
7436 }
7437 
SetWindowFocus()7438 void ImGui::SetWindowFocus()
7439 {
7440     FocusWindow(GImGui->CurrentWindow);
7441 }
7442 
SetWindowFocus(const char * name)7443 void ImGui::SetWindowFocus(const char* name)
7444 {
7445     if (name)
7446     {
7447         if (ImGuiWindow* window = FindWindowByName(name))
7448             FocusWindow(window);
7449     }
7450     else
7451     {
7452         FocusWindow(NULL);
7453     }
7454 }
7455 
SetNextWindowPos(const ImVec2 & pos,ImGuiCond cond,const ImVec2 & pivot)7456 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
7457 {
7458     ImGuiContext& g = *GImGui;
7459     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
7460     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos;
7461     g.NextWindowData.PosVal = pos;
7462     g.NextWindowData.PosPivotVal = pivot;
7463     g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
7464     g.NextWindowData.PosUndock = true;
7465 }
7466 
SetNextWindowSize(const ImVec2 & size,ImGuiCond cond)7467 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
7468 {
7469     ImGuiContext& g = *GImGui;
7470     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
7471     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize;
7472     g.NextWindowData.SizeVal = size;
7473     g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
7474 }
7475 
SetNextWindowSizeConstraints(const ImVec2 & size_min,const ImVec2 & size_max,ImGuiSizeCallback custom_callback,void * custom_callback_user_data)7476 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
7477 {
7478     ImGuiContext& g = *GImGui;
7479     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint;
7480     g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
7481     g.NextWindowData.SizeCallback = custom_callback;
7482     g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
7483 }
7484 
7485 // Content size = inner scrollable rectangle, padded with WindowPadding.
7486 // SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
SetNextWindowContentSize(const ImVec2 & size)7487 void ImGui::SetNextWindowContentSize(const ImVec2& size)
7488 {
7489     ImGuiContext& g = *GImGui;
7490     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize;
7491     g.NextWindowData.ContentSizeVal = ImFloor(size);
7492 }
7493 
SetNextWindowScroll(const ImVec2 & scroll)7494 void ImGui::SetNextWindowScroll(const ImVec2& scroll)
7495 {
7496     ImGuiContext& g = *GImGui;
7497     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasScroll;
7498     g.NextWindowData.ScrollVal = scroll;
7499 }
7500 
SetNextWindowCollapsed(bool collapsed,ImGuiCond cond)7501 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
7502 {
7503     ImGuiContext& g = *GImGui;
7504     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
7505     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed;
7506     g.NextWindowData.CollapsedVal = collapsed;
7507     g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
7508 }
7509 
SetNextWindowFocus()7510 void ImGui::SetNextWindowFocus()
7511 {
7512     ImGuiContext& g = *GImGui;
7513     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus;
7514 }
7515 
SetNextWindowBgAlpha(float alpha)7516 void ImGui::SetNextWindowBgAlpha(float alpha)
7517 {
7518     ImGuiContext& g = *GImGui;
7519     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha;
7520     g.NextWindowData.BgAlphaVal = alpha;
7521 }
7522 
SetNextWindowViewport(ImGuiID id)7523 void ImGui::SetNextWindowViewport(ImGuiID id)
7524 {
7525     ImGuiContext& g = *GImGui;
7526     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasViewport;
7527     g.NextWindowData.ViewportId = id;
7528 }
7529 
SetNextWindowDockID(ImGuiID id,ImGuiCond cond)7530 void ImGui::SetNextWindowDockID(ImGuiID id, ImGuiCond cond)
7531 {
7532     ImGuiContext& g = *GImGui;
7533     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasDock;
7534     g.NextWindowData.DockCond = cond ? cond : ImGuiCond_Always;
7535     g.NextWindowData.DockId = id;
7536 }
7537 
SetNextWindowClass(const ImGuiWindowClass * window_class)7538 void ImGui::SetNextWindowClass(const ImGuiWindowClass* window_class)
7539 {
7540     ImGuiContext& g = *GImGui;
7541     IM_ASSERT((window_class->ViewportFlagsOverrideSet & window_class->ViewportFlagsOverrideClear) == 0); // Cannot set both set and clear for the same bit
7542     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasWindowClass;
7543     g.NextWindowData.WindowClass = *window_class;
7544 }
7545 
GetWindowDrawList()7546 ImDrawList* ImGui::GetWindowDrawList()
7547 {
7548     ImGuiWindow* window = GetCurrentWindow();
7549     return window->DrawList;
7550 }
7551 
GetWindowDpiScale()7552 float ImGui::GetWindowDpiScale()
7553 {
7554     ImGuiContext& g = *GImGui;
7555     return g.CurrentDpiScale;
7556 }
7557 
GetWindowViewport()7558 ImGuiViewport* ImGui::GetWindowViewport()
7559 {
7560     ImGuiContext& g = *GImGui;
7561     IM_ASSERT(g.CurrentViewport != NULL && g.CurrentViewport == g.CurrentWindow->Viewport);
7562     return g.CurrentViewport;
7563 }
7564 
GetFont()7565 ImFont* ImGui::GetFont()
7566 {
7567     return GImGui->Font;
7568 }
7569 
GetFontSize()7570 float ImGui::GetFontSize()
7571 {
7572     return GImGui->FontSize;
7573 }
7574 
GetFontTexUvWhitePixel()7575 ImVec2 ImGui::GetFontTexUvWhitePixel()
7576 {
7577     return GImGui->DrawListSharedData.TexUvWhitePixel;
7578 }
7579 
SetWindowFontScale(float scale)7580 void ImGui::SetWindowFontScale(float scale)
7581 {
7582     IM_ASSERT(scale > 0.0f);
7583     ImGuiContext& g = *GImGui;
7584     ImGuiWindow* window = GetCurrentWindow();
7585     window->FontWindowScale = scale;
7586     g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
7587 }
7588 
ActivateItem(ImGuiID id)7589 void ImGui::ActivateItem(ImGuiID id)
7590 {
7591     ImGuiContext& g = *GImGui;
7592     g.NavNextActivateId = id;
7593 }
7594 
PushFocusScope(ImGuiID id)7595 void ImGui::PushFocusScope(ImGuiID id)
7596 {
7597     ImGuiContext& g = *GImGui;
7598     ImGuiWindow* window = g.CurrentWindow;
7599     g.FocusScopeStack.push_back(window->DC.NavFocusScopeIdCurrent);
7600     window->DC.NavFocusScopeIdCurrent = id;
7601 }
7602 
PopFocusScope()7603 void ImGui::PopFocusScope()
7604 {
7605     ImGuiContext& g = *GImGui;
7606     ImGuiWindow* window = g.CurrentWindow;
7607     IM_ASSERT(g.FocusScopeStack.Size > 0); // Too many PopFocusScope() ?
7608     window->DC.NavFocusScopeIdCurrent = g.FocusScopeStack.back();
7609     g.FocusScopeStack.pop_back();
7610 }
7611 
SetKeyboardFocusHere(int offset)7612 void ImGui::SetKeyboardFocusHere(int offset)
7613 {
7614     IM_ASSERT(offset >= -1);    // -1 is allowed but not below
7615     ImGuiContext& g = *GImGui;
7616     ImGuiWindow* window = g.CurrentWindow;
7617     g.TabFocusRequestNextWindow = window;
7618     g.TabFocusRequestNextCounterRegular = window->DC.FocusCounterRegular + 1 + offset;
7619     g.TabFocusRequestNextCounterTabStop = INT_MAX;
7620 }
7621 
SetItemDefaultFocus()7622 void ImGui::SetItemDefaultFocus()
7623 {
7624     ImGuiContext& g = *GImGui;
7625     ImGuiWindow* window = g.CurrentWindow;
7626     if (!window->Appearing)
7627         return;
7628     if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == window->DC.NavLayerCurrent)
7629     {
7630         g.NavInitRequest = false;
7631         g.NavInitResultId = window->DC.LastItemId;
7632         g.NavInitResultRectRel = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos);
7633         NavUpdateAnyRequestFlag();
7634         if (!IsItemVisible())
7635             SetScrollHereY();
7636     }
7637 }
7638 
SetStateStorage(ImGuiStorage * tree)7639 void ImGui::SetStateStorage(ImGuiStorage* tree)
7640 {
7641     ImGuiWindow* window = GImGui->CurrentWindow;
7642     window->DC.StateStorage = tree ? tree : &window->StateStorage;
7643 }
7644 
GetStateStorage()7645 ImGuiStorage* ImGui::GetStateStorage()
7646 {
7647     ImGuiWindow* window = GImGui->CurrentWindow;
7648     return window->DC.StateStorage;
7649 }
7650 
PushID(const char * str_id)7651 void ImGui::PushID(const char* str_id)
7652 {
7653     ImGuiContext& g = *GImGui;
7654     ImGuiWindow* window = g.CurrentWindow;
7655     ImGuiID id = window->GetIDNoKeepAlive(str_id);
7656     window->IDStack.push_back(id);
7657 }
7658 
PushID(const char * str_id_begin,const char * str_id_end)7659 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
7660 {
7661     ImGuiContext& g = *GImGui;
7662     ImGuiWindow* window = g.CurrentWindow;
7663     ImGuiID id = window->GetIDNoKeepAlive(str_id_begin, str_id_end);
7664     window->IDStack.push_back(id);
7665 }
7666 
PushID(const void * ptr_id)7667 void ImGui::PushID(const void* ptr_id)
7668 {
7669     ImGuiContext& g = *GImGui;
7670     ImGuiWindow* window = g.CurrentWindow;
7671     ImGuiID id = window->GetIDNoKeepAlive(ptr_id);
7672     window->IDStack.push_back(id);
7673 }
7674 
PushID(int int_id)7675 void ImGui::PushID(int int_id)
7676 {
7677     ImGuiContext& g = *GImGui;
7678     ImGuiWindow* window = g.CurrentWindow;
7679     ImGuiID id = window->GetIDNoKeepAlive(int_id);
7680     window->IDStack.push_back(id);
7681 }
7682 
7683 // Push a given id value ignoring the ID stack as a seed.
PushOverrideID(ImGuiID id)7684 void ImGui::PushOverrideID(ImGuiID id)
7685 {
7686     ImGuiContext& g = *GImGui;
7687     ImGuiWindow* window = g.CurrentWindow;
7688     window->IDStack.push_back(id);
7689 }
7690 
7691 // Helper to avoid a common series of PushOverrideID -> GetID() -> PopID() call
7692 // (note that when using this pattern, TestEngine's "Stack Tool" will tend to not display the intermediate stack level.
7693 //  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)7694 ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed)
7695 {
7696     ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
7697     ImGui::KeepAliveID(id);
7698 #ifdef IMGUI_ENABLE_TEST_ENGINE
7699     ImGuiContext& g = *GImGui;
7700     IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
7701 #endif
7702     return id;
7703 }
7704 
PopID()7705 void ImGui::PopID()
7706 {
7707     ImGuiWindow* window = GImGui->CurrentWindow;
7708     IM_ASSERT(window->IDStack.Size > 1); // Too many PopID(), or could be popping in a wrong/different window?
7709     window->IDStack.pop_back();
7710 }
7711 
GetID(const char * str_id)7712 ImGuiID ImGui::GetID(const char* str_id)
7713 {
7714     ImGuiWindow* window = GImGui->CurrentWindow;
7715     return window->GetID(str_id);
7716 }
7717 
GetID(const char * str_id_begin,const char * str_id_end)7718 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
7719 {
7720     ImGuiWindow* window = GImGui->CurrentWindow;
7721     return window->GetID(str_id_begin, str_id_end);
7722 }
7723 
GetID(const void * ptr_id)7724 ImGuiID ImGui::GetID(const void* ptr_id)
7725 {
7726     ImGuiWindow* window = GImGui->CurrentWindow;
7727     return window->GetID(ptr_id);
7728 }
7729 
IsRectVisible(const ImVec2 & size)7730 bool ImGui::IsRectVisible(const ImVec2& size)
7731 {
7732     ImGuiWindow* window = GImGui->CurrentWindow;
7733     return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
7734 }
7735 
IsRectVisible(const ImVec2 & rect_min,const ImVec2 & rect_max)7736 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
7737 {
7738     ImGuiWindow* window = GImGui->CurrentWindow;
7739     return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
7740 }
7741 
7742 
7743 //-----------------------------------------------------------------------------
7744 // [SECTION] ERROR CHECKING
7745 //-----------------------------------------------------------------------------
7746 
7747 // Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui.
7748 // Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
7749 // If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. your user code
7750 // may see different structures than what imgui.cpp sees, which is problematic.
7751 // 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)7752 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)
7753 {
7754     bool error = false;
7755     if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); }
7756     if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
7757     if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
7758     if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
7759     if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
7760     if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
7761     if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); }
7762     return !error;
7763 }
7764 
ErrorCheckNewFrameSanityChecks()7765 static void ImGui::ErrorCheckNewFrameSanityChecks()
7766 {
7767     ImGuiContext& g = *GImGui;
7768 
7769     // Check user IM_ASSERT macro
7770     // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means your assert macro is incorrectly defined!
7771     //  If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block.
7772     //  This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.)
7773     // #define IM_ASSERT(EXPR)   if (SomeCode(EXPR)) SomeMoreCode();                    // Wrong!
7774     // #define IM_ASSERT(EXPR)   do { if (SomeCode(EXPR)) SomeMoreCode(); } while (0)   // Correct!
7775     if (true) IM_ASSERT(1); else IM_ASSERT(0);
7776 
7777     // Check user data
7778     // (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)
7779     IM_ASSERT(g.Initialized);
7780     IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0)              && "Need a positive DeltaTime!");
7781     IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount)  && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
7782     IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f  && "Invalid DisplaySize value!");
7783     IM_ASSERT(g.IO.Fonts->IsBuilt()                                     && "Font Atlas not built! Make sure you called ImGui_ImplXXXX_NewFrame() function for renderer backend, which should call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()");
7784     IM_ASSERT(g.Style.CurveTessellationTol > 0.0f                       && "Invalid style setting!");
7785     IM_ASSERT(g.Style.CircleTessellationMaxError  > 0.0f                && "Invalid style setting!");
7786     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
7787     IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting.");
7788     IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
7789     for (int n = 0; n < ImGuiKey_COUNT; n++)
7790         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)");
7791 
7792     // 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)
7793     if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
7794         IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
7795 
7796     // Check: the io.ConfigWindowsResizeFromEdges option requires backend to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
7797     if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
7798         g.IO.ConfigWindowsResizeFromEdges = false;
7799 
7800     // 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.
7801     if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_DockingEnable) == 0)
7802         IM_ASSERT(0 && "Please set DockingEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!");
7803     if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_ViewportsEnable) == 0)
7804         IM_ASSERT(0 && "Please set ViewportsEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!");
7805 
7806     // Perform simple checks: multi-viewport and platform windows support
7807     if (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
7808     {
7809         if ((g.IO.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasViewports))
7810         {
7811             IM_ASSERT((g.FrameCount == 0 || g.FrameCount == g.FrameCountPlatformEnded) && "Forgot to call UpdatePlatformWindows() in main loop after EndFrame()? Check examples/ applications for reference.");
7812             IM_ASSERT(g.PlatformIO.Platform_CreateWindow  != NULL && "Platform init didn't install handlers?");
7813             IM_ASSERT(g.PlatformIO.Platform_DestroyWindow != NULL && "Platform init didn't install handlers?");
7814             IM_ASSERT(g.PlatformIO.Platform_GetWindowPos  != NULL && "Platform init didn't install handlers?");
7815             IM_ASSERT(g.PlatformIO.Platform_SetWindowPos  != NULL && "Platform init didn't install handlers?");
7816             IM_ASSERT(g.PlatformIO.Platform_GetWindowSize != NULL && "Platform init didn't install handlers?");
7817             IM_ASSERT(g.PlatformIO.Platform_SetWindowSize != NULL && "Platform init didn't install handlers?");
7818             IM_ASSERT(g.PlatformIO.Monitors.Size > 0 && "Platform init didn't setup Monitors list?");
7819             IM_ASSERT((g.Viewports[0]->PlatformUserData != NULL || g.Viewports[0]->PlatformHandle != NULL) && "Platform init didn't setup main viewport.");
7820             if (g.IO.ConfigDockingTransparentPayload && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
7821                 IM_ASSERT(g.PlatformIO.Platform_SetWindowAlpha != NULL && "Platform_SetWindowAlpha handler is required to use io.ConfigDockingTransparent!");
7822         }
7823         else
7824         {
7825             // Disable feature, our backends do not support it
7826             g.IO.ConfigFlags &= ~ImGuiConfigFlags_ViewportsEnable;
7827         }
7828 
7829         // Perform simple checks on platform monitor data + compute a total bounding box for quick early outs
7830         for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++)
7831         {
7832             ImGuiPlatformMonitor& mon = g.PlatformIO.Monitors[monitor_n];
7833             IM_UNUSED(mon);
7834             IM_ASSERT(mon.MainSize.x > 0.0f && mon.MainSize.y > 0.0f && "Monitor main bounds not setup properly.");
7835             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.");
7836             IM_ASSERT(mon.DpiScale != 0.0f);
7837         }
7838     }
7839 }
7840 
ErrorCheckEndFrameSanityChecks()7841 static void ImGui::ErrorCheckEndFrameSanityChecks()
7842 {
7843     ImGuiContext& g = *GImGui;
7844 
7845     // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame()
7846     // One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame().
7847     // It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will
7848     // send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs.
7849     // We silently accommodate for this case by ignoring/ the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0),
7850     // while still correctly asserting on mid-frame key press events.
7851     const ImGuiKeyModFlags key_mod_flags = GetMergedKeyModFlags();
7852     IM_ASSERT((key_mod_flags == 0 || g.IO.KeyMods == key_mod_flags) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
7853     IM_UNUSED(key_mod_flags);
7854 
7855     // Recover from errors
7856     //ErrorCheckEndFrameRecover();
7857 
7858     // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you
7859     // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API).
7860     if (g.CurrentWindowStack.Size != 1)
7861     {
7862         if (g.CurrentWindowStack.Size > 1)
7863         {
7864             IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?");
7865             while (g.CurrentWindowStack.Size > 1)
7866                 End();
7867         }
7868         else
7869         {
7870             IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?");
7871         }
7872     }
7873 
7874     IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, "Missing EndGroup call!");
7875 }
7876 
7877 // Experimental recovery from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.
7878 // Must be called during or before EndFrame().
7879 // This is generally flawed as we are not necessarily End/Popping things in the right order.
7880 // FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.
7881 // FIXME: Can't recover from interleaved BeginTabBar/Begin
ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback,void * user_data)7882 void    ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data)
7883 {
7884     // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations"
7885     ImGuiContext& g = *GImGui;
7886     while (g.CurrentWindowStack.Size > 0)
7887     {
7888         while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow))
7889         {
7890             if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name);
7891             EndTable();
7892         }
7893         ImGuiWindow* window = g.CurrentWindow;
7894         IM_ASSERT(window != NULL);
7895         while (g.CurrentTabBar != NULL) //-V1044
7896         {
7897             if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name);
7898             EndTabBar();
7899         }
7900         while (window->DC.TreeDepth > 0)
7901         {
7902             if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name);
7903             TreePop();
7904         }
7905         while (g.GroupStack.Size > window->DC.StackSizesOnBegin.SizeOfGroupStack)
7906         {
7907             if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name);
7908             EndGroup();
7909         }
7910         while (window->IDStack.Size > 1)
7911         {
7912             if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name);
7913             PopID();
7914         }
7915         while (g.ColorStack.Size > window->DC.StackSizesOnBegin.SizeOfColorStack)
7916         {
7917             if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col));
7918             PopStyleColor();
7919         }
7920         while (g.StyleVarStack.Size > window->DC.StackSizesOnBegin.SizeOfStyleVarStack)
7921         {
7922             if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name);
7923             PopStyleVar();
7924         }
7925         while (g.FocusScopeStack.Size > window->DC.StackSizesOnBegin.SizeOfFocusScopeStack)
7926         {
7927             if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name);
7928             PopFocusScope();
7929         }
7930         if (g.CurrentWindowStack.Size == 1)
7931         {
7932             IM_ASSERT(g.CurrentWindow->IsFallbackWindow);
7933             break;
7934         }
7935         IM_ASSERT(window == g.CurrentWindow);
7936         if (window->Flags & ImGuiWindowFlags_ChildWindow)
7937         {
7938             if (log_callback) log_callback(user_data, "Recovered from missing EndChild() for '%s'", window->Name);
7939             EndChild();
7940         }
7941         else
7942         {
7943             if (log_callback) log_callback(user_data, "Recovered from missing End() for '%s'", window->Name);
7944             End();
7945         }
7946     }
7947 }
7948 
7949 // Save current stack sizes for later compare
SetToCurrentState()7950 void ImGuiStackSizes::SetToCurrentState()
7951 {
7952     ImGuiContext& g = *GImGui;
7953     ImGuiWindow* window = g.CurrentWindow;
7954     SizeOfIDStack = (short)window->IDStack.Size;
7955     SizeOfColorStack = (short)g.ColorStack.Size;
7956     SizeOfStyleVarStack = (short)g.StyleVarStack.Size;
7957     SizeOfFontStack = (short)g.FontStack.Size;
7958     SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size;
7959     SizeOfGroupStack = (short)g.GroupStack.Size;
7960     SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size;
7961 }
7962 
7963 // Compare to detect usage errors
CompareWithCurrentState()7964 void ImGuiStackSizes::CompareWithCurrentState()
7965 {
7966     ImGuiContext& g = *GImGui;
7967     ImGuiWindow* window = g.CurrentWindow;
7968     IM_UNUSED(window);
7969 
7970     // Window stacks
7971     // NOT checking: DC.ItemWidth, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin)
7972     IM_ASSERT(SizeOfIDStack         == window->IDStack.Size     && "PushID/PopID or TreeNode/TreePop Mismatch!");
7973 
7974     // Global stacks
7975     // 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.
7976     IM_ASSERT(SizeOfGroupStack      == g.GroupStack.Size        && "BeginGroup/EndGroup Mismatch!");
7977     IM_ASSERT(SizeOfBeginPopupStack == g.BeginPopupStack.Size   && "BeginPopup/EndPopup or BeginMenu/EndMenu Mismatch!");
7978     IM_ASSERT(SizeOfColorStack      >= g.ColorStack.Size        && "PushStyleColor/PopStyleColor Mismatch!");
7979     IM_ASSERT(SizeOfStyleVarStack   >= g.StyleVarStack.Size     && "PushStyleVar/PopStyleVar Mismatch!");
7980     IM_ASSERT(SizeOfFontStack       >= g.FontStack.Size         && "PushFont/PopFont Mismatch!");
7981     IM_ASSERT(SizeOfFocusScopeStack == g.FocusScopeStack.Size   && "PushFocusScope/PopFocusScope Mismatch!");
7982 }
7983 
7984 
7985 //-----------------------------------------------------------------------------
7986 // [SECTION] LAYOUT
7987 //-----------------------------------------------------------------------------
7988 // - ItemSize()
7989 // - ItemAdd()
7990 // - SameLine()
7991 // - GetCursorScreenPos()
7992 // - SetCursorScreenPos()
7993 // - GetCursorPos(), GetCursorPosX(), GetCursorPosY()
7994 // - SetCursorPos(), SetCursorPosX(), SetCursorPosY()
7995 // - GetCursorStartPos()
7996 // - Indent()
7997 // - Unindent()
7998 // - SetNextItemWidth()
7999 // - PushItemWidth()
8000 // - PushMultiItemsWidths()
8001 // - PopItemWidth()
8002 // - CalcItemWidth()
8003 // - CalcItemSize()
8004 // - GetTextLineHeight()
8005 // - GetTextLineHeightWithSpacing()
8006 // - GetFrameHeight()
8007 // - GetFrameHeightWithSpacing()
8008 // - GetContentRegionMax()
8009 // - GetContentRegionMaxAbs() [Internal]
8010 // - GetContentRegionAvail(),
8011 // - GetWindowContentRegionMin(), GetWindowContentRegionMax()
8012 // - GetWindowContentRegionWidth()
8013 // - BeginGroup()
8014 // - EndGroup()
8015 // Also see in imgui_widgets: tab bars, columns.
8016 //-----------------------------------------------------------------------------
8017 
8018 // Advance cursor given item size for layout.
8019 // Register minimum needed size so it can extend the bounding box used for auto-fit calculation.
8020 // 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)8021 void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
8022 {
8023     ImGuiContext& g = *GImGui;
8024     ImGuiWindow* window = g.CurrentWindow;
8025     if (window->SkipItems)
8026         return;
8027 
8028     // We increase the height in this function to accommodate for baseline offset.
8029     // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
8030     // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
8031     const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
8032     const float line_height = ImMax(window->DC.CurrLineSize.y, size.y + offset_to_match_baseline_y);
8033 
8034     // Always align ourselves on pixel boundaries
8035     //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]
8036     window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
8037     window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y;
8038     window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);    // Next line
8039     window->DC.CursorPos.y = IM_FLOOR(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y);        // Next line
8040     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
8041     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
8042     //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
8043 
8044     window->DC.PrevLineSize.y = line_height;
8045     window->DC.CurrLineSize.y = 0.0f;
8046     window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
8047     window->DC.CurrLineTextBaseOffset = 0.0f;
8048 
8049     // Horizontal layout mode
8050     if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
8051         SameLine();
8052 }
8053 
ItemSize(const ImRect & bb,float text_baseline_y)8054 void ImGui::ItemSize(const ImRect& bb, float text_baseline_y)
8055 {
8056     ItemSize(bb.GetSize(), text_baseline_y);
8057 }
8058 
8059 // Declare item bounding box for clipping and interaction.
8060 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
8061 // declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction.
ItemAdd(const ImRect & bb,ImGuiID id,const ImRect * nav_bb_arg,ImGuiItemAddFlags flags)8062 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemAddFlags flags)
8063 {
8064     ImGuiContext& g = *GImGui;
8065     ImGuiWindow* window = g.CurrentWindow;
8066 
8067     if (id != 0)
8068     {
8069         // Navigation processing runs prior to clipping early-out
8070         //  (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
8071         //  (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
8072         //      unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
8073         //      thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
8074         //      We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
8075         //      to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
8076         // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
8077         // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
8078         window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent);
8079         if (g.NavId == id || g.NavAnyRequest)
8080             if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
8081                 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
8082                     NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
8083 
8084         // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd()
8085 #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
8086         if (id == g.DebugItemPickerBreakId)
8087         {
8088             IM_DEBUG_BREAK();
8089             g.DebugItemPickerBreakId = 0;
8090         }
8091 #endif
8092     }
8093 
8094     // Equivalent to calling SetLastItemData()
8095     window->DC.LastItemId = id;
8096     window->DC.LastItemRect = bb;
8097     window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None;
8098     g.NextItemData.Flags = ImGuiNextItemDataFlags_None;
8099 
8100 #ifdef IMGUI_ENABLE_TEST_ENGINE
8101     if (id != 0)
8102         IMGUI_TEST_ENGINE_ITEM_ADD(nav_bb_arg ? *nav_bb_arg : bb, id);
8103 #endif
8104 
8105     // Clipping test
8106     const bool is_clipped = IsClippedEx(bb, id, false);
8107     if (is_clipped)
8108         return false;
8109     //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
8110 
8111     // Tab stop handling (previously was using internal ItemFocusable() api)
8112     // FIXME-NAV: We would now want to move this above the clipping test, but this would require being able to scroll and currently this would mean an extra frame. (#4079, #343)
8113     if (flags & ImGuiItemAddFlags_Focusable)
8114         ItemFocusable(window, id);
8115 
8116     // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
8117     if (IsMouseHoveringRect(bb.Min, bb.Max))
8118         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect;
8119     return true;
8120 }
8121 
8122 // Gets back to previous line and continue with horizontal layout
8123 //      offset_from_start_x == 0 : follow right after previous item
8124 //      offset_from_start_x != 0 : align to specified x position (relative to window/group left)
8125 //      spacing_w < 0            : use default spacing if pos_x == 0, no spacing if pos_x != 0
8126 //      spacing_w >= 0           : enforce spacing amount
SameLine(float offset_from_start_x,float spacing_w)8127 void ImGui::SameLine(float offset_from_start_x, float spacing_w)
8128 {
8129     ImGuiWindow* window = GetCurrentWindow();
8130     if (window->SkipItems)
8131         return;
8132 
8133     ImGuiContext& g = *GImGui;
8134     if (offset_from_start_x != 0.0f)
8135     {
8136         if (spacing_w < 0.0f) spacing_w = 0.0f;
8137         window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
8138         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
8139     }
8140     else
8141     {
8142         if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
8143         window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
8144         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
8145     }
8146     window->DC.CurrLineSize = window->DC.PrevLineSize;
8147     window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
8148 }
8149 
GetCursorScreenPos()8150 ImVec2 ImGui::GetCursorScreenPos()
8151 {
8152     ImGuiWindow* window = GetCurrentWindowRead();
8153     return window->DC.CursorPos;
8154 }
8155 
SetCursorScreenPos(const ImVec2 & pos)8156 void ImGui::SetCursorScreenPos(const ImVec2& pos)
8157 {
8158     ImGuiWindow* window = GetCurrentWindow();
8159     window->DC.CursorPos = pos;
8160     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
8161 }
8162 
8163 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
8164 // 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()8165 ImVec2 ImGui::GetCursorPos()
8166 {
8167     ImGuiWindow* window = GetCurrentWindowRead();
8168     return window->DC.CursorPos - window->Pos + window->Scroll;
8169 }
8170 
GetCursorPosX()8171 float ImGui::GetCursorPosX()
8172 {
8173     ImGuiWindow* window = GetCurrentWindowRead();
8174     return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
8175 }
8176 
GetCursorPosY()8177 float ImGui::GetCursorPosY()
8178 {
8179     ImGuiWindow* window = GetCurrentWindowRead();
8180     return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
8181 }
8182 
SetCursorPos(const ImVec2 & local_pos)8183 void ImGui::SetCursorPos(const ImVec2& local_pos)
8184 {
8185     ImGuiWindow* window = GetCurrentWindow();
8186     window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
8187     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
8188 }
8189 
SetCursorPosX(float x)8190 void ImGui::SetCursorPosX(float x)
8191 {
8192     ImGuiWindow* window = GetCurrentWindow();
8193     window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
8194     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
8195 }
8196 
SetCursorPosY(float y)8197 void ImGui::SetCursorPosY(float y)
8198 {
8199     ImGuiWindow* window = GetCurrentWindow();
8200     window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
8201     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
8202 }
8203 
GetCursorStartPos()8204 ImVec2 ImGui::GetCursorStartPos()
8205 {
8206     ImGuiWindow* window = GetCurrentWindowRead();
8207     return window->DC.CursorStartPos - window->Pos;
8208 }
8209 
Indent(float indent_w)8210 void ImGui::Indent(float indent_w)
8211 {
8212     ImGuiContext& g = *GImGui;
8213     ImGuiWindow* window = GetCurrentWindow();
8214     window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
8215     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
8216 }
8217 
Unindent(float indent_w)8218 void ImGui::Unindent(float indent_w)
8219 {
8220     ImGuiContext& g = *GImGui;
8221     ImGuiWindow* window = GetCurrentWindow();
8222     window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
8223     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
8224 }
8225 
8226 // Affect large frame+labels widgets only.
SetNextItemWidth(float item_width)8227 void ImGui::SetNextItemWidth(float item_width)
8228 {
8229     ImGuiContext& g = *GImGui;
8230     g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasWidth;
8231     g.NextItemData.Width = item_width;
8232 }
8233 
8234 // FIXME: Remove the == 0.0f behavior?
PushItemWidth(float item_width)8235 void ImGui::PushItemWidth(float item_width)
8236 {
8237     ImGuiContext& g = *GImGui;
8238     ImGuiWindow* window = g.CurrentWindow;
8239     window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width
8240     window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
8241     g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
8242 }
8243 
PushMultiItemsWidths(int components,float w_full)8244 void ImGui::PushMultiItemsWidths(int components, float w_full)
8245 {
8246     ImGuiContext& g = *GImGui;
8247     ImGuiWindow* window = g.CurrentWindow;
8248     const ImGuiStyle& style = g.Style;
8249     const float w_item_one  = ImMax(1.0f, IM_FLOOR((w_full - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components));
8250     const float w_item_last = ImMax(1.0f, IM_FLOOR(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components - 1)));
8251     window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width
8252     window->DC.ItemWidthStack.push_back(w_item_last);
8253     for (int i = 0; i < components - 2; i++)
8254         window->DC.ItemWidthStack.push_back(w_item_one);
8255     window->DC.ItemWidth = (components == 1) ? w_item_last : w_item_one;
8256     g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
8257 }
8258 
PopItemWidth()8259 void ImGui::PopItemWidth()
8260 {
8261     ImGuiWindow* window = GetCurrentWindow();
8262     window->DC.ItemWidth = window->DC.ItemWidthStack.back();
8263     window->DC.ItemWidthStack.pop_back();
8264 }
8265 
8266 // Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().
8267 // The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()
CalcItemWidth()8268 float ImGui::CalcItemWidth()
8269 {
8270     ImGuiContext& g = *GImGui;
8271     ImGuiWindow* window = g.CurrentWindow;
8272     float w;
8273     if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth)
8274         w = g.NextItemData.Width;
8275     else
8276         w = window->DC.ItemWidth;
8277     if (w < 0.0f)
8278     {
8279         float region_max_x = GetContentRegionMaxAbs().x;
8280         w = ImMax(1.0f, region_max_x - window->DC.CursorPos.x + w);
8281     }
8282     w = IM_FLOOR(w);
8283     return w;
8284 }
8285 
8286 // [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().
8287 // Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.
8288 // Note that only CalcItemWidth() is publicly exposed.
8289 // 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)8290 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)
8291 {
8292     ImGuiWindow* window = GImGui->CurrentWindow;
8293 
8294     ImVec2 region_max;
8295     if (size.x < 0.0f || size.y < 0.0f)
8296         region_max = GetContentRegionMaxAbs();
8297 
8298     if (size.x == 0.0f)
8299         size.x = default_w;
8300     else if (size.x < 0.0f)
8301         size.x = ImMax(4.0f, region_max.x - window->DC.CursorPos.x + size.x);
8302 
8303     if (size.y == 0.0f)
8304         size.y = default_h;
8305     else if (size.y < 0.0f)
8306         size.y = ImMax(4.0f, region_max.y - window->DC.CursorPos.y + size.y);
8307 
8308     return size;
8309 }
8310 
GetTextLineHeight()8311 float ImGui::GetTextLineHeight()
8312 {
8313     ImGuiContext& g = *GImGui;
8314     return g.FontSize;
8315 }
8316 
GetTextLineHeightWithSpacing()8317 float ImGui::GetTextLineHeightWithSpacing()
8318 {
8319     ImGuiContext& g = *GImGui;
8320     return g.FontSize + g.Style.ItemSpacing.y;
8321 }
8322 
GetFrameHeight()8323 float ImGui::GetFrameHeight()
8324 {
8325     ImGuiContext& g = *GImGui;
8326     return g.FontSize + g.Style.FramePadding.y * 2.0f;
8327 }
8328 
GetFrameHeightWithSpacing()8329 float ImGui::GetFrameHeightWithSpacing()
8330 {
8331     ImGuiContext& g = *GImGui;
8332     return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
8333 }
8334 
8335 // 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!
8336 
8337 // FIXME: This is in window space (not screen space!).
GetContentRegionMax()8338 ImVec2 ImGui::GetContentRegionMax()
8339 {
8340     ImGuiContext& g = *GImGui;
8341     ImGuiWindow* window = g.CurrentWindow;
8342     ImVec2 mx = window->ContentRegionRect.Max - window->Pos;
8343     if (window->DC.CurrentColumns || g.CurrentTable)
8344         mx.x = window->WorkRect.Max.x - window->Pos.x;
8345     return mx;
8346 }
8347 
8348 // [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features.
GetContentRegionMaxAbs()8349 ImVec2 ImGui::GetContentRegionMaxAbs()
8350 {
8351     ImGuiContext& g = *GImGui;
8352     ImGuiWindow* window = g.CurrentWindow;
8353     ImVec2 mx = window->ContentRegionRect.Max;
8354     if (window->DC.CurrentColumns || g.CurrentTable)
8355         mx.x = window->WorkRect.Max.x;
8356     return mx;
8357 }
8358 
GetContentRegionAvail()8359 ImVec2 ImGui::GetContentRegionAvail()
8360 {
8361     ImGuiWindow* window = GImGui->CurrentWindow;
8362     return GetContentRegionMaxAbs() - window->DC.CursorPos;
8363 }
8364 
8365 // In window space (not screen space!)
GetWindowContentRegionMin()8366 ImVec2 ImGui::GetWindowContentRegionMin()
8367 {
8368     ImGuiWindow* window = GImGui->CurrentWindow;
8369     return window->ContentRegionRect.Min - window->Pos;
8370 }
8371 
GetWindowContentRegionMax()8372 ImVec2 ImGui::GetWindowContentRegionMax()
8373 {
8374     ImGuiWindow* window = GImGui->CurrentWindow;
8375     return window->ContentRegionRect.Max - window->Pos;
8376 }
8377 
GetWindowContentRegionWidth()8378 float ImGui::GetWindowContentRegionWidth()
8379 {
8380     ImGuiWindow* window = GImGui->CurrentWindow;
8381     return window->ContentRegionRect.GetWidth();
8382 }
8383 
8384 // 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.)
8385 // Groups are currently a mishmash of functionalities which should perhaps be clarified and separated.
BeginGroup()8386 void ImGui::BeginGroup()
8387 {
8388     ImGuiContext& g = *GImGui;
8389     ImGuiWindow* window = g.CurrentWindow;
8390 
8391     g.GroupStack.resize(g.GroupStack.Size + 1);
8392     ImGuiGroupData& group_data = g.GroupStack.back();
8393     group_data.WindowID = window->ID;
8394     group_data.BackupCursorPos = window->DC.CursorPos;
8395     group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
8396     group_data.BackupIndent = window->DC.Indent;
8397     group_data.BackupGroupOffset = window->DC.GroupOffset;
8398     group_data.BackupCurrLineSize = window->DC.CurrLineSize;
8399     group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;
8400     group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
8401     group_data.BackupHoveredIdIsAlive = g.HoveredId != 0;
8402     group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
8403     group_data.EmitItem = true;
8404 
8405     window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
8406     window->DC.Indent = window->DC.GroupOffset;
8407     window->DC.CursorMaxPos = window->DC.CursorPos;
8408     window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
8409     if (g.LogEnabled)
8410         g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
8411 }
8412 
EndGroup()8413 void ImGui::EndGroup()
8414 {
8415     ImGuiContext& g = *GImGui;
8416     ImGuiWindow* window = g.CurrentWindow;
8417     IM_ASSERT(g.GroupStack.Size > 0); // Mismatched BeginGroup()/EndGroup() calls
8418 
8419     ImGuiGroupData& group_data = g.GroupStack.back();
8420     IM_ASSERT(group_data.WindowID == window->ID); // EndGroup() in wrong window?
8421 
8422     ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos));
8423 
8424     window->DC.CursorPos = group_data.BackupCursorPos;
8425     window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
8426     window->DC.Indent = group_data.BackupIndent;
8427     window->DC.GroupOffset = group_data.BackupGroupOffset;
8428     window->DC.CurrLineSize = group_data.BackupCurrLineSize;
8429     window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;
8430     if (g.LogEnabled)
8431         g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
8432 
8433     if (!group_data.EmitItem)
8434     {
8435         g.GroupStack.pop_back();
8436         return;
8437     }
8438 
8439     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.
8440     ItemSize(group_bb.GetSize());
8441     ItemAdd(group_bb, 0);
8442 
8443     // 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.
8444     // 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.
8445     // Also if you grep for LastItemId you'll notice it is only used in that context.
8446     // (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.)
8447     const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId;
8448     const bool group_contains_prev_active_id = (group_data.BackupActiveIdPreviousFrameIsAlive == false) && (g.ActiveIdPreviousFrameIsAlive == true);
8449     if (group_contains_curr_active_id)
8450         window->DC.LastItemId = g.ActiveId;
8451     else if (group_contains_prev_active_id)
8452         window->DC.LastItemId = g.ActiveIdPreviousFrame;
8453     window->DC.LastItemRect = group_bb;
8454 
8455     // Forward Hovered flag
8456     const bool group_contains_curr_hovered_id = (group_data.BackupHoveredIdIsAlive == false) && g.HoveredId != 0;
8457     if (group_contains_curr_hovered_id)
8458         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
8459 
8460     // Forward Edited flag
8461     if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame)
8462         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
8463 
8464     // Forward Deactivated flag
8465     window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDeactivated;
8466     if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame)
8467         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Deactivated;
8468 
8469     g.GroupStack.pop_back();
8470     //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255));   // [Debug]
8471 }
8472 
8473 
8474 //-----------------------------------------------------------------------------
8475 // [SECTION] SCROLLING
8476 //-----------------------------------------------------------------------------
8477 
8478 // Helper to snap on edges when aiming at an item very close to the edge,
8479 // So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling.
8480 // When we refactor the scrolling API this may be configurable with a flag?
8481 // 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)8482 static float CalcScrollEdgeSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio)
8483 {
8484     if (target <= snap_min + snap_threshold)
8485         return ImLerp(snap_min, target, center_ratio);
8486     if (target >= snap_max - snap_threshold)
8487         return ImLerp(target, snap_max, center_ratio);
8488     return target;
8489 }
8490 
CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow * window)8491 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
8492 {
8493     ImVec2 scroll = window->Scroll;
8494     if (window->ScrollTarget.x < FLT_MAX)
8495     {
8496         float decoration_total_width = window->ScrollbarSizes.x;
8497         float center_x_ratio = window->ScrollTargetCenterRatio.x;
8498         float scroll_target_x = window->ScrollTarget.x;
8499         if (window->ScrollTargetEdgeSnapDist.x > 0.0f)
8500         {
8501             float snap_x_min = 0.0f;
8502             float snap_x_max = window->ScrollMax.x + window->SizeFull.x - decoration_total_width;
8503             scroll_target_x = CalcScrollEdgeSnap(scroll_target_x, snap_x_min, snap_x_max, window->ScrollTargetEdgeSnapDist.x, center_x_ratio);
8504         }
8505         scroll.x = scroll_target_x - center_x_ratio * (window->SizeFull.x - decoration_total_width);
8506     }
8507     if (window->ScrollTarget.y < FLT_MAX)
8508     {
8509         float decoration_total_height = window->TitleBarHeight() + window->MenuBarHeight() + window->ScrollbarSizes.y;
8510         float center_y_ratio = window->ScrollTargetCenterRatio.y;
8511         float scroll_target_y = window->ScrollTarget.y;
8512         if (window->ScrollTargetEdgeSnapDist.y > 0.0f)
8513         {
8514             float snap_y_min = 0.0f;
8515             float snap_y_max = window->ScrollMax.y + window->SizeFull.y - decoration_total_height;
8516             scroll_target_y = CalcScrollEdgeSnap(scroll_target_y, snap_y_min, snap_y_max, window->ScrollTargetEdgeSnapDist.y, center_y_ratio);
8517         }
8518         scroll.y = scroll_target_y - center_y_ratio * (window->SizeFull.y - decoration_total_height);
8519     }
8520     scroll.x = IM_FLOOR(ImMax(scroll.x, 0.0f));
8521     scroll.y = IM_FLOOR(ImMax(scroll.y, 0.0f));
8522     if (!window->Collapsed && !window->SkipItems)
8523     {
8524         scroll.x = ImMin(scroll.x, window->ScrollMax.x);
8525         scroll.y = ImMin(scroll.y, window->ScrollMax.y);
8526     }
8527     return scroll;
8528 }
8529 
8530 // Scroll to keep newly navigated item fully into view
ScrollToBringRectIntoView(ImGuiWindow * window,const ImRect & item_rect)8531 ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect)
8532 {
8533     ImGuiContext& g = *GImGui;
8534     ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));
8535     //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
8536 
8537     ImVec2 delta_scroll;
8538     if (!window_rect.Contains(item_rect))
8539     {
8540         if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x)
8541             SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x - g.Style.ItemSpacing.x, 0.0f);
8542         else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x)
8543             SetScrollFromPosX(window, item_rect.Max.x - window->Pos.x + g.Style.ItemSpacing.x, 1.0f);
8544         if (item_rect.Min.y < window_rect.Min.y)
8545             SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y - g.Style.ItemSpacing.y, 0.0f);
8546         else if (item_rect.Max.y >= window_rect.Max.y)
8547             SetScrollFromPosY(window, item_rect.Max.y - window->Pos.y + g.Style.ItemSpacing.y, 1.0f);
8548 
8549         ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
8550         delta_scroll = next_scroll - window->Scroll;
8551     }
8552 
8553     // Also scroll parent window to keep us into view if necessary
8554     if (window->Flags & ImGuiWindowFlags_ChildWindow)
8555         delta_scroll += ScrollToBringRectIntoView(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll));
8556 
8557     return delta_scroll;
8558 }
8559 
GetScrollX()8560 float ImGui::GetScrollX()
8561 {
8562     ImGuiWindow* window = GImGui->CurrentWindow;
8563     return window->Scroll.x;
8564 }
8565 
GetScrollY()8566 float ImGui::GetScrollY()
8567 {
8568     ImGuiWindow* window = GImGui->CurrentWindow;
8569     return window->Scroll.y;
8570 }
8571 
GetScrollMaxX()8572 float ImGui::GetScrollMaxX()
8573 {
8574     ImGuiWindow* window = GImGui->CurrentWindow;
8575     return window->ScrollMax.x;
8576 }
8577 
GetScrollMaxY()8578 float ImGui::GetScrollMaxY()
8579 {
8580     ImGuiWindow* window = GImGui->CurrentWindow;
8581     return window->ScrollMax.y;
8582 }
8583 
SetScrollX(ImGuiWindow * window,float scroll_x)8584 void ImGui::SetScrollX(ImGuiWindow* window, float scroll_x)
8585 {
8586     window->ScrollTarget.x = scroll_x;
8587     window->ScrollTargetCenterRatio.x = 0.0f;
8588     window->ScrollTargetEdgeSnapDist.x = 0.0f;
8589 }
8590 
SetScrollY(ImGuiWindow * window,float scroll_y)8591 void ImGui::SetScrollY(ImGuiWindow* window, float scroll_y)
8592 {
8593     window->ScrollTarget.y = scroll_y;
8594     window->ScrollTargetCenterRatio.y = 0.0f;
8595     window->ScrollTargetEdgeSnapDist.y = 0.0f;
8596 }
8597 
SetScrollX(float scroll_x)8598 void ImGui::SetScrollX(float scroll_x)
8599 {
8600     ImGuiContext& g = *GImGui;
8601     SetScrollX(g.CurrentWindow, scroll_x);
8602 }
8603 
SetScrollY(float scroll_y)8604 void ImGui::SetScrollY(float scroll_y)
8605 {
8606     ImGuiContext& g = *GImGui;
8607     SetScrollY(g.CurrentWindow, scroll_y);
8608 }
8609 
8610 // Note that a local position will vary depending on initial scroll value,
8611 // This is a little bit confusing so bear with us:
8612 //  - local_pos = (absolution_pos - window->Pos)
8613 //  - So local_x/local_y are 0.0f for a position at the upper-left corner of a window,
8614 //    and generally local_x/local_y are >(padding+decoration) && <(size-padding-decoration) when in the visible area.
8615 //  - They mostly exists because of legacy API.
8616 // Following the rules above, when trying to work with scrolling code, consider that:
8617 //  - SetScrollFromPosY(0.0f) == SetScrollY(0.0f + scroll.y) == has no effect!
8618 //  - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0f) == reset scroll. Of course writing SetScrollY(0.0f) directly then makes more sense
8619 // 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)8620 void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
8621 {
8622     IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);
8623     window->ScrollTarget.x = IM_FLOOR(local_x + window->Scroll.x); // Convert local position to scroll offset
8624     window->ScrollTargetCenterRatio.x = center_x_ratio;
8625     window->ScrollTargetEdgeSnapDist.x = 0.0f;
8626 }
8627 
SetScrollFromPosY(ImGuiWindow * window,float local_y,float center_y_ratio)8628 void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
8629 {
8630     IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
8631     const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); // FIXME: Would be nice to have a more standardized access to our scrollable/client rect;
8632     local_y -= decoration_up_height;
8633     window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y); // Convert local position to scroll offset
8634     window->ScrollTargetCenterRatio.y = center_y_ratio;
8635     window->ScrollTargetEdgeSnapDist.y = 0.0f;
8636 }
8637 
SetScrollFromPosX(float local_x,float center_x_ratio)8638 void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)
8639 {
8640     ImGuiContext& g = *GImGui;
8641     SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio);
8642 }
8643 
SetScrollFromPosY(float local_y,float center_y_ratio)8644 void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
8645 {
8646     ImGuiContext& g = *GImGui;
8647     SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio);
8648 }
8649 
8650 // 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)8651 void ImGui::SetScrollHereX(float center_x_ratio)
8652 {
8653     ImGuiContext& g = *GImGui;
8654     ImGuiWindow* window = g.CurrentWindow;
8655     float spacing_x = ImMax(window->WindowPadding.x, g.Style.ItemSpacing.x);
8656     float target_pos_x = ImLerp(window->DC.LastItemRect.Min.x - spacing_x, window->DC.LastItemRect.Max.x + spacing_x, center_x_ratio);
8657     SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos
8658 
8659     // Tweak: snap on edges when aiming at an item very close to the edge
8660     window->ScrollTargetEdgeSnapDist.x = ImMax(0.0f, window->WindowPadding.x - spacing_x);
8661 }
8662 
8663 // 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)8664 void ImGui::SetScrollHereY(float center_y_ratio)
8665 {
8666     ImGuiContext& g = *GImGui;
8667     ImGuiWindow* window = g.CurrentWindow;
8668     float spacing_y = ImMax(window->WindowPadding.y, g.Style.ItemSpacing.y);
8669     float target_pos_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio);
8670     SetScrollFromPosY(window, target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos
8671 
8672     // Tweak: snap on edges when aiming at an item very close to the edge
8673     window->ScrollTargetEdgeSnapDist.y = ImMax(0.0f, window->WindowPadding.y - spacing_y);
8674 }
8675 
8676 //-----------------------------------------------------------------------------
8677 // [SECTION] TOOLTIPS
8678 //-----------------------------------------------------------------------------
8679 
BeginTooltip()8680 void ImGui::BeginTooltip()
8681 {
8682     BeginTooltipEx(ImGuiWindowFlags_None, ImGuiTooltipFlags_None);
8683 }
8684 
BeginTooltipEx(ImGuiWindowFlags extra_flags,ImGuiTooltipFlags tooltip_flags)8685 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags)
8686 {
8687     ImGuiContext& g = *GImGui;
8688 
8689     if (g.DragDropWithinSource || g.DragDropWithinTarget)
8690     {
8691         // 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)
8692         // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
8693         // 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.
8694         //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
8695         ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale);
8696         SetNextWindowPos(tooltip_pos);
8697         SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
8698         //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
8699         tooltip_flags |= ImGuiTooltipFlags_OverridePreviousTooltip;
8700     }
8701 
8702     char window_name[16];
8703     ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
8704     if (tooltip_flags & ImGuiTooltipFlags_OverridePreviousTooltip)
8705         if (ImGuiWindow* window = FindWindowByName(window_name))
8706             if (window->Active)
8707             {
8708                 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
8709                 window->Hidden = true;
8710                 window->HiddenFramesCanSkipItems = 1; // FIXME: This may not be necessary?
8711                 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
8712             }
8713     ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking;
8714     Begin(window_name, NULL, flags | extra_flags);
8715 }
8716 
EndTooltip()8717 void ImGui::EndTooltip()
8718 {
8719     IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip);   // Mismatched BeginTooltip()/EndTooltip() calls
8720     End();
8721 }
8722 
SetTooltipV(const char * fmt,va_list args)8723 void ImGui::SetTooltipV(const char* fmt, va_list args)
8724 {
8725     BeginTooltipEx(0, ImGuiTooltipFlags_OverridePreviousTooltip);
8726     TextV(fmt, args);
8727     EndTooltip();
8728 }
8729 
SetTooltip(const char * fmt,...)8730 void ImGui::SetTooltip(const char* fmt, ...)
8731 {
8732     va_list args;
8733     va_start(args, fmt);
8734     SetTooltipV(fmt, args);
8735     va_end(args);
8736 }
8737 
8738 //-----------------------------------------------------------------------------
8739 // [SECTION] POPUPS
8740 //-----------------------------------------------------------------------------
8741 
8742 // Supported flags: ImGuiPopupFlags_AnyPopupId, ImGuiPopupFlags_AnyPopupLevel
IsPopupOpen(ImGuiID id,ImGuiPopupFlags popup_flags)8743 bool ImGui::IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags)
8744 {
8745     ImGuiContext& g = *GImGui;
8746     if (popup_flags & ImGuiPopupFlags_AnyPopupId)
8747     {
8748         // Return true if any popup is open at the current BeginPopup() level of the popup stack
8749         // This may be used to e.g. test for another popups already opened to handle popups priorities at the same level.
8750         IM_ASSERT(id == 0);
8751         if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
8752             return g.OpenPopupStack.Size > 0;
8753         else
8754             return g.OpenPopupStack.Size > g.BeginPopupStack.Size;
8755     }
8756     else
8757     {
8758         if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
8759         {
8760             // Return true if the popup is open anywhere in the popup stack
8761             for (int n = 0; n < g.OpenPopupStack.Size; n++)
8762                 if (g.OpenPopupStack[n].PopupId == id)
8763                     return true;
8764             return false;
8765         }
8766         else
8767         {
8768             // Return true if the popup is open at the current BeginPopup() level of the popup stack (this is the most-common query)
8769             return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
8770         }
8771     }
8772 }
8773 
IsPopupOpen(const char * str_id,ImGuiPopupFlags popup_flags)8774 bool ImGui::IsPopupOpen(const char* str_id, ImGuiPopupFlags popup_flags)
8775 {
8776     ImGuiContext& g = *GImGui;
8777     ImGuiID id = (popup_flags & ImGuiPopupFlags_AnyPopupId) ? 0 : g.CurrentWindow->GetID(str_id);
8778     if ((popup_flags & ImGuiPopupFlags_AnyPopupLevel) && id != 0)
8779         IM_ASSERT(0 && "Cannot use IsPopupOpen() with a string id and ImGuiPopupFlags_AnyPopupLevel."); // But non-string version is legal and used internally
8780     return IsPopupOpen(id, popup_flags);
8781 }
8782 
GetTopMostPopupModal()8783 ImGuiWindow* ImGui::GetTopMostPopupModal()
8784 {
8785     ImGuiContext& g = *GImGui;
8786     for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
8787         if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
8788             if (popup->Flags & ImGuiWindowFlags_Modal)
8789                 return popup;
8790     return NULL;
8791 }
8792 
OpenPopup(const char * str_id,ImGuiPopupFlags popup_flags)8793 void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags)
8794 {
8795     ImGuiContext& g = *GImGui;
8796     OpenPopupEx(g.CurrentWindow->GetID(str_id), popup_flags);
8797 }
8798 
OpenPopup(ImGuiID id,ImGuiPopupFlags popup_flags)8799 void ImGui::OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags)
8800 {
8801     OpenPopupEx(id, popup_flags);
8802 }
8803 
8804 // Mark popup as open (toggle toward open state).
8805 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
8806 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
8807 // 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)8808 void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags)
8809 {
8810     ImGuiContext& g = *GImGui;
8811     ImGuiWindow* parent_window = g.CurrentWindow;
8812     const int current_stack_size = g.BeginPopupStack.Size;
8813 
8814     if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup)
8815         if (IsPopupOpen(0u, ImGuiPopupFlags_AnyPopupId))
8816             return;
8817 
8818     ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
8819     popup_ref.PopupId = id;
8820     popup_ref.Window = NULL;
8821     popup_ref.SourceWindow = g.NavWindow;
8822     popup_ref.OpenFrameCount = g.FrameCount;
8823     popup_ref.OpenParentId = parent_window->IDStack.back();
8824     popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
8825     popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
8826 
8827     IMGUI_DEBUG_LOG_POPUP("OpenPopupEx(0x%08X)\n", id);
8828     if (g.OpenPopupStack.Size < current_stack_size + 1)
8829     {
8830         g.OpenPopupStack.push_back(popup_ref);
8831     }
8832     else
8833     {
8834         // 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
8835         // 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
8836         // 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.
8837         if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
8838         {
8839             g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
8840         }
8841         else
8842         {
8843             // Close child popups if any, then flag popup for open/reopen
8844             ClosePopupToLevel(current_stack_size, false);
8845             g.OpenPopupStack.push_back(popup_ref);
8846         }
8847 
8848         // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
8849         // This is equivalent to what ClosePopupToLevel() does.
8850         //if (g.OpenPopupStack[current_stack_size].PopupId == id)
8851         //    FocusWindow(parent_window);
8852     }
8853 }
8854 
8855 // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
8856 // This function closes any popups that are over 'ref_window'.
ClosePopupsOverWindow(ImGuiWindow * ref_window,bool restore_focus_to_window_under_popup)8857 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
8858 {
8859     ImGuiContext& g = *GImGui;
8860     if (g.OpenPopupStack.Size == 0)
8861         return;
8862 
8863     // Don't close our own child popup windows.
8864     int popup_count_to_keep = 0;
8865     if (ref_window)
8866     {
8867         // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
8868         for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
8869         {
8870             ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];
8871             if (!popup.Window)
8872                 continue;
8873             IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
8874             if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
8875                 continue;
8876 
8877             // Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow)
8878             // - With this stack of window, clicking/focusing Popup1 will close Popup2 and Popup3:
8879             //     Window -> Popup1 -> Popup2 -> Popup3
8880             // - Each popups may contain child windows, which is why we compare ->RootWindowDockTree!
8881             //     Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child
8882             bool ref_window_is_descendent_of_popup = false;
8883             for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++)
8884                 if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window)
8885                     if (popup_window->RootWindowDockTree == ref_window->RootWindowDockTree)
8886                     {
8887                         ref_window_is_descendent_of_popup = true;
8888                         break;
8889                     }
8890             if (!ref_window_is_descendent_of_popup)
8891                 break;
8892         }
8893     }
8894     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
8895     {
8896         IMGUI_DEBUG_LOG_POPUP("ClosePopupsOverWindow(\"%s\") -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep);
8897         ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup);
8898     }
8899 }
8900 
ClosePopupToLevel(int remaining,bool restore_focus_to_window_under_popup)8901 void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
8902 {
8903     ImGuiContext& g = *GImGui;
8904     IMGUI_DEBUG_LOG_POPUP("ClosePopupToLevel(%d), restore_focus_to_window_under_popup=%d\n", remaining, restore_focus_to_window_under_popup);
8905     IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
8906 
8907     // Trim open popup stack
8908     ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow;
8909     ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window;
8910     g.OpenPopupStack.resize(remaining);
8911 
8912     if (restore_focus_to_window_under_popup)
8913     {
8914         if (focus_window && !focus_window->WasActive && popup_window)
8915         {
8916             // Fallback
8917             FocusTopMostWindowUnderOne(popup_window, NULL);
8918         }
8919         else
8920         {
8921             if (g.NavLayer == ImGuiNavLayer_Main && focus_window)
8922                 focus_window = NavRestoreLastChildNavWindow(focus_window);
8923             FocusWindow(focus_window);
8924         }
8925     }
8926 }
8927 
8928 // Close the popup we have begin-ed into.
CloseCurrentPopup()8929 void ImGui::CloseCurrentPopup()
8930 {
8931     ImGuiContext& g = *GImGui;
8932     int popup_idx = g.BeginPopupStack.Size - 1;
8933     if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
8934         return;
8935 
8936     // Closing a menu closes its top-most parent popup (unless a modal)
8937     while (popup_idx > 0)
8938     {
8939         ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;
8940         ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
8941         bool close_parent = false;
8942         if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
8943             if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal))
8944                 close_parent = true;
8945         if (!close_parent)
8946             break;
8947         popup_idx--;
8948     }
8949     IMGUI_DEBUG_LOG_POPUP("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx);
8950     ClosePopupToLevel(popup_idx, true);
8951 
8952     // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
8953     // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
8954     // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
8955     if (ImGuiWindow* window = g.NavWindow)
8956         window->DC.NavHideHighlightOneFrame = true;
8957 }
8958 
8959 // Attention! BeginPopup() adds default flags which BeginPopupEx()!
BeginPopupEx(ImGuiID id,ImGuiWindowFlags flags)8960 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags)
8961 {
8962     ImGuiContext& g = *GImGui;
8963     if (!IsPopupOpen(id, ImGuiPopupFlags_None))
8964     {
8965         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
8966         return false;
8967     }
8968 
8969     char name[20];
8970     if (flags & ImGuiWindowFlags_ChildMenu)
8971         ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth
8972     else
8973         ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
8974 
8975     flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoDocking;
8976     bool is_open = Begin(name, NULL, flags);
8977     if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
8978         EndPopup();
8979 
8980     return is_open;
8981 }
8982 
BeginPopup(const char * str_id,ImGuiWindowFlags flags)8983 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
8984 {
8985     ImGuiContext& g = *GImGui;
8986     if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
8987     {
8988         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
8989         return false;
8990     }
8991     flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
8992     return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags);
8993 }
8994 
8995 // If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
8996 // 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)8997 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
8998 {
8999     ImGuiContext& g = *GImGui;
9000     ImGuiWindow* window = g.CurrentWindow;
9001     const ImGuiID id = window->GetID(name);
9002     if (!IsPopupOpen(id, ImGuiPopupFlags_None))
9003     {
9004         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
9005         return false;
9006     }
9007 
9008     // Center modal windows by default for increased visibility
9009     // (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves)
9010     // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
9011     if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
9012     {
9013         const ImGuiViewport* viewport = window->WasActive ? window->Viewport : GetMainViewport(); // FIXME-VIEWPORT: What may be our reference viewport?
9014         SetNextWindowPos(viewport->GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
9015     }
9016 
9017     flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking;
9018     const bool is_open = Begin(name, p_open, flags);
9019     if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
9020     {
9021         EndPopup();
9022         if (is_open)
9023             ClosePopupToLevel(g.BeginPopupStack.Size, true);
9024         return false;
9025     }
9026     return is_open;
9027 }
9028 
EndPopup()9029 void ImGui::EndPopup()
9030 {
9031     ImGuiContext& g = *GImGui;
9032     ImGuiWindow* window = g.CurrentWindow;
9033     IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup);  // Mismatched BeginPopup()/EndPopup() calls
9034     IM_ASSERT(g.BeginPopupStack.Size > 0);
9035 
9036     // Make all menus and popups wrap around for now, may need to expose that policy.
9037     if (g.NavWindow == window)
9038         NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
9039 
9040     // Child-popups don't need to be laid out
9041     IM_ASSERT(g.WithinEndChild == false);
9042     if (window->Flags & ImGuiWindowFlags_ChildWindow)
9043         g.WithinEndChild = true;
9044     End();
9045     g.WithinEndChild = false;
9046 }
9047 
9048 // Helper to open a popup if mouse button is released over the item
9049 // - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup()
OpenPopupOnItemClick(const char * str_id,ImGuiPopupFlags popup_flags)9050 void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags)
9051 {
9052     ImGuiWindow* window = GImGui->CurrentWindow;
9053     int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
9054     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
9055     {
9056         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!
9057         IM_ASSERT(id != 0);                                                  // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
9058         OpenPopupEx(id, popup_flags);
9059     }
9060 }
9061 
9062 // This is a helper to handle the simplest case of associating one named popup to one given widget.
9063 // - To create a popup associated to the last item, you generally want to pass a NULL value to str_id.
9064 // - To create a popup with a specific identifier, pass it in str_id.
9065 //    - This is useful when using using BeginPopupContextItem() on an item which doesn't have an identifier, e.g. a Text() call.
9066 //    - This is useful when multiple code locations may want to manipulate/open the same popup, given an explicit id.
9067 // - You may want to handle the whole on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
9068 //   This is essentially the same as:
9069 //       id = str_id ? GetID(str_id) : GetItemID();
9070 //       OpenPopupOnItemClick(str_id);
9071 //       return BeginPopup(id);
9072 //   Which is essentially the same as:
9073 //       id = str_id ? GetID(str_id) : GetItemID();
9074 //       if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))
9075 //           OpenPopup(id);
9076 //       return BeginPopup(id);
9077 //   The main difference being that this is tweaked to avoid computing the ID twice.
BeginPopupContextItem(const char * str_id,ImGuiPopupFlags popup_flags)9078 bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags)
9079 {
9080     ImGuiWindow* window = GImGui->CurrentWindow;
9081     if (window->SkipItems)
9082         return false;
9083     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!
9084     IM_ASSERT(id != 0);                                                  // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
9085     int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
9086     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
9087         OpenPopupEx(id, popup_flags);
9088     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
9089 }
9090 
BeginPopupContextWindow(const char * str_id,ImGuiPopupFlags popup_flags)9091 bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags)
9092 {
9093     ImGuiWindow* window = GImGui->CurrentWindow;
9094     if (!str_id)
9095         str_id = "window_context";
9096     ImGuiID id = window->GetID(str_id);
9097     int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
9098     if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
9099         if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered())
9100             OpenPopupEx(id, popup_flags);
9101     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
9102 }
9103 
BeginPopupContextVoid(const char * str_id,ImGuiPopupFlags popup_flags)9104 bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags)
9105 {
9106     ImGuiWindow* window = GImGui->CurrentWindow;
9107     if (!str_id)
9108         str_id = "void_context";
9109     ImGuiID id = window->GetID(str_id);
9110     int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
9111     if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
9112         if (GetTopMostPopupModal() == NULL)
9113             OpenPopupEx(id, popup_flags);
9114     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
9115 }
9116 
9117 // 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.)
9118 // 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.
9119 // (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor
9120 //  information are available, it may represent the entire platform monitor from the frame of reference of the current viewport.
9121 //  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)9122 ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
9123 {
9124     ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
9125     //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
9126     //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
9127 
9128     // Combo Box policy (we want a connecting edge)
9129     if (policy == ImGuiPopupPositionPolicy_ComboBox)
9130     {
9131         const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
9132         for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
9133         {
9134             const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
9135             if (n != -1 && dir == *last_dir) // Already tried this direction?
9136                 continue;
9137             ImVec2 pos;
9138             if (dir == ImGuiDir_Down)  pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y);          // Below, Toward Right (default)
9139             if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
9140             if (dir == ImGuiDir_Left)  pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
9141             if (dir == ImGuiDir_Up)    pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
9142             if (!r_outer.Contains(ImRect(pos, pos + size)))
9143                 continue;
9144             *last_dir = dir;
9145             return pos;
9146         }
9147     }
9148 
9149     // Tooltip and Default popup policy
9150     // (Always first try the direction we used on the last frame, if any)
9151     if (policy == ImGuiPopupPositionPolicy_Tooltip || policy == ImGuiPopupPositionPolicy_Default)
9152     {
9153         const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
9154         for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
9155         {
9156             const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
9157             if (n != -1 && dir == *last_dir) // Already tried this direction?
9158                 continue;
9159 
9160             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);
9161             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);
9162 
9163             // 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)
9164             if (avail_w < size.x && (dir == ImGuiDir_Left || dir == ImGuiDir_Right))
9165                 continue;
9166             if (avail_h < size.y && (dir == ImGuiDir_Up || dir == ImGuiDir_Down))
9167                 continue;
9168 
9169             ImVec2 pos;
9170             pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
9171             pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
9172 
9173             // Clamp top-left corner of popup
9174             pos.x = ImMax(pos.x, r_outer.Min.x);
9175             pos.y = ImMax(pos.y, r_outer.Min.y);
9176 
9177             *last_dir = dir;
9178             return pos;
9179         }
9180     }
9181 
9182     // Fallback when not enough room:
9183     *last_dir = ImGuiDir_None;
9184 
9185     // For tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
9186     if (policy == ImGuiPopupPositionPolicy_Tooltip)
9187         return ref_pos + ImVec2(2, 2);
9188 
9189     // Otherwise try to keep within display
9190     ImVec2 pos = ref_pos;
9191     pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
9192     pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
9193     return pos;
9194 }
9195 
9196 // Note that this is used for popups, which can overlap the non work-area of individual viewports.
GetPopupAllowedExtentRect(ImGuiWindow * window)9197 ImRect ImGui::GetPopupAllowedExtentRect(ImGuiWindow* window)
9198 {
9199     ImGuiContext& g = *GImGui;
9200     ImRect r_screen;
9201     if (window->ViewportAllowPlatformMonitorExtend >= 0)
9202     {
9203         // Extent with be in the frame of reference of the given viewport (so Min is likely to be negative here)
9204         const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->ViewportAllowPlatformMonitorExtend];
9205         r_screen.Min = monitor.WorkPos;
9206         r_screen.Max = monitor.WorkPos + monitor.WorkSize;
9207     }
9208     else
9209     {
9210         // Use the full viewport area (not work area) for popups
9211         r_screen = window->Viewport->GetMainRect();
9212     }
9213     ImVec2 padding = g.Style.DisplaySafeAreaPadding;
9214     r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
9215     return r_screen;
9216 }
9217 
FindBestWindowPosForPopup(ImGuiWindow * window)9218 ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
9219 {
9220     ImGuiContext& g = *GImGui;
9221 
9222     ImRect r_outer = GetPopupAllowedExtentRect(window);
9223     if (window->Flags & ImGuiWindowFlags_ChildMenu)
9224     {
9225         // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
9226         // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
9227         ImGuiWindow* parent_window = window->ParentWindow;
9228         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).
9229         ImRect r_avoid;
9230         if (parent_window->DC.MenuBarAppending)
9231             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
9232         else
9233             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);
9234         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default);
9235     }
9236     if (window->Flags & ImGuiWindowFlags_Popup)
9237     {
9238         ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
9239         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default);
9240     }
9241     if (window->Flags & ImGuiWindowFlags_Tooltip)
9242     {
9243         // Position tooltip (always follows mouse)
9244         float sc = g.Style.MouseCursorScale;
9245         ImVec2 ref_pos = NavCalcPreferredRefPos();
9246         ImRect r_avoid;
9247         if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
9248             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
9249         else
9250             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.
9251         return FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip);
9252     }
9253     IM_ASSERT(0);
9254     return window->Pos;
9255 }
9256 
9257 //-----------------------------------------------------------------------------
9258 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
9259 //-----------------------------------------------------------------------------
9260 
9261 // FIXME-NAV: The existence of SetNavID vs SetFocusID properly needs to be clarified/reworked.
SetNavID(ImGuiID id,ImGuiNavLayer nav_layer,ImGuiID focus_scope_id,const ImRect & rect_rel)9262 void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel)
9263 {
9264     ImGuiContext& g = *GImGui;
9265     IM_ASSERT(g.NavWindow != NULL);
9266     IM_ASSERT(nav_layer == ImGuiNavLayer_Main || nav_layer == ImGuiNavLayer_Menu);
9267     g.NavId = id;
9268     g.NavLayer = nav_layer;
9269     g.NavFocusScopeId = focus_scope_id;
9270     g.NavWindow->NavLastIds[nav_layer] = id;
9271     g.NavWindow->NavRectRel[nav_layer] = rect_rel;
9272     //g.NavDisableHighlight = false;
9273     //g.NavDisableMouseHover = g.NavMousePosDirty = true;
9274 }
9275 
SetFocusID(ImGuiID id,ImGuiWindow * window)9276 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
9277 {
9278     ImGuiContext& g = *GImGui;
9279     IM_ASSERT(id != 0);
9280 
9281     // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and window->DC.NavFocusScopeIdCurrent are valid.
9282     // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text)
9283     const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
9284     if (g.NavWindow != window)
9285         g.NavInitRequest = false;
9286     g.NavWindow = window;
9287     g.NavId = id;
9288     g.NavLayer = nav_layer;
9289     g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
9290     window->NavLastIds[nav_layer] = id;
9291     if (window->DC.LastItemId == id)
9292         window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos);
9293 
9294     if (g.ActiveIdSource == ImGuiInputSource_Nav)
9295         g.NavDisableMouseHover = true;
9296     else
9297         g.NavDisableHighlight = true;
9298 }
9299 
ImGetDirQuadrantFromDelta(float dx,float dy)9300 ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
9301 {
9302     if (ImFabs(dx) > ImFabs(dy))
9303         return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
9304     return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
9305 }
9306 
NavScoreItemDistInterval(float a0,float a1,float b0,float b1)9307 static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
9308 {
9309     if (a1 < b0)
9310         return a1 - b0;
9311     if (b1 < a0)
9312         return a0 - b1;
9313     return 0.0f;
9314 }
9315 
NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir,ImRect & r,const ImRect & clip_rect)9316 static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect)
9317 {
9318     if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
9319     {
9320         r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y);
9321         r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y);
9322     }
9323     else
9324     {
9325         r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x);
9326         r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x);
9327     }
9328 }
9329 
9330 // Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057
NavScoreItem(ImGuiNavItemData * result,ImRect cand)9331 static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand)
9332 {
9333     ImGuiContext& g = *GImGui;
9334     ImGuiWindow* window = g.CurrentWindow;
9335     if (g.NavLayer != window->DC.NavLayerCurrent)
9336         return false;
9337 
9338     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)
9339     g.NavScoringCount++;
9340 
9341     // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
9342     if (window->ParentWindow == g.NavWindow)
9343     {
9344         IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened);
9345         if (!window->ClipRect.Overlaps(cand))
9346             return false;
9347         cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
9348     }
9349 
9350     // 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)
9351     // For example, this ensure that items in one column are not reached when moving vertically from items in another column.
9352     NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect);
9353 
9354     // Compute distance between boxes
9355     // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
9356     float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
9357     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
9358     if (dby != 0.0f && dbx != 0.0f)
9359         dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
9360     float dist_box = ImFabs(dbx) + ImFabs(dby);
9361 
9362     // 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)
9363     float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
9364     float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
9365     float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
9366 
9367     // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
9368     ImGuiDir quadrant;
9369     float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
9370     if (dbx != 0.0f || dby != 0.0f)
9371     {
9372         // For non-overlapping boxes, use distance between boxes
9373         dax = dbx;
9374         day = dby;
9375         dist_axial = dist_box;
9376         quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
9377     }
9378     else if (dcx != 0.0f || dcy != 0.0f)
9379     {
9380         // For overlapping boxes with different centers, use distance between centers
9381         dax = dcx;
9382         day = dcy;
9383         dist_axial = dist_center;
9384         quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
9385     }
9386     else
9387     {
9388         // 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)
9389         quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
9390     }
9391 
9392 #if IMGUI_DEBUG_NAV_SCORING
9393     char buf[128];
9394     if (IsMouseHoveringRect(cand.Min, cand.Max))
9395     {
9396         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]);
9397         ImDrawList* draw_list = GetForegroundDrawList(window);
9398         draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
9399         draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
9400         draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40,0,0,150));
9401         draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
9402     }
9403     else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
9404     {
9405         if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; }
9406         if (quadrant == g.NavMoveDir)
9407         {
9408             ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
9409             ImDrawList* draw_list = GetForegroundDrawList(window);
9410             draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
9411             draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
9412         }
9413     }
9414 #endif
9415 
9416     // Is it in the quadrant we're interesting in moving to?
9417     bool new_best = false;
9418     if (quadrant == g.NavMoveDir)
9419     {
9420         // Does it beat the current best candidate?
9421         if (dist_box < result->DistBox)
9422         {
9423             result->DistBox = dist_box;
9424             result->DistCenter = dist_center;
9425             return true;
9426         }
9427         if (dist_box == result->DistBox)
9428         {
9429             // Try using distance between center points to break ties
9430             if (dist_center < result->DistCenter)
9431             {
9432                 result->DistCenter = dist_center;
9433                 new_best = true;
9434             }
9435             else if (dist_center == result->DistCenter)
9436             {
9437                 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
9438                 // (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),
9439                 // 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.
9440                 if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
9441                     new_best = true;
9442             }
9443         }
9444     }
9445 
9446     // 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
9447     // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
9448     // 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.
9449     // 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.
9450     // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
9451     if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial)  // Check axial match
9452         if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
9453             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))
9454             {
9455                 result->DistAxial = dist_axial;
9456                 new_best = true;
9457             }
9458 
9459     return new_best;
9460 }
9461 
NavApplyItemToResult(ImGuiNavItemData * result,ImGuiWindow * window,ImGuiID id,const ImRect & nav_bb_rel)9462 static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel)
9463 {
9464     result->Window = window;
9465     result->ID = id;
9466     result->FocusScopeId = window->DC.NavFocusScopeIdCurrent;
9467     result->RectRel = nav_bb_rel;
9468 }
9469 
9470 // 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)9471 static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
9472 {
9473     ImGuiContext& g = *GImGui;
9474     //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.
9475     //    return;
9476 
9477     const ImGuiItemFlags item_flags = g.CurrentItemFlags;
9478     const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
9479 
9480     // Process Init Request
9481     if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
9482     {
9483         // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
9484         const bool candidate_for_nav_default_focus = (item_flags & (ImGuiItemFlags_NoNavDefaultFocus | ImGuiItemFlags_Disabled)) == 0;
9485         if (candidate_for_nav_default_focus || g.NavInitResultId == 0)
9486         {
9487             g.NavInitResultId = id;
9488             g.NavInitResultRectRel = nav_bb_rel;
9489         }
9490         if (candidate_for_nav_default_focus)
9491         {
9492             g.NavInitRequest = false; // Found a match, clear request
9493             NavUpdateAnyRequestFlag();
9494         }
9495     }
9496 
9497     // Process Move Request (scoring for navigation)
9498     // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
9499     if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav)))
9500     {
9501         ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
9502 #if IMGUI_DEBUG_NAV_SCORING
9503         // [DEBUG] Score all items in NavWindow at all times
9504         if (!g.NavMoveRequest)
9505             g.NavMoveDir = g.NavMoveDirLast;
9506         bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest;
9507 #else
9508         bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb);
9509 #endif
9510         if (new_best)
9511             NavApplyItemToResult(result, window, id, nav_bb_rel);
9512 
9513         // Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
9514         const float VISIBLE_RATIO = 0.70f;
9515         if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
9516             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)
9517                 if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb))
9518                     NavApplyItemToResult(&g.NavMoveResultLocalVisibleSet, window, id, nav_bb_rel);
9519     }
9520 
9521     // Update window-relative bounding box of navigated item
9522     if (g.NavId == id)
9523     {
9524         g.NavWindow = window;                                           // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
9525         g.NavLayer = window->DC.NavLayerCurrent;
9526         g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
9527         g.NavIdIsAlive = true;
9528         window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel;    // Store item bounding box (relative to window position)
9529     }
9530 }
9531 
NavMoveRequestButNoResultYet()9532 bool ImGui::NavMoveRequestButNoResultYet()
9533 {
9534     ImGuiContext& g = *GImGui;
9535     return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
9536 }
9537 
NavMoveRequestCancel()9538 void ImGui::NavMoveRequestCancel()
9539 {
9540     ImGuiContext& g = *GImGui;
9541     g.NavMoveRequest = false;
9542     NavUpdateAnyRequestFlag();
9543 }
9544 
NavMoveRequestForward(ImGuiDir move_dir,ImGuiDir clip_dir,const ImRect & bb_rel,ImGuiNavMoveFlags move_flags)9545 void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags)
9546 {
9547     ImGuiContext& g = *GImGui;
9548     IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None);
9549     NavMoveRequestCancel();
9550     g.NavMoveDir = move_dir;
9551     g.NavMoveClipDir = clip_dir;
9552     g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
9553     g.NavMoveRequestFlags = move_flags;
9554     g.NavWindow->NavRectRel[g.NavLayer] = bb_rel;
9555 }
9556 
NavMoveRequestTryWrapping(ImGuiWindow * window,ImGuiNavMoveFlags move_flags)9557 void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
9558 {
9559     ImGuiContext& g = *GImGui;
9560 
9561     // Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
9562     // popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.
9563     g.NavWrapRequestWindow = window;
9564     g.NavWrapRequestFlags = move_flags;
9565 }
9566 
9567 // FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
9568 // This way we could find the last focused window among our children. It would be much less confusing this way?
NavSaveLastChildNavWindowIntoParent(ImGuiWindow * nav_window)9569 static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window)
9570 {
9571     ImGuiWindow* parent = nav_window;
9572     while (parent && parent->RootWindow != parent && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
9573         parent = parent->ParentWindow;
9574     if (parent && parent != nav_window)
9575         parent->NavLastChildNavWindow = nav_window;
9576 }
9577 
9578 // Restore the last focused child.
9579 // Call when we are expected to land on the Main Layer (0) after FocusWindow()
NavRestoreLastChildNavWindow(ImGuiWindow * window)9580 static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
9581 {
9582     if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive)
9583         return window->NavLastChildNavWindow;
9584     if (window->DockNodeAsHost && window->DockNodeAsHost->TabBar)
9585         if (ImGuiTabItem* tab = TabBarFindMostRecentlySelectedTabForActiveWindow(window->DockNodeAsHost->TabBar))
9586             return tab->Window;
9587     return window;
9588 }
9589 
NavRestoreLayer(ImGuiNavLayer layer)9590 void ImGui::NavRestoreLayer(ImGuiNavLayer layer)
9591 {
9592     ImGuiContext& g = *GImGui;
9593     if (layer == ImGuiNavLayer_Main)
9594         g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow);
9595     ImGuiWindow* window = g.NavWindow;
9596     if (window->NavLastIds[layer] != 0)
9597     {
9598         SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]);
9599         g.NavDisableHighlight = false;
9600         g.NavDisableMouseHover = g.NavMousePosDirty = true;
9601     }
9602     else
9603     {
9604         g.NavLayer = layer;
9605         NavInitWindow(window, true);
9606     }
9607 }
9608 
NavUpdateAnyRequestFlag()9609 static inline void ImGui::NavUpdateAnyRequestFlag()
9610 {
9611     ImGuiContext& g = *GImGui;
9612     g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
9613     if (g.NavAnyRequest)
9614         IM_ASSERT(g.NavWindow != NULL);
9615 }
9616 
9617 // This needs to be called before we submit any widget (aka in or before Begin)
NavInitWindow(ImGuiWindow * window,bool force_reinit)9618 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
9619 {
9620     // FIXME: ChildWindow test here is wrong for docking
9621     ImGuiContext& g = *GImGui;
9622     IM_ASSERT(window == g.NavWindow);
9623 
9624     if (window->Flags & ImGuiWindowFlags_NoNavInputs)
9625     {
9626         g.NavId = g.NavFocusScopeId = 0;
9627         return;
9628     }
9629 
9630     bool init_for_nav = false;
9631     if (window == window->RootWindow || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
9632         init_for_nav = true;
9633     IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer);
9634     if (init_for_nav)
9635     {
9636         SetNavID(0, g.NavLayer, 0, ImRect());
9637         g.NavInitRequest = true;
9638         g.NavInitRequestFromMove = false;
9639         g.NavInitResultId = 0;
9640         g.NavInitResultRectRel = ImRect();
9641         NavUpdateAnyRequestFlag();
9642     }
9643     else
9644     {
9645         g.NavId = window->NavLastIds[0];
9646         g.NavFocusScopeId = 0;
9647     }
9648 }
9649 
NavCalcPreferredRefPos()9650 static ImVec2 ImGui::NavCalcPreferredRefPos()
9651 {
9652     ImGuiContext& g = *GImGui;
9653     if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow)
9654     {
9655         // Mouse (we need a fallback in case the mouse becomes invalid after being used)
9656         if (IsMousePosValid(&g.IO.MousePos))
9657             return g.IO.MousePos;
9658         return g.LastValidMousePos;
9659     }
9660     else
9661     {
9662         // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item.
9663         const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
9664         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()));
9665         ImGuiViewport* viewport = g.NavWindow->Viewport;
9666         return ImFloor(ImClamp(pos, viewport->Pos, viewport->Pos + viewport->Size)); // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta.
9667     }
9668 }
9669 
GetNavInputAmount(ImGuiNavInput n,ImGuiInputReadMode mode)9670 float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode)
9671 {
9672     ImGuiContext& g = *GImGui;
9673     if (mode == ImGuiInputReadMode_Down)
9674         return g.IO.NavInputs[n];                         // Instant, read analog input (0.0f..1.0f, as provided by user)
9675 
9676     const float t = g.IO.NavInputsDownDuration[n];
9677     if (t < 0.0f && mode == ImGuiInputReadMode_Released)  // Return 1.0f when just released, no repeat, ignore analog input.
9678         return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
9679     if (t < 0.0f)
9680         return 0.0f;
9681     if (mode == ImGuiInputReadMode_Pressed)               // Return 1.0f when just pressed, no repeat, ignore analog input.
9682         return (t == 0.0f) ? 1.0f : 0.0f;
9683     if (mode == ImGuiInputReadMode_Repeat)
9684         return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.80f);
9685     if (mode == ImGuiInputReadMode_RepeatSlow)
9686         return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 1.25f, g.IO.KeyRepeatRate * 2.00f);
9687     if (mode == ImGuiInputReadMode_RepeatFast)
9688         return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.30f);
9689     return 0.0f;
9690 }
9691 
GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources,ImGuiInputReadMode mode,float slow_factor,float fast_factor)9692 ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor)
9693 {
9694     ImVec2 delta(0.0f, 0.0f);
9695     if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
9696         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode)   - GetNavInputAmount(ImGuiNavInput_KeyLeft_,   mode), GetNavInputAmount(ImGuiNavInput_KeyDown_,   mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_,   mode));
9697     if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
9698         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode)   - GetNavInputAmount(ImGuiNavInput_DpadLeft,   mode), GetNavInputAmount(ImGuiNavInput_DpadDown,   mode) - GetNavInputAmount(ImGuiNavInput_DpadUp,   mode));
9699     if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
9700         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode));
9701     if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow))
9702         delta *= slow_factor;
9703     if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast))
9704         delta *= fast_factor;
9705     return delta;
9706 }
9707 
NavUpdate()9708 static void ImGui::NavUpdate()
9709 {
9710     ImGuiContext& g = *GImGui;
9711     ImGuiIO& io = g.IO;
9712 
9713     io.WantSetMousePos = false;
9714     g.NavWrapRequestWindow = NULL;
9715     g.NavWrapRequestFlags = ImGuiNavMoveFlags_None;
9716 #if 0
9717     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);
9718 #endif
9719 
9720     // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard)
9721     // (do it before we map Keyboard input!)
9722     bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
9723     bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
9724     if (nav_gamepad_active && g.NavInputSource != ImGuiInputSource_Gamepad)
9725     {
9726         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
9727             || io.NavInputs[ImGuiNavInput_DpadLeft] > 0.0f || io.NavInputs[ImGuiNavInput_DpadRight] > 0.0f || io.NavInputs[ImGuiNavInput_DpadUp] > 0.0f || io.NavInputs[ImGuiNavInput_DpadDown] > 0.0f)
9728             g.NavInputSource = ImGuiInputSource_Gamepad;
9729     }
9730 
9731     // Update Keyboard->Nav inputs mapping
9732     if (nav_keyboard_active)
9733     {
9734         #define NAV_MAP_KEY(_KEY, _NAV_INPUT)  do { if (IsKeyDown(io.KeyMap[_KEY])) { io.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_Keyboard; } } while (0)
9735         NAV_MAP_KEY(ImGuiKey_Space,     ImGuiNavInput_Activate );
9736         NAV_MAP_KEY(ImGuiKey_Enter,     ImGuiNavInput_Input    );
9737         NAV_MAP_KEY(ImGuiKey_Escape,    ImGuiNavInput_Cancel   );
9738         NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ );
9739         NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
9740         NAV_MAP_KEY(ImGuiKey_UpArrow,   ImGuiNavInput_KeyUp_   );
9741         NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
9742         if (io.KeyCtrl)
9743             io.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
9744         if (io.KeyShift)
9745             io.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
9746 
9747         // AltGR is normally Alt+Ctrl but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl)
9748         // But also even on keyboards without AltGR we don't want Alt+Ctrl to open menu anyway.
9749         if (io.KeyAlt && !io.KeyCtrl)
9750             io.NavInputs[ImGuiNavInput_KeyMenu_]  = 1.0f;
9751 
9752         // We automatically cancel toggling nav layer when any text has been typed while holding Alt. (See #370)
9753         if (io.KeyAlt && !io.KeyCtrl && g.NavWindowingToggleLayer && io.InputQueueCharacters.Size > 0)
9754             g.NavWindowingToggleLayer = false;
9755 
9756         #undef NAV_MAP_KEY
9757     }
9758     memcpy(io.NavInputsDownDurationPrev, io.NavInputsDownDuration, sizeof(io.NavInputsDownDuration));
9759     for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++)
9760         io.NavInputsDownDuration[i] = (io.NavInputs[i] > 0.0f) ? (io.NavInputsDownDuration[i] < 0.0f ? 0.0f : io.NavInputsDownDuration[i] + io.DeltaTime) : -1.0f;
9761 
9762     // Process navigation init request (select first/default focus)
9763     if (g.NavInitResultId != 0)
9764         NavUpdateInitResult();
9765     g.NavInitRequest = false;
9766     g.NavInitRequestFromMove = false;
9767     g.NavInitResultId = 0;
9768     g.NavJustMovedToId = 0;
9769 
9770     // Process navigation move request
9771     if (g.NavMoveRequest)
9772         NavUpdateMoveResult();
9773 
9774     // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
9775     if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
9776     {
9777         IM_ASSERT(g.NavMoveRequest);
9778         if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
9779             g.NavDisableHighlight = false;
9780         g.NavMoveRequestForward = ImGuiNavForward_None;
9781     }
9782 
9783     // Apply application mouse position movement, after we had a chance to process move request result.
9784     if (g.NavMousePosDirty && g.NavIdIsAlive)
9785     {
9786         // Set mouse position given our knowledge of the navigated item position from last frame
9787         if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
9788             if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
9789             {
9790                 io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos();
9791                 io.WantSetMousePos = true;
9792             }
9793         g.NavMousePosDirty = false;
9794     }
9795     g.NavIdIsAlive = false;
9796     g.NavJustTabbedId = 0;
9797     IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
9798 
9799     // 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
9800     if (g.NavWindow)
9801         NavSaveLastChildNavWindowIntoParent(g.NavWindow);
9802     if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main)
9803         g.NavWindow->NavLastChildNavWindow = NULL;
9804 
9805     // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
9806     NavUpdateWindowing();
9807 
9808     // Set output flags for user application
9809     io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
9810     io.NavVisible = (io.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL);
9811 
9812     // Process NavCancel input (to close a popup, get back to parent, clear focus)
9813     if (IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
9814     {
9815         IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n");
9816         if (g.ActiveId != 0)
9817         {
9818             if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel))
9819                 ClearActiveID();
9820         }
9821         else if (g.NavLayer != ImGuiNavLayer_Main)
9822         {
9823             // Leave the "menu" layer
9824             NavRestoreLayer(ImGuiNavLayer_Main);
9825         }
9826         else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
9827         {
9828             // Exit child window
9829             ImGuiWindow* child_window = g.NavWindow;
9830             ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
9831             IM_ASSERT(child_window->ChildId != 0);
9832             ImRect child_rect = child_window->Rect();
9833             FocusWindow(parent_window);
9834             SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, ImRect(child_rect.Min - parent_window->Pos, child_rect.Max - parent_window->Pos));
9835         }
9836         else if (g.OpenPopupStack.Size > 0)
9837         {
9838             // Close open popup/menu
9839             if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
9840                 ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
9841         }
9842         else
9843         {
9844             // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
9845             if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
9846                 g.NavWindow->NavLastIds[0] = 0;
9847             g.NavId = g.NavFocusScopeId = 0;
9848         }
9849     }
9850 
9851     // Process manual activation request
9852     g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
9853     if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
9854     {
9855         bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
9856         bool activate_pressed = activate_down && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
9857         if (g.ActiveId == 0 && activate_pressed)
9858             g.NavActivateId = g.NavId;
9859         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
9860             g.NavActivateDownId = g.NavId;
9861         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
9862             g.NavActivatePressedId = g.NavId;
9863         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
9864             g.NavInputId = g.NavId;
9865     }
9866     if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
9867         g.NavDisableHighlight = true;
9868     if (g.NavActivateId != 0)
9869         IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
9870     g.NavMoveRequest = false;
9871 
9872     // Process programmatic activation request
9873     if (g.NavNextActivateId != 0)
9874         g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
9875     g.NavNextActivateId = 0;
9876 
9877     // Initiate directional inputs request
9878     if (g.NavMoveRequestForward == ImGuiNavForward_None)
9879     {
9880         g.NavMoveDir = ImGuiDir_None;
9881         g.NavMoveRequestFlags = ImGuiNavMoveFlags_None;
9882         if (g.NavWindow && !g.NavWindowingTarget && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
9883         {
9884             const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat;
9885             if (!IsActiveIdUsingNavDir(ImGuiDir_Left)  && (IsNavInputTest(ImGuiNavInput_DpadLeft,  read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_,  read_mode))) { g.NavMoveDir = ImGuiDir_Left; }
9886             if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; }
9887             if (!IsActiveIdUsingNavDir(ImGuiDir_Up)    && (IsNavInputTest(ImGuiNavInput_DpadUp,    read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_,    read_mode))) { g.NavMoveDir = ImGuiDir_Up; }
9888             if (!IsActiveIdUsingNavDir(ImGuiDir_Down)  && (IsNavInputTest(ImGuiNavInput_DpadDown,  read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_,  read_mode))) { g.NavMoveDir = ImGuiDir_Down; }
9889         }
9890         g.NavMoveClipDir = g.NavMoveDir;
9891     }
9892     else
9893     {
9894         // 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)
9895         // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function)
9896         IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
9897         IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
9898         IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir);
9899         g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
9900     }
9901 
9902     // Update PageUp/PageDown/Home/End scroll
9903     // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
9904     float nav_scoring_rect_offset_y = 0.0f;
9905     if (nav_keyboard_active)
9906         nav_scoring_rect_offset_y = NavUpdatePageUpPageDown();
9907 
9908     // 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
9909     if (g.NavMoveDir != ImGuiDir_None)
9910     {
9911         g.NavMoveRequest = true;
9912         g.NavMoveRequestKeyMods = io.KeyMods;
9913         g.NavMoveDirLast = g.NavMoveDir;
9914     }
9915     if (g.NavMoveRequest && g.NavId == 0)
9916     {
9917         IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer);
9918         g.NavInitRequest = g.NavInitRequestFromMove = true;
9919         // Reassigning with same value, we're being explicit here.
9920         g.NavInitResultId = 0;     // -V1048
9921         g.NavDisableHighlight = false;
9922     }
9923     NavUpdateAnyRequestFlag();
9924 
9925     // Scrolling
9926     if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
9927     {
9928         // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
9929         ImGuiWindow* window = g.NavWindow;
9930         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.
9931         if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
9932         {
9933             if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
9934                 SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
9935             if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
9936                 SetScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
9937         }
9938 
9939         // *Normal* Manual scroll with NavScrollXXX keys
9940         // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
9941         ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f / 10.0f, 10.0f);
9942         if (scroll_dir.x != 0.0f && window->ScrollbarX)
9943             SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
9944         if (scroll_dir.y != 0.0f)
9945             SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
9946     }
9947 
9948     // Reset search results
9949     g.NavMoveResultLocal.Clear();
9950     g.NavMoveResultLocalVisibleSet.Clear();
9951     g.NavMoveResultOther.Clear();
9952 
9953     // When using gamepad, we project the reference nav bounding box into window visible area.
9954     // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad every movements are relative
9955     // (can't focus a visible object like we can with the mouse).
9956     if (g.NavMoveRequest && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main)
9957     {
9958         ImGuiWindow* window = g.NavWindow;
9959         ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1));
9960         if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
9961         {
9962             IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel\n");
9963             float pad = window->CalcFontSize() * 0.5f;
9964             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
9965             window->NavRectRel[g.NavLayer].ClipWithFull(window_rect_rel);
9966             g.NavId = g.NavFocusScopeId = 0;
9967         }
9968     }
9969 
9970     // 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)
9971     ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);
9972     g.NavScoringRect = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : ImRect(0, 0, 0, 0);
9973     g.NavScoringRect.TranslateY(nav_scoring_rect_offset_y);
9974     g.NavScoringRect.Min.x = ImMin(g.NavScoringRect.Min.x + 1.0f, g.NavScoringRect.Max.x);
9975     g.NavScoringRect.Max.x = g.NavScoringRect.Min.x;
9976     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().
9977     //GetForegroundDrawList()->AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
9978     g.NavScoringCount = 0;
9979 #if IMGUI_DEBUG_NAV_RECTS
9980     if (g.NavWindow)
9981     {
9982         ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow);
9983         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]
9984         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); }
9985     }
9986 #endif
9987 }
9988 
NavUpdateInitResult()9989 static void ImGui::NavUpdateInitResult()
9990 {
9991     // 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)
9992     ImGuiContext& g = *GImGui;
9993     if (!g.NavWindow)
9994         return;
9995 
9996     // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
9997     // FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently.
9998     IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name);
9999     SetNavID(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel);
10000     if (g.NavInitRequestFromMove)
10001     {
10002         g.NavDisableHighlight = false;
10003         g.NavDisableMouseHover = g.NavMousePosDirty = true;
10004     }
10005 }
10006 
10007 // Apply result from previous frame navigation directional move request
NavUpdateMoveResult()10008 static void ImGui::NavUpdateMoveResult()
10009 {
10010     ImGuiContext& g = *GImGui;
10011     if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
10012     {
10013         // 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)
10014         if (g.NavId != 0)
10015         {
10016             g.NavDisableHighlight = false;
10017             g.NavDisableMouseHover = true;
10018         }
10019         return;
10020     }
10021 
10022     // Select which result to use
10023     ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
10024 
10025     // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
10026     if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
10027         if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId)
10028             result = &g.NavMoveResultLocalVisibleSet;
10029 
10030     // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
10031     if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
10032         if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
10033             result = &g.NavMoveResultOther;
10034     IM_ASSERT(g.NavWindow && result->Window);
10035 
10036     // Scroll to keep newly navigated item fully into view.
10037     if (g.NavLayer == ImGuiNavLayer_Main)
10038     {
10039         ImVec2 delta_scroll;
10040         if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_ScrollToEdge)
10041         {
10042             float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
10043             delta_scroll.y = result->Window->Scroll.y - scroll_target;
10044             SetScrollY(result->Window, scroll_target);
10045         }
10046         else
10047         {
10048             ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos);
10049             delta_scroll = ScrollToBringRectIntoView(result->Window, rect_abs);
10050         }
10051 
10052         // Offset our result position so mouse position can be applied immediately after in NavUpdate()
10053         result->RectRel.TranslateX(-delta_scroll.x);
10054         result->RectRel.TranslateY(-delta_scroll.y);
10055     }
10056 
10057     ClearActiveID();
10058     g.NavWindow = result->Window;
10059     if (g.NavId != result->ID)
10060     {
10061         // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
10062         g.NavJustMovedToId = result->ID;
10063         g.NavJustMovedToFocusScopeId = result->FocusScopeId;
10064         g.NavJustMovedToKeyMods = g.NavMoveRequestKeyMods;
10065     }
10066     IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
10067     SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
10068     g.NavDisableHighlight = false;
10069     g.NavDisableMouseHover = g.NavMousePosDirty = true;
10070 }
10071 
10072 // Handle PageUp/PageDown/Home/End keys
NavUpdatePageUpPageDown()10073 static float ImGui::NavUpdatePageUpPageDown()
10074 {
10075     ImGuiContext& g = *GImGui;
10076     ImGuiIO& io = g.IO;
10077 
10078     if (g.NavMoveDir != ImGuiDir_None || g.NavWindow == NULL)
10079         return 0.0f;
10080     if ((g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != ImGuiNavLayer_Main)
10081         return 0.0f;
10082 
10083     ImGuiWindow* window = g.NavWindow;
10084     const bool page_up_held = IsKeyDown(io.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp);
10085     const bool page_down_held = IsKeyDown(io.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown);
10086     const bool home_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home);
10087     const bool end_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End);
10088     if (page_up_held != page_down_held || home_pressed != end_pressed) // If either (not both) are pressed
10089     {
10090         if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll)
10091         {
10092             // Fallback manual-scroll when window has no navigable item
10093             if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
10094                 SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
10095             else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
10096                 SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
10097             else if (home_pressed)
10098                 SetScrollY(window, 0.0f);
10099             else if (end_pressed)
10100                 SetScrollY(window, window->ScrollMax.y);
10101         }
10102         else
10103         {
10104             ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
10105             const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
10106             float nav_scoring_rect_offset_y = 0.0f;
10107             if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
10108             {
10109                 nav_scoring_rect_offset_y = -page_offset_y;
10110                 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)
10111                 g.NavMoveClipDir = ImGuiDir_Up;
10112                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
10113             }
10114             else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
10115             {
10116                 nav_scoring_rect_offset_y = +page_offset_y;
10117                 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)
10118                 g.NavMoveClipDir = ImGuiDir_Down;
10119                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
10120             }
10121             else if (home_pressed)
10122             {
10123                 // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
10124                 // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result.
10125                 // Preserve current horizontal position if we have any.
10126                 nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y;
10127                 if (nav_rect_rel.IsInverted())
10128                     nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
10129                 g.NavMoveDir = ImGuiDir_Down;
10130                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
10131             }
10132             else if (end_pressed)
10133             {
10134                 nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y;
10135                 if (nav_rect_rel.IsInverted())
10136                     nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
10137                 g.NavMoveDir = ImGuiDir_Up;
10138                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
10139             }
10140             return nav_scoring_rect_offset_y;
10141         }
10142     }
10143     return 0.0f;
10144 }
10145 
NavEndFrame()10146 static void ImGui::NavEndFrame()
10147 {
10148     ImGuiContext& g = *GImGui;
10149 
10150     // Show CTRL+TAB list window
10151     if (g.NavWindowingTarget != NULL)
10152         NavUpdateWindowingOverlay();
10153 
10154     // Perform wrap-around in menus
10155     ImGuiWindow* window = g.NavWrapRequestWindow;
10156     ImGuiNavMoveFlags move_flags = g.NavWrapRequestFlags;
10157     if (window != NULL && g.NavWindow == window && NavMoveRequestButNoResultYet() && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == ImGuiNavLayer_Main)
10158     {
10159         IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
10160         ImRect bb_rel = window->NavRectRel[0];
10161 
10162         ImGuiDir clip_dir = g.NavMoveDir;
10163         if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
10164         {
10165             bb_rel.Min.x = bb_rel.Max.x =
10166                 ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x;
10167             if (move_flags & ImGuiNavMoveFlags_WrapX)
10168             {
10169                 bb_rel.TranslateY(-bb_rel.GetHeight());
10170                 clip_dir = ImGuiDir_Up;
10171             }
10172             NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
10173         }
10174         if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
10175         {
10176             bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
10177             if (move_flags & ImGuiNavMoveFlags_WrapX)
10178             {
10179                 bb_rel.TranslateY(+bb_rel.GetHeight());
10180                 clip_dir = ImGuiDir_Down;
10181             }
10182             NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
10183         }
10184         if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
10185         {
10186             bb_rel.Min.y = bb_rel.Max.y =
10187                 ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y;
10188             if (move_flags & ImGuiNavMoveFlags_WrapY)
10189             {
10190                 bb_rel.TranslateX(-bb_rel.GetWidth());
10191                 clip_dir = ImGuiDir_Left;
10192             }
10193             NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
10194         }
10195         if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
10196         {
10197             bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
10198             if (move_flags & ImGuiNavMoveFlags_WrapY)
10199             {
10200                 bb_rel.TranslateX(+bb_rel.GetWidth());
10201                 clip_dir = ImGuiDir_Right;
10202             }
10203             NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
10204         }
10205     }
10206 }
10207 
FindWindowFocusIndex(ImGuiWindow * window)10208 static int ImGui::FindWindowFocusIndex(ImGuiWindow* window)
10209 {
10210     ImGuiContext& g = *GImGui;
10211     IM_UNUSED(g);
10212     int order = window->FocusOrder;
10213     IM_ASSERT(g.WindowsFocusOrder[order] == window);
10214     return order;
10215 }
10216 
FindWindowNavFocusable(int i_start,int i_stop,int dir)10217 static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
10218 {
10219     ImGuiContext& g = *GImGui;
10220     for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
10221         if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
10222             return g.WindowsFocusOrder[i];
10223     return NULL;
10224 }
10225 
NavUpdateWindowingHighlightWindow(int focus_change_dir)10226 static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
10227 {
10228     ImGuiContext& g = *GImGui;
10229     IM_ASSERT(g.NavWindowingTarget);
10230     if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
10231         return;
10232 
10233     const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget);
10234     ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
10235     if (!window_target)
10236         window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
10237     if (window_target) // Don't reset windowing target if there's a single window in the list
10238         g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
10239     g.NavWindowingToggleLayer = false;
10240 }
10241 
10242 // Windowing management mode
10243 // Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer)
10244 // Gamepad:  Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)
NavUpdateWindowing()10245 static void ImGui::NavUpdateWindowing()
10246 {
10247     ImGuiContext& g = *GImGui;
10248     ImGuiWindow* apply_focus_window = NULL;
10249     bool apply_toggle_layer = false;
10250 
10251     ImGuiWindow* modal_window = GetTopMostPopupModal();
10252     bool allow_windowing = (modal_window == NULL);
10253     if (!allow_windowing)
10254         g.NavWindowingTarget = NULL;
10255 
10256     // Fade out
10257     if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
10258     {
10259         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f);
10260         if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
10261             g.NavWindowingTargetAnim = NULL;
10262     }
10263 
10264     // Start CTRL-TAB or Square+L/R window selection
10265     bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
10266     bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
10267     if (start_windowing_with_gamepad || start_windowing_with_keyboard)
10268         if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
10269         {
10270             g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow;
10271             g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
10272             g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true;
10273             g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad;
10274         }
10275 
10276     // Gamepad update
10277     g.NavWindowingTimer += g.IO.DeltaTime;
10278     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Gamepad)
10279     {
10280         // 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
10281         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
10282 
10283         // Select window to focus
10284         const int focus_change_dir = (int)IsNavInputTest(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputTest(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow);
10285         if (focus_change_dir != 0)
10286         {
10287             NavUpdateWindowingHighlightWindow(focus_change_dir);
10288             g.NavWindowingHighlightAlpha = 1.0f;
10289         }
10290 
10291         // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)
10292         if (!IsNavInputDown(ImGuiNavInput_Menu))
10293         {
10294             g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
10295             if (g.NavWindowingToggleLayer && g.NavWindow)
10296                 apply_toggle_layer = true;
10297             else if (!g.NavWindowingToggleLayer)
10298                 apply_focus_window = g.NavWindowingTarget;
10299             g.NavWindowingTarget = NULL;
10300         }
10301     }
10302 
10303     // Keyboard: Focus
10304     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Keyboard)
10305     {
10306         // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
10307         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
10308         if (IsKeyPressedMap(ImGuiKey_Tab, true))
10309             NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1);
10310         if (!g.IO.KeyCtrl)
10311             apply_focus_window = g.NavWindowingTarget;
10312     }
10313 
10314     // Keyboard: Press and Release ALT to toggle menu layer
10315     // 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
10316     if (IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Pressed))
10317         g.NavWindowingToggleLayer = true;
10318     if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && g.NavWindowingToggleLayer && IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released))
10319         if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev))
10320             apply_toggle_layer = true;
10321 
10322     // Move window
10323     if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
10324     {
10325         ImVec2 move_delta;
10326         if (g.NavInputSource == ImGuiInputSource_Keyboard && !g.IO.KeyShift)
10327             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
10328         if (g.NavInputSource == ImGuiInputSource_Gamepad)
10329             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down);
10330         if (move_delta.x != 0.0f || move_delta.y != 0.0f)
10331         {
10332             const float NAV_MOVE_SPEED = 800.0f;
10333             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
10334             ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindowDockTree;
10335             SetWindowPos(moving_window, moving_window->Pos + move_delta * move_speed, ImGuiCond_Always);
10336             MarkIniSettingsDirty(moving_window);
10337             g.NavDisableMouseHover = true;
10338         }
10339     }
10340 
10341     // Apply final focus
10342     if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
10343     {
10344         ImGuiViewport* previous_viewport = g.NavWindow ? g.NavWindow->Viewport : NULL;
10345         ClearActiveID();
10346         g.NavDisableHighlight = false;
10347         g.NavDisableMouseHover = true;
10348         apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
10349         ClosePopupsOverWindow(apply_focus_window, false);
10350         FocusWindow(apply_focus_window);
10351         if (apply_focus_window->NavLastIds[0] == 0)
10352             NavInitWindow(apply_focus_window, false);
10353 
10354         // If the window has ONLY a menu layer (no main layer), select it directly
10355         // Use NavLayersActiveMaskNext since windows didn't have a chance to be Begin()-ed on this frame,
10356         // so CTRL+Tab where the keys are only held for 1 frame will be able to use correct layers mask since
10357         // the target window as already been previewed once.
10358         // FIXME-NAV: This should be done in NavInit.. or in FocusWindow... However in both of those cases,
10359         // we won't have a guarantee that windows has been visible before and therefore NavLayersActiveMask*
10360         // won't be valid.
10361         if (apply_focus_window->DC.NavLayersActiveMaskNext == (1 << ImGuiNavLayer_Menu))
10362             g.NavLayer = ImGuiNavLayer_Menu;
10363 
10364         // Request OS level focus
10365         if (apply_focus_window->Viewport != previous_viewport && g.PlatformIO.Platform_SetWindowFocus)
10366             g.PlatformIO.Platform_SetWindowFocus(apply_focus_window->Viewport);
10367     }
10368     if (apply_focus_window)
10369         g.NavWindowingTarget = NULL;
10370 
10371     // Apply menu/layer toggle
10372     if (apply_toggle_layer && g.NavWindow)
10373     {
10374         ClearActiveID();
10375 
10376         // Move to parent menu if necessary
10377         ImGuiWindow* new_nav_window = g.NavWindow;
10378         while (new_nav_window->ParentWindow
10379             && (new_nav_window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0
10380             && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
10381             && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
10382             new_nav_window = new_nav_window->ParentWindow;
10383         if (new_nav_window != g.NavWindow)
10384         {
10385             ImGuiWindow* old_nav_window = g.NavWindow;
10386             FocusWindow(new_nav_window);
10387             new_nav_window->NavLastChildNavWindow = old_nav_window;
10388         }
10389         g.NavDisableHighlight = false;
10390         g.NavDisableMouseHover = true;
10391 
10392         // Reinitialize navigation when entering menu bar with the Alt key.
10393         const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;
10394         const bool preserve_layer_1_nav_id = (new_nav_window->DockNodeAsHost != NULL);
10395         if (new_nav_layer == ImGuiNavLayer_Menu && !preserve_layer_1_nav_id)
10396             g.NavWindow->NavLastIds[new_nav_layer] = 0;
10397         NavRestoreLayer(new_nav_layer);
10398     }
10399 }
10400 
10401 // Window has already passed the IsWindowNavFocusable()
GetFallbackWindowNameForWindowingList(ImGuiWindow * window)10402 static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
10403 {
10404     if (window->Flags & ImGuiWindowFlags_Popup)
10405         return "(Popup)";
10406     if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
10407         return "(Main menu bar)";
10408     if (window->DockNodeAsHost)
10409         return "(Dock node)";
10410     return "(Untitled)";
10411 }
10412 
10413 // Overlay displayed when using CTRL+TAB. Called by EndFrame().
NavUpdateWindowingOverlay()10414 void ImGui::NavUpdateWindowingOverlay()
10415 {
10416     ImGuiContext& g = *GImGui;
10417     IM_ASSERT(g.NavWindowingTarget != NULL);
10418 
10419     if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
10420         return;
10421 
10422     if (g.NavWindowingListWindow == NULL)
10423         g.NavWindowingListWindow = FindWindowByName("###NavWindowingList");
10424     const ImGuiViewport* viewport = /*g.NavWindow ? g.NavWindow->Viewport :*/ GetMainViewport();
10425     SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
10426     SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
10427     PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
10428     Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
10429     for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
10430     {
10431         ImGuiWindow* window = g.WindowsFocusOrder[n];
10432         IM_ASSERT(window != NULL); // Fix static analyzers
10433         if (!IsWindowNavFocusable(window))
10434             continue;
10435         const char* label = window->Name;
10436         if (label == FindRenderedTextEnd(label))
10437             label = GetFallbackWindowNameForWindowingList(window);
10438         Selectable(label, g.NavWindowingTarget == window);
10439     }
10440     End();
10441     PopStyleVar();
10442 }
10443 
10444 
10445 //-----------------------------------------------------------------------------
10446 // [SECTION] DRAG AND DROP
10447 //-----------------------------------------------------------------------------
10448 
ClearDragDrop()10449 void ImGui::ClearDragDrop()
10450 {
10451     ImGuiContext& g = *GImGui;
10452     g.DragDropActive = false;
10453     g.DragDropPayload.Clear();
10454     g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
10455     g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
10456     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
10457     g.DragDropAcceptFrameCount = -1;
10458 
10459     g.DragDropPayloadBufHeap.clear();
10460     memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
10461 }
10462 
10463 // When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
10464 // If the item has an identifier:
10465 // - This assume/require the item to be activated (typically via ButtonBehavior).
10466 // - Therefore if you want to use this with a mouse button other than left mouse button, it is up to the item itself to activate with another button.
10467 // - We then pull and use the mouse button that was used to activate the item and use it to carry on the drag.
10468 // If the item has no identifier:
10469 // - Currently always assume left mouse button.
BeginDragDropSource(ImGuiDragDropFlags flags)10470 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
10471 {
10472     ImGuiContext& g = *GImGui;
10473     ImGuiWindow* window = g.CurrentWindow;
10474 
10475     // FIXME-DRAGDROP: While in the common-most "drag from non-zero active id" case we can tell the mouse button,
10476     // in both SourceExtern and id==0 cases we may requires something else (explicit flags or some heuristic).
10477     ImGuiMouseButton mouse_button = ImGuiMouseButton_Left;
10478 
10479     bool source_drag_active = false;
10480     ImGuiID source_id = 0;
10481     ImGuiID source_parent_id = 0;
10482     if (!(flags & ImGuiDragDropFlags_SourceExtern))
10483     {
10484         source_id = window->DC.LastItemId;
10485         if (source_id != 0)
10486         {
10487             // Common path: items with ID
10488             if (g.ActiveId != source_id)
10489                 return false;
10490             if (g.ActiveIdMouseButton != -1)
10491                 mouse_button = g.ActiveIdMouseButton;
10492             if (g.IO.MouseDown[mouse_button] == false)
10493                 return false;
10494             g.ActiveIdAllowOverlap = false;
10495         }
10496         else
10497         {
10498             // Uncommon path: items without ID
10499             if (g.IO.MouseDown[mouse_button] == false)
10500                 return false;
10501 
10502             // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
10503             // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
10504             if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
10505             {
10506                 IM_ASSERT(0);
10507                 return false;
10508             }
10509 
10510             // Early out
10511             if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
10512                 return false;
10513 
10514             // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
10515             // We build a throwaway ID based on current ID stack + relative AABB of items in window.
10516             // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
10517             // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
10518             source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect);
10519             bool is_hovered = ItemHoverable(window->DC.LastItemRect, source_id);
10520             if (is_hovered && g.IO.MouseClicked[mouse_button])
10521             {
10522                 SetActiveID(source_id, window);
10523                 FocusWindow(window);
10524             }
10525             if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
10526                 g.ActiveIdAllowOverlap = is_hovered;
10527         }
10528         if (g.ActiveId != source_id)
10529             return false;
10530         source_parent_id = window->IDStack.back();
10531         source_drag_active = IsMouseDragging(mouse_button);
10532 
10533         // Disable navigation and key inputs while dragging + cancel existing request if any
10534         SetActiveIdUsingNavAndKeys();
10535     }
10536     else
10537     {
10538         window = NULL;
10539         source_id = ImHashStr("#SourceExtern");
10540         source_drag_active = true;
10541     }
10542 
10543     if (source_drag_active)
10544     {
10545         if (!g.DragDropActive)
10546         {
10547             IM_ASSERT(source_id != 0);
10548             ClearDragDrop();
10549             ImGuiPayload& payload = g.DragDropPayload;
10550             payload.SourceId = source_id;
10551             payload.SourceParentId = source_parent_id;
10552             g.DragDropActive = true;
10553             g.DragDropSourceFlags = flags;
10554             g.DragDropMouseButton = mouse_button;
10555             if (payload.SourceId == g.ActiveId)
10556                 g.ActiveIdNoClearOnFocusLoss = true;
10557         }
10558         g.DragDropSourceFrameCount = g.FrameCount;
10559         g.DragDropWithinSource = true;
10560 
10561         if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
10562         {
10563             // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
10564             // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
10565             BeginTooltip();
10566             if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
10567             {
10568                 ImGuiWindow* tooltip_window = g.CurrentWindow;
10569                 tooltip_window->SkipItems = true;
10570                 tooltip_window->HiddenFramesCanSkipItems = 1;
10571             }
10572         }
10573 
10574         if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
10575             window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
10576 
10577         return true;
10578     }
10579     return false;
10580 }
10581 
EndDragDropSource()10582 void ImGui::EndDragDropSource()
10583 {
10584     ImGuiContext& g = *GImGui;
10585     IM_ASSERT(g.DragDropActive);
10586     IM_ASSERT(g.DragDropWithinSource && "Not after a BeginDragDropSource()?");
10587 
10588     if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
10589         EndTooltip();
10590 
10591     // Discard the drag if have not called SetDragDropPayload()
10592     if (g.DragDropPayload.DataFrameCount == -1)
10593         ClearDragDrop();
10594     g.DragDropWithinSource = false;
10595 }
10596 
10597 // 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)10598 bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
10599 {
10600     ImGuiContext& g = *GImGui;
10601     ImGuiPayload& payload = g.DragDropPayload;
10602     if (cond == 0)
10603         cond = ImGuiCond_Always;
10604 
10605     IM_ASSERT(type != NULL);
10606     IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
10607     IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
10608     IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
10609     IM_ASSERT(payload.SourceId != 0);                               // Not called between BeginDragDropSource() and EndDragDropSource()
10610 
10611     if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
10612     {
10613         // Copy payload
10614         ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
10615         g.DragDropPayloadBufHeap.resize(0);
10616         if (data_size > sizeof(g.DragDropPayloadBufLocal))
10617         {
10618             // Store in heap
10619             g.DragDropPayloadBufHeap.resize((int)data_size);
10620             payload.Data = g.DragDropPayloadBufHeap.Data;
10621             memcpy(payload.Data, data, data_size);
10622         }
10623         else if (data_size > 0)
10624         {
10625             // Store locally
10626             memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
10627             payload.Data = g.DragDropPayloadBufLocal;
10628             memcpy(payload.Data, data, data_size);
10629         }
10630         else
10631         {
10632             payload.Data = NULL;
10633         }
10634         payload.DataSize = (int)data_size;
10635     }
10636     payload.DataFrameCount = g.FrameCount;
10637 
10638     return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
10639 }
10640 
BeginDragDropTargetCustom(const ImRect & bb,ImGuiID id)10641 bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
10642 {
10643     ImGuiContext& g = *GImGui;
10644     if (!g.DragDropActive)
10645         return false;
10646 
10647     ImGuiWindow* window = g.CurrentWindow;
10648     ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
10649     if (hovered_window == NULL || window->RootWindowDockTree != hovered_window->RootWindowDockTree)
10650         return false;
10651     IM_ASSERT(id != 0);
10652     if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
10653         return false;
10654     if (window->SkipItems)
10655         return false;
10656 
10657     IM_ASSERT(g.DragDropWithinTarget == false);
10658     g.DragDropTargetRect = bb;
10659     g.DragDropTargetId = id;
10660     g.DragDropWithinTarget = true;
10661     return true;
10662 }
10663 
10664 // We don't use BeginDragDropTargetCustom() and duplicate its code because:
10665 // 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
10666 // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
10667 // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
BeginDragDropTarget()10668 bool ImGui::BeginDragDropTarget()
10669 {
10670     ImGuiContext& g = *GImGui;
10671     if (!g.DragDropActive)
10672         return false;
10673 
10674     ImGuiWindow* window = g.CurrentWindow;
10675     if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
10676         return false;
10677     ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
10678     if (hovered_window == NULL || window->RootWindowDockTree != hovered_window->RootWindowDockTree)
10679         return false;
10680 
10681     const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect;
10682     ImGuiID id = window->DC.LastItemId;
10683     if (id == 0)
10684         id = window->GetIDFromRectangle(display_rect);
10685     if (g.DragDropPayload.SourceId == id)
10686         return false;
10687 
10688     IM_ASSERT(g.DragDropWithinTarget == false);
10689     g.DragDropTargetRect = display_rect;
10690     g.DragDropTargetId = id;
10691     g.DragDropWithinTarget = true;
10692     return true;
10693 }
10694 
IsDragDropPayloadBeingAccepted()10695 bool ImGui::IsDragDropPayloadBeingAccepted()
10696 {
10697     ImGuiContext& g = *GImGui;
10698     return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
10699 }
10700 
AcceptDragDropPayload(const char * type,ImGuiDragDropFlags flags)10701 const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
10702 {
10703     ImGuiContext& g = *GImGui;
10704     ImGuiWindow* window = g.CurrentWindow;
10705     ImGuiPayload& payload = g.DragDropPayload;
10706     IM_ASSERT(g.DragDropActive);                        // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
10707     IM_ASSERT(payload.DataFrameCount != -1);            // Forgot to call EndDragDropTarget() ?
10708     if (type != NULL && !payload.IsDataType(type))
10709         return NULL;
10710 
10711     // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
10712     // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
10713     const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
10714     ImRect r = g.DragDropTargetRect;
10715     float r_surface = r.GetWidth() * r.GetHeight();
10716     if (r_surface <= g.DragDropAcceptIdCurrRectSurface)
10717     {
10718         g.DragDropAcceptFlags = flags;
10719         g.DragDropAcceptIdCurr = g.DragDropTargetId;
10720         g.DragDropAcceptIdCurrRectSurface = r_surface;
10721     }
10722 
10723     // Render default drop visuals
10724     // FIXME-DRAGDROP: Settle on a proper default visuals for drop target.
10725     payload.Preview = was_accepted_previously;
10726     flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
10727     if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
10728         window->DrawList->AddRect(r.Min - ImVec2(3.5f,3.5f), r.Max + ImVec2(3.5f, 3.5f), GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f);
10729 
10730     g.DragDropAcceptFrameCount = g.FrameCount;
10731     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()
10732     if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
10733         return NULL;
10734 
10735     return &payload;
10736 }
10737 
GetDragDropPayload()10738 const ImGuiPayload* ImGui::GetDragDropPayload()
10739 {
10740     ImGuiContext& g = *GImGui;
10741     return g.DragDropActive ? &g.DragDropPayload : NULL;
10742 }
10743 
10744 // We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
EndDragDropTarget()10745 void ImGui::EndDragDropTarget()
10746 {
10747     ImGuiContext& g = *GImGui;
10748     IM_ASSERT(g.DragDropActive);
10749     IM_ASSERT(g.DragDropWithinTarget);
10750     g.DragDropWithinTarget = false;
10751 }
10752 
10753 //-----------------------------------------------------------------------------
10754 // [SECTION] LOGGING/CAPTURING
10755 //-----------------------------------------------------------------------------
10756 // All text output from the interface can be captured into tty/file/clipboard.
10757 // By default, tree nodes are automatically opened during logging.
10758 //-----------------------------------------------------------------------------
10759 
10760 // Pass text data straight to log (without being displayed)
LogTextV(ImGuiContext & g,const char * fmt,va_list args)10761 static inline void LogTextV(ImGuiContext& g, const char* fmt, va_list args)
10762 {
10763     if (g.LogFile)
10764     {
10765         g.LogBuffer.Buf.resize(0);
10766         g.LogBuffer.appendfv(fmt, args);
10767         ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile);
10768     }
10769     else
10770     {
10771         g.LogBuffer.appendfv(fmt, args);
10772     }
10773 }
10774 
LogText(const char * fmt,...)10775 void ImGui::LogText(const char* fmt, ...)
10776 {
10777     ImGuiContext& g = *GImGui;
10778     if (!g.LogEnabled)
10779         return;
10780 
10781     va_list args;
10782     va_start(args, fmt);
10783     LogTextV(g, fmt, args);
10784     va_end(args);
10785 }
10786 
LogTextV(const char * fmt,va_list args)10787 void ImGui::LogTextV(const char* fmt, va_list args)
10788 {
10789     ImGuiContext& g = *GImGui;
10790     if (!g.LogEnabled)
10791         return;
10792 
10793     LogTextV(g, fmt, args);
10794 }
10795 
10796 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
10797 // We split text into individual lines to add current tree level padding
10798 // FIXME: This code is a little complicated perhaps, considering simplifying the whole system.
LogRenderedText(const ImVec2 * ref_pos,const char * text,const char * text_end)10799 void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
10800 {
10801     ImGuiContext& g = *GImGui;
10802     ImGuiWindow* window = g.CurrentWindow;
10803 
10804     const char* prefix = g.LogNextPrefix;
10805     const char* suffix = g.LogNextSuffix;
10806     g.LogNextPrefix = g.LogNextSuffix = NULL;
10807 
10808     if (!text_end)
10809         text_end = FindRenderedTextEnd(text, text_end);
10810 
10811     const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + g.Style.FramePadding.y + 1);
10812     if (ref_pos)
10813         g.LogLinePosY = ref_pos->y;
10814     if (log_new_line)
10815     {
10816         LogText(IM_NEWLINE);
10817         g.LogLineFirstItem = true;
10818     }
10819 
10820     if (prefix)
10821         LogRenderedText(ref_pos, prefix, prefix + strlen(prefix)); // Calculate end ourself to ensure "##" are included here.
10822 
10823     // Re-adjust padding if we have popped out of our starting depth
10824     if (g.LogDepthRef > window->DC.TreeDepth)
10825         g.LogDepthRef = window->DC.TreeDepth;
10826     const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);
10827 
10828     const char* text_remaining = text;
10829     for (;;)
10830     {
10831         // Split the string. Each new line (after a '\n') is followed by indentation corresponding to the current depth of our log entry.
10832         // We don't add a trailing \n yet to allow a subsequent item on the same line to be captured.
10833         const char* line_start = text_remaining;
10834         const char* line_end = ImStreolRange(line_start, text_end);
10835         const bool is_last_line = (line_end == text_end);
10836         if (line_start != line_end || !is_last_line)
10837         {
10838             const int line_length = (int)(line_end - line_start);
10839             const int indentation = g.LogLineFirstItem ? tree_depth * 4 : 1;
10840             LogText("%*s%.*s", indentation, "", line_length, line_start);
10841             g.LogLineFirstItem = false;
10842             if (*line_end == '\n')
10843             {
10844                 LogText(IM_NEWLINE);
10845                 g.LogLineFirstItem = true;
10846             }
10847         }
10848         if (is_last_line)
10849             break;
10850         text_remaining = line_end + 1;
10851     }
10852 
10853     if (suffix)
10854         LogRenderedText(ref_pos, suffix, suffix + strlen(suffix));
10855 }
10856 
10857 // Start logging/capturing text output
LogBegin(ImGuiLogType type,int auto_open_depth)10858 void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth)
10859 {
10860     ImGuiContext& g = *GImGui;
10861     ImGuiWindow* window = g.CurrentWindow;
10862     IM_ASSERT(g.LogEnabled == false);
10863     IM_ASSERT(g.LogFile == NULL);
10864     IM_ASSERT(g.LogBuffer.empty());
10865     g.LogEnabled = true;
10866     g.LogType = type;
10867     g.LogNextPrefix = g.LogNextSuffix = NULL;
10868     g.LogDepthRef = window->DC.TreeDepth;
10869     g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);
10870     g.LogLinePosY = FLT_MAX;
10871     g.LogLineFirstItem = true;
10872 }
10873 
10874 // Important: doesn't copy underlying data, use carefully (prefix/suffix must be in scope at the time of the next LogRenderedText)
LogSetNextTextDecoration(const char * prefix,const char * suffix)10875 void ImGui::LogSetNextTextDecoration(const char* prefix, const char* suffix)
10876 {
10877     ImGuiContext& g = *GImGui;
10878     g.LogNextPrefix = prefix;
10879     g.LogNextSuffix = suffix;
10880 }
10881 
LogToTTY(int auto_open_depth)10882 void ImGui::LogToTTY(int auto_open_depth)
10883 {
10884     ImGuiContext& g = *GImGui;
10885     if (g.LogEnabled)
10886         return;
10887     IM_UNUSED(auto_open_depth);
10888 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
10889     LogBegin(ImGuiLogType_TTY, auto_open_depth);
10890     g.LogFile = stdout;
10891 #endif
10892 }
10893 
10894 // Start logging/capturing text output to given file
LogToFile(int auto_open_depth,const char * filename)10895 void ImGui::LogToFile(int auto_open_depth, const char* filename)
10896 {
10897     ImGuiContext& g = *GImGui;
10898     if (g.LogEnabled)
10899         return;
10900 
10901     // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still
10902     // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE.
10903     // By opening the file in binary mode "ab" we have consistent output everywhere.
10904     if (!filename)
10905         filename = g.IO.LogFilename;
10906     if (!filename || !filename[0])
10907         return;
10908     ImFileHandle f = ImFileOpen(filename, "ab");
10909     if (!f)
10910     {
10911         IM_ASSERT(0);
10912         return;
10913     }
10914 
10915     LogBegin(ImGuiLogType_File, auto_open_depth);
10916     g.LogFile = f;
10917 }
10918 
10919 // Start logging/capturing text output to clipboard
LogToClipboard(int auto_open_depth)10920 void ImGui::LogToClipboard(int auto_open_depth)
10921 {
10922     ImGuiContext& g = *GImGui;
10923     if (g.LogEnabled)
10924         return;
10925     LogBegin(ImGuiLogType_Clipboard, auto_open_depth);
10926 }
10927 
LogToBuffer(int auto_open_depth)10928 void ImGui::LogToBuffer(int auto_open_depth)
10929 {
10930     ImGuiContext& g = *GImGui;
10931     if (g.LogEnabled)
10932         return;
10933     LogBegin(ImGuiLogType_Buffer, auto_open_depth);
10934 }
10935 
LogFinish()10936 void ImGui::LogFinish()
10937 {
10938     ImGuiContext& g = *GImGui;
10939     if (!g.LogEnabled)
10940         return;
10941 
10942     LogText(IM_NEWLINE);
10943     switch (g.LogType)
10944     {
10945     case ImGuiLogType_TTY:
10946 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
10947         fflush(g.LogFile);
10948 #endif
10949         break;
10950     case ImGuiLogType_File:
10951         ImFileClose(g.LogFile);
10952         break;
10953     case ImGuiLogType_Buffer:
10954         break;
10955     case ImGuiLogType_Clipboard:
10956         if (!g.LogBuffer.empty())
10957             SetClipboardText(g.LogBuffer.begin());
10958         break;
10959     case ImGuiLogType_None:
10960         IM_ASSERT(0);
10961         break;
10962     }
10963 
10964     g.LogEnabled = false;
10965     g.LogType = ImGuiLogType_None;
10966     g.LogFile = NULL;
10967     g.LogBuffer.clear();
10968 }
10969 
10970 // Helper to display logging buttons
10971 // FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!)
LogButtons()10972 void ImGui::LogButtons()
10973 {
10974     ImGuiContext& g = *GImGui;
10975 
10976     PushID("LogButtons");
10977 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
10978     const bool log_to_tty = Button("Log To TTY"); SameLine();
10979 #else
10980     const bool log_to_tty = false;
10981 #endif
10982     const bool log_to_file = Button("Log To File"); SameLine();
10983     const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
10984     PushAllowKeyboardFocus(false);
10985     SetNextItemWidth(80.0f);
10986     SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL);
10987     PopAllowKeyboardFocus();
10988     PopID();
10989 
10990     // Start logging at the end of the function so that the buttons don't appear in the log
10991     if (log_to_tty)
10992         LogToTTY();
10993     if (log_to_file)
10994         LogToFile();
10995     if (log_to_clipboard)
10996         LogToClipboard();
10997 }
10998 
10999 
11000 //-----------------------------------------------------------------------------
11001 // [SECTION] SETTINGS
11002 //-----------------------------------------------------------------------------
11003 // - UpdateSettings() [Internal]
11004 // - MarkIniSettingsDirty() [Internal]
11005 // - CreateNewWindowSettings() [Internal]
11006 // - FindWindowSettings() [Internal]
11007 // - FindOrCreateWindowSettings() [Internal]
11008 // - FindSettingsHandler() [Internal]
11009 // - ClearIniSettings() [Internal]
11010 // - LoadIniSettingsFromDisk()
11011 // - LoadIniSettingsFromMemory()
11012 // - SaveIniSettingsToDisk()
11013 // - SaveIniSettingsToMemory()
11014 // - WindowSettingsHandler_***() [Internal]
11015 //-----------------------------------------------------------------------------
11016 
11017 // Called by NewFrame()
UpdateSettings()11018 void ImGui::UpdateSettings()
11019 {
11020     // Load settings on first frame (if not explicitly loaded manually before)
11021     ImGuiContext& g = *GImGui;
11022     if (!g.SettingsLoaded)
11023     {
11024         IM_ASSERT(g.SettingsWindows.empty());
11025         if (g.IO.IniFilename)
11026             LoadIniSettingsFromDisk(g.IO.IniFilename);
11027         g.SettingsLoaded = true;
11028     }
11029 
11030     // Save settings (with a delay after the last modification, so we don't spam disk too much)
11031     if (g.SettingsDirtyTimer > 0.0f)
11032     {
11033         g.SettingsDirtyTimer -= g.IO.DeltaTime;
11034         if (g.SettingsDirtyTimer <= 0.0f)
11035         {
11036             if (g.IO.IniFilename != NULL)
11037                 SaveIniSettingsToDisk(g.IO.IniFilename);
11038             else
11039                 g.IO.WantSaveIniSettings = true;  // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
11040             g.SettingsDirtyTimer = 0.0f;
11041         }
11042     }
11043 }
11044 
MarkIniSettingsDirty()11045 void ImGui::MarkIniSettingsDirty()
11046 {
11047     ImGuiContext& g = *GImGui;
11048     if (g.SettingsDirtyTimer <= 0.0f)
11049         g.SettingsDirtyTimer = g.IO.IniSavingRate;
11050 }
11051 
MarkIniSettingsDirty(ImGuiWindow * window)11052 void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
11053 {
11054     ImGuiContext& g = *GImGui;
11055     if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
11056         if (g.SettingsDirtyTimer <= 0.0f)
11057             g.SettingsDirtyTimer = g.IO.IniSavingRate;
11058 }
11059 
CreateNewWindowSettings(const char * name)11060 ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
11061 {
11062     ImGuiContext& g = *GImGui;
11063 
11064 #if !IMGUI_DEBUG_INI_SETTINGS
11065     // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
11066     // Preserve the full string when IMGUI_DEBUG_INI_SETTINGS is set to make .ini inspection easier.
11067     if (const char* p = strstr(name, "###"))
11068         name = p;
11069 #endif
11070     const size_t name_len = strlen(name);
11071 
11072     // Allocate chunk
11073     const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1;
11074     ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size);
11075     IM_PLACEMENT_NEW(settings) ImGuiWindowSettings();
11076     settings->ID = ImHashStr(name, name_len);
11077     memcpy(settings->GetName(), name, name_len + 1);   // Store with zero terminator
11078 
11079     return settings;
11080 }
11081 
FindWindowSettings(ImGuiID id)11082 ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
11083 {
11084     ImGuiContext& g = *GImGui;
11085     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
11086         if (settings->ID == id)
11087             return settings;
11088     return NULL;
11089 }
11090 
FindOrCreateWindowSettings(const char * name)11091 ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name)
11092 {
11093     if (ImGuiWindowSettings* settings = FindWindowSettings(ImHashStr(name)))
11094         return settings;
11095     return CreateNewWindowSettings(name);
11096 }
11097 
FindSettingsHandler(const char * type_name)11098 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
11099 {
11100     ImGuiContext& g = *GImGui;
11101     const ImGuiID type_hash = ImHashStr(type_name);
11102     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
11103         if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
11104             return &g.SettingsHandlers[handler_n];
11105     return NULL;
11106 }
11107 
ClearIniSettings()11108 void ImGui::ClearIniSettings()
11109 {
11110     ImGuiContext& g = *GImGui;
11111     g.SettingsIniData.clear();
11112     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
11113         if (g.SettingsHandlers[handler_n].ClearAllFn)
11114             g.SettingsHandlers[handler_n].ClearAllFn(&g, &g.SettingsHandlers[handler_n]);
11115 }
11116 
LoadIniSettingsFromDisk(const char * ini_filename)11117 void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
11118 {
11119     size_t file_data_size = 0;
11120     char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
11121     if (!file_data)
11122         return;
11123     LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
11124     IM_FREE(file_data);
11125 }
11126 
11127 // Zero-tolerance, no error reporting, cheap .ini parsing
LoadIniSettingsFromMemory(const char * ini_data,size_t ini_size)11128 void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
11129 {
11130     ImGuiContext& g = *GImGui;
11131     IM_ASSERT(g.Initialized);
11132     //IM_ASSERT(!g.WithinFrameScope && "Cannot be called between NewFrame() and EndFrame()");
11133     //IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
11134 
11135     // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
11136     // 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..
11137     if (ini_size == 0)
11138         ini_size = strlen(ini_data);
11139     g.SettingsIniData.Buf.resize((int)ini_size + 1);
11140     char* const buf = g.SettingsIniData.Buf.Data;
11141     char* const buf_end = buf + ini_size;
11142     memcpy(buf, ini_data, ini_size);
11143     buf_end[0] = 0;
11144 
11145     // Call pre-read handlers
11146     // Some types will clear their data (e.g. dock information) some types will allow merge/override (window)
11147     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
11148         if (g.SettingsHandlers[handler_n].ReadInitFn)
11149             g.SettingsHandlers[handler_n].ReadInitFn(&g, &g.SettingsHandlers[handler_n]);
11150 
11151     void* entry_data = NULL;
11152     ImGuiSettingsHandler* entry_handler = NULL;
11153 
11154     char* line_end = NULL;
11155     for (char* line = buf; line < buf_end; line = line_end + 1)
11156     {
11157         // Skip new lines markers, then find end of the line
11158         while (*line == '\n' || *line == '\r')
11159             line++;
11160         line_end = line;
11161         while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
11162             line_end++;
11163         line_end[0] = 0;
11164         if (line[0] == ';')
11165             continue;
11166         if (line[0] == '[' && line_end > line && line_end[-1] == ']')
11167         {
11168             // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
11169             line_end[-1] = 0;
11170             const char* name_end = line_end - 1;
11171             const char* type_start = line + 1;
11172             char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']');
11173             const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
11174             if (!type_end || !name_start)
11175                 continue;
11176             *type_end = 0; // Overwrite first ']'
11177             name_start++;  // Skip second '['
11178             entry_handler = FindSettingsHandler(type_start);
11179             entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
11180         }
11181         else if (entry_handler != NULL && entry_data != NULL)
11182         {
11183             // Let type handler parse the line
11184             entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
11185         }
11186     }
11187     g.SettingsLoaded = true;
11188 
11189     // [DEBUG] Restore untouched copy so it can be browsed in Metrics (not strictly necessary)
11190     memcpy(buf, ini_data, ini_size);
11191 
11192     // Call post-read handlers
11193     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
11194         if (g.SettingsHandlers[handler_n].ApplyAllFn)
11195             g.SettingsHandlers[handler_n].ApplyAllFn(&g, &g.SettingsHandlers[handler_n]);
11196 }
11197 
SaveIniSettingsToDisk(const char * ini_filename)11198 void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
11199 {
11200     ImGuiContext& g = *GImGui;
11201     g.SettingsDirtyTimer = 0.0f;
11202     if (!ini_filename)
11203         return;
11204 
11205     size_t ini_data_size = 0;
11206     const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
11207     ImFileHandle f = ImFileOpen(ini_filename, "wt");
11208     if (!f)
11209         return;
11210     ImFileWrite(ini_data, sizeof(char), ini_data_size, f);
11211     ImFileClose(f);
11212 }
11213 
11214 // Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
SaveIniSettingsToMemory(size_t * out_size)11215 const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
11216 {
11217     ImGuiContext& g = *GImGui;
11218     g.SettingsDirtyTimer = 0.0f;
11219     g.SettingsIniData.Buf.resize(0);
11220     g.SettingsIniData.Buf.push_back(0);
11221     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
11222     {
11223         ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
11224         handler->WriteAllFn(&g, handler, &g.SettingsIniData);
11225     }
11226     if (out_size)
11227         *out_size = (size_t)g.SettingsIniData.size();
11228     return g.SettingsIniData.c_str();
11229 }
11230 
WindowSettingsHandler_ClearAll(ImGuiContext * ctx,ImGuiSettingsHandler *)11231 static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
11232 {
11233     ImGuiContext& g = *ctx;
11234     for (int i = 0; i != g.Windows.Size; i++)
11235         g.Windows[i]->SettingsOffset = -1;
11236     g.SettingsWindows.clear();
11237 }
11238 
WindowSettingsHandler_ReadOpen(ImGuiContext *,ImGuiSettingsHandler *,const char * name)11239 static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
11240 {
11241     ImGuiWindowSettings* settings = ImGui::FindOrCreateWindowSettings(name);
11242     ImGuiID id = settings->ID;
11243     *settings = ImGuiWindowSettings(); // Clear existing if recycling previous entry
11244     settings->ID = id;
11245     settings->WantApply = true;
11246     return (void*)settings;
11247 }
11248 
WindowSettingsHandler_ReadLine(ImGuiContext *,ImGuiSettingsHandler *,void * entry,const char * line)11249 static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
11250 {
11251     ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
11252     int x, y;
11253     int i;
11254     ImU32 u1;
11255     if (sscanf(line, "Pos=%i,%i", &x, &y) == 2)             { settings->Pos = ImVec2ih((short)x, (short)y); }
11256     else if (sscanf(line, "Size=%i,%i", &x, &y) == 2)       { settings->Size = ImVec2ih((short)x, (short)y); }
11257     else if (sscanf(line, "ViewportId=0x%08X", &u1) == 1)   { settings->ViewportId = u1; }
11258     else if (sscanf(line, "ViewportPos=%i,%i", &x, &y) == 2){ settings->ViewportPos = ImVec2ih((short)x, (short)y); }
11259     else if (sscanf(line, "Collapsed=%d", &i) == 1)         { settings->Collapsed = (i != 0); }
11260     else if (sscanf(line, "DockId=0x%X,%d", &u1, &i) == 2)  { settings->DockId = u1; settings->DockOrder = (short)i; }
11261     else if (sscanf(line, "DockId=0x%X", &u1) == 1)         { settings->DockId = u1; settings->DockOrder = -1; }
11262     else if (sscanf(line, "ClassId=0x%X", &u1) == 1)        { settings->ClassId = u1; }
11263 }
11264 
11265 // Apply to existing windows (if any)
WindowSettingsHandler_ApplyAll(ImGuiContext * ctx,ImGuiSettingsHandler *)11266 static void WindowSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
11267 {
11268     ImGuiContext& g = *ctx;
11269     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
11270         if (settings->WantApply)
11271         {
11272             if (ImGuiWindow* window = ImGui::FindWindowByID(settings->ID))
11273                 ApplyWindowSettings(window, settings);
11274             settings->WantApply = false;
11275         }
11276 }
11277 
WindowSettingsHandler_WriteAll(ImGuiContext * ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)11278 static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
11279 {
11280     // Gather data from windows that were active during this session
11281     // (if a window wasn't opened in this session we preserve its settings)
11282     ImGuiContext& g = *ctx;
11283     for (int i = 0; i != g.Windows.Size; i++)
11284     {
11285         ImGuiWindow* window = g.Windows[i];
11286         if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
11287             continue;
11288 
11289         ImGuiWindowSettings* settings = (window->SettingsOffset != -1) ? g.SettingsWindows.ptr_from_offset(window->SettingsOffset) : ImGui::FindWindowSettings(window->ID);
11290         if (!settings)
11291         {
11292             settings = ImGui::CreateNewWindowSettings(window->Name);
11293             window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
11294         }
11295         IM_ASSERT(settings->ID == window->ID);
11296         settings->Pos = ImVec2ih(window->Pos - window->ViewportPos);
11297         settings->Size = ImVec2ih(window->SizeFull);
11298         settings->ViewportId = window->ViewportId;
11299         settings->ViewportPos = ImVec2ih(window->ViewportPos);
11300         IM_ASSERT(window->DockNode == NULL || window->DockNode->ID == window->DockId);
11301         settings->DockId = window->DockId;
11302         settings->ClassId = window->WindowClass.ClassId;
11303         settings->DockOrder = window->DockOrder;
11304         settings->Collapsed = window->Collapsed;
11305     }
11306 
11307     // Write to text buffer
11308     buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve
11309     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
11310     {
11311         const char* settings_name = settings->GetName();
11312         buf->appendf("[%s][%s]\n", handler->TypeName, settings_name);
11313         if (settings->ViewportId != 0 && settings->ViewportId != ImGui::IMGUI_VIEWPORT_DEFAULT_ID)
11314         {
11315             buf->appendf("ViewportPos=%d,%d\n", settings->ViewportPos.x, settings->ViewportPos.y);
11316             buf->appendf("ViewportId=0x%08X\n", settings->ViewportId);
11317         }
11318         if (settings->Pos.x != 0 || settings->Pos.y != 0 || settings->ViewportId == ImGui::IMGUI_VIEWPORT_DEFAULT_ID)
11319             buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
11320         if (settings->Size.x != 0 || settings->Size.y != 0)
11321             buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
11322         buf->appendf("Collapsed=%d\n", settings->Collapsed);
11323         if (settings->DockId != 0)
11324         {
11325             if (settings->DockOrder == -1)
11326                 buf->appendf("DockId=0x%08X\n", settings->DockId);
11327             else
11328                 buf->appendf("DockId=0x%08X,%d\n", settings->DockId, settings->DockOrder);
11329             if (settings->ClassId != 0)
11330                 buf->appendf("ClassId=0x%08X\n", settings->ClassId);
11331         }
11332         buf->append("\n");
11333     }
11334 }
11335 
11336 
11337 //-----------------------------------------------------------------------------
11338 // [SECTION] VIEWPORTS, PLATFORM WINDOWS
11339 //-----------------------------------------------------------------------------
11340 // - GetMainViewport()
11341 // - FindViewportByID()
11342 // - FindViewportByPlatformHandle()
11343 // - SetCurrentViewport() [Internal]
11344 // - SetWindowViewport() [Internal]
11345 // - GetWindowAlwaysWantOwnViewport() [Internal]
11346 // - UpdateTryMergeWindowIntoHostViewport() [Internal]
11347 // - UpdateTryMergeWindowIntoHostViewports() [Internal]
11348 // - TranslateWindowsInViewport() [Internal]
11349 // - ScaleWindowsInViewport() [Internal]
11350 // - FindHoveredViewportFromPlatformWindowStack() [Internal]
11351 // - UpdateViewportsNewFrame() [Internal]
11352 // - UpdateViewportsEndFrame() [Internal]
11353 // - AddUpdateViewport() [Internal]
11354 // - WindowSelectViewport() [Internal]
11355 // - WindowSyncOwnedViewport() [Internal]
11356 // - UpdatePlatformWindows()
11357 // - RenderPlatformWindowsDefault()
11358 // - FindPlatformMonitorForPos() [Internal]
11359 // - FindPlatformMonitorForRect() [Internal]
11360 // - UpdateViewportPlatformMonitor() [Internal]
11361 // - DestroyPlatformWindow() [Internal]
11362 // - DestroyPlatformWindows()
11363 //-----------------------------------------------------------------------------
11364 
GetMainViewport()11365 ImGuiViewport* ImGui::GetMainViewport()
11366 {
11367     ImGuiContext& g = *GImGui;
11368     return g.Viewports[0];
11369 }
11370 
11371 // FIXME: This leaks access to viewports not listed in PlatformIO.Viewports[]. Problematic? (#4236)
FindViewportByID(ImGuiID id)11372 ImGuiViewport* ImGui::FindViewportByID(ImGuiID id)
11373 {
11374     ImGuiContext& g = *GImGui;
11375     for (int n = 0; n < g.Viewports.Size; n++)
11376         if (g.Viewports[n]->ID == id)
11377             return g.Viewports[n];
11378     return NULL;
11379 }
11380 
FindViewportByPlatformHandle(void * platform_handle)11381 ImGuiViewport* ImGui::FindViewportByPlatformHandle(void* platform_handle)
11382 {
11383     ImGuiContext& g = *GImGui;
11384     for (int i = 0; i != g.Viewports.Size; i++)
11385         if (g.Viewports[i]->PlatformHandle == platform_handle)
11386             return g.Viewports[i];
11387     return NULL;
11388 }
11389 
SetCurrentViewport(ImGuiWindow * current_window,ImGuiViewportP * viewport)11390 void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* viewport)
11391 {
11392     ImGuiContext& g = *GImGui;
11393     (void)current_window;
11394 
11395     if (viewport)
11396         viewport->LastFrameActive = g.FrameCount;
11397     if (g.CurrentViewport == viewport)
11398         return;
11399     g.CurrentDpiScale = viewport ? viewport->DpiScale : 1.0f;
11400     g.CurrentViewport = viewport;
11401     //IMGUI_DEBUG_LOG_VIEWPORT("SetCurrentViewport changed '%s' 0x%08X\n", current_window ? current_window->Name : NULL, viewport ? viewport->ID : 0);
11402 
11403     // Notify platform layer of viewport changes
11404     // FIXME-DPI: This is only currently used for experimenting with handling of multiple DPI
11405     if (g.CurrentViewport && g.PlatformIO.Platform_OnChangedViewport)
11406         g.PlatformIO.Platform_OnChangedViewport(g.CurrentViewport);
11407 }
11408 
SetWindowViewport(ImGuiWindow * window,ImGuiViewportP * viewport)11409 static void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport)
11410 {
11411     window->Viewport = viewport;
11412     window->ViewportId = viewport->ID;
11413     window->ViewportOwned = (viewport->Window == window);
11414 }
11415 
GetWindowAlwaysWantOwnViewport(ImGuiWindow * window)11416 static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window)
11417 {
11418     // 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.
11419     ImGuiContext& g = *GImGui;
11420     if (g.IO.ConfigViewportsNoAutoMerge || (window->WindowClass.ViewportFlagsOverrideSet & ImGuiViewportFlags_NoAutoMerge))
11421         if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)
11422             if (!window->DockIsActive)
11423                 if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip)) == 0)
11424                     if ((window->Flags & ImGuiWindowFlags_Popup) == 0 || (window->Flags & ImGuiWindowFlags_Modal) != 0)
11425                         return true;
11426     return false;
11427 }
11428 
UpdateTryMergeWindowIntoHostViewport(ImGuiWindow * window,ImGuiViewportP * viewport)11429 static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport)
11430 {
11431     ImGuiContext& g = *GImGui;
11432     if (window->Viewport == viewport)
11433         return false;
11434     if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) == 0)
11435         return false;
11436     if ((viewport->Flags & ImGuiViewportFlags_Minimized) != 0)
11437         return false;
11438     if (!viewport->GetMainRect().Contains(window->Rect()))
11439         return false;
11440     if (GetWindowAlwaysWantOwnViewport(window))
11441         return false;
11442 
11443     for (int n = 0; n < g.Windows.Size; n++)
11444     {
11445         ImGuiWindow* window_behind = g.Windows[n];
11446         if (window_behind == window)
11447             break;
11448         if (window_behind->WasActive && window_behind->ViewportOwned && !(window_behind->Flags & ImGuiWindowFlags_ChildWindow))
11449             if (window_behind->Viewport->GetMainRect().Overlaps(window->Rect()))
11450                 return false;
11451     }
11452 
11453     // Move to the existing viewport, Move child/hosted windows as well (FIXME-OPT: iterate child)
11454     ImGuiViewportP* old_viewport = window->Viewport;
11455     if (window->ViewportOwned)
11456         for (int n = 0; n < g.Windows.Size; n++)
11457             if (g.Windows[n]->Viewport == old_viewport)
11458                 SetWindowViewport(g.Windows[n], viewport);
11459     SetWindowViewport(window, viewport);
11460     BringWindowToDisplayFront(window);
11461 
11462     return true;
11463 }
11464 
11465 // FIXME: handle 0 to N host viewports
UpdateTryMergeWindowIntoHostViewports(ImGuiWindow * window)11466 static bool ImGui::UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window)
11467 {
11468     ImGuiContext& g = *GImGui;
11469     return UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]);
11470 }
11471 
11472 // Translate Dear ImGui windows when a Host Viewport has been moved
11473 // (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!)
TranslateWindowsInViewport(ImGuiViewportP * viewport,const ImVec2 & old_pos,const ImVec2 & new_pos)11474 void ImGui::TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos)
11475 {
11476     ImGuiContext& g = *GImGui;
11477     IM_ASSERT(viewport->Window == NULL && (viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows));
11478 
11479     // 1) We test if ImGuiConfigFlags_ViewportsEnable was just toggled, which allows us to conveniently
11480     // translate imgui windows from OS-window-local to absolute coordinates or vice-versa.
11481     // 2) If it's not going to fit into the new size, keep it at same absolute position.
11482     // One problem with this is that most Win32 applications doesn't update their render while dragging,
11483     // and so the window will appear to teleport when releasing the mouse.
11484     const bool translate_all_windows = (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) != (g.ConfigFlagsLastFrame & ImGuiConfigFlags_ViewportsEnable);
11485     ImRect test_still_fit_rect(old_pos, old_pos + viewport->Size);
11486     ImVec2 delta_pos = new_pos - old_pos;
11487     for (int window_n = 0; window_n < g.Windows.Size; window_n++) // FIXME-OPT
11488         if (translate_all_windows || (g.Windows[window_n]->Viewport == viewport && test_still_fit_rect.Contains(g.Windows[window_n]->Rect())))
11489             TranslateWindow(g.Windows[window_n], delta_pos);
11490 }
11491 
11492 // Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!)
ScaleWindowsInViewport(ImGuiViewportP * viewport,float scale)11493 void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale)
11494 {
11495     ImGuiContext& g = *GImGui;
11496     if (viewport->Window)
11497     {
11498         ScaleWindow(viewport->Window, scale);
11499     }
11500     else
11501     {
11502         for (int i = 0; i != g.Windows.Size; i++)
11503             if (g.Windows[i]->Viewport == viewport)
11504                 ScaleWindow(g.Windows[i], scale);
11505     }
11506 }
11507 
11508 // If the backend doesn't set MouseLastHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs, we do a search ourselves.
11509 // A) It won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window.
11510 // B) It requires Platform_GetWindowFocus to be implemented by backend.
FindHoveredViewportFromPlatformWindowStack(const ImVec2 mouse_platform_pos)11511 static ImGuiViewportP* FindHoveredViewportFromPlatformWindowStack(const ImVec2 mouse_platform_pos)
11512 {
11513     ImGuiContext& g = *GImGui;
11514     ImGuiViewportP* best_candidate = NULL;
11515     for (int n = 0; n < g.Viewports.Size; n++)
11516     {
11517         ImGuiViewportP* viewport = g.Viewports[n];
11518         if (!(viewport->Flags & (ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_Minimized)) && viewport->GetMainRect().Contains(mouse_platform_pos))
11519             if (best_candidate == NULL || best_candidate->LastFrontMostStampCount < viewport->LastFrontMostStampCount)
11520                 best_candidate = viewport;
11521     }
11522     return best_candidate;
11523 }
11524 
11525 // Update viewports and monitor infos
11526 // 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()11527 static void ImGui::UpdateViewportsNewFrame()
11528 {
11529     ImGuiContext& g = *GImGui;
11530     IM_ASSERT(g.PlatformIO.Viewports.Size <= g.Viewports.Size);
11531 
11532     // Update Minimized status (we need it first in order to decide if we'll apply Pos/Size of the main viewport)
11533     const bool viewports_enabled = (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) != 0;
11534     if (viewports_enabled)
11535     {
11536         for (int n = 0; n < g.Viewports.Size; n++)
11537         {
11538             ImGuiViewportP* viewport = g.Viewports[n];
11539             const bool platform_funcs_available = viewport->PlatformWindowCreated;
11540             if (g.PlatformIO.Platform_GetWindowMinimized && platform_funcs_available)
11541             {
11542                 bool minimized = g.PlatformIO.Platform_GetWindowMinimized(viewport);
11543                 if (minimized)
11544                     viewport->Flags |= ImGuiViewportFlags_Minimized;
11545                 else
11546                     viewport->Flags &= ~ImGuiViewportFlags_Minimized;
11547             }
11548         }
11549     }
11550 
11551     // Create/update main viewport with current platform position.
11552     // FIXME-VIEWPORT: Size is driven by backend/user code for backward-compatibility but we should aim to make this more consistent.
11553     ImGuiViewportP* main_viewport = g.Viewports[0];
11554     IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID);
11555     IM_ASSERT(main_viewport->Window == NULL);
11556     ImVec2 main_viewport_pos = viewports_enabled ? g.PlatformIO.Platform_GetWindowPos(main_viewport) : ImVec2(0.0f, 0.0f);
11557     ImVec2 main_viewport_size = g.IO.DisplaySize;
11558     if (viewports_enabled && (main_viewport->Flags & ImGuiViewportFlags_Minimized))
11559     {
11560         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)
11561         main_viewport_size = main_viewport->Size;
11562     }
11563     AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_pos, main_viewport_size, ImGuiViewportFlags_OwnedByApp | ImGuiViewportFlags_CanHostOtherWindows);
11564 
11565     g.CurrentDpiScale = 0.0f;
11566     g.CurrentViewport = NULL;
11567     g.MouseViewport = NULL;
11568     for (int n = 0; n < g.Viewports.Size; n++)
11569     {
11570         ImGuiViewportP* viewport = g.Viewports[n];
11571         viewport->Idx = n;
11572 
11573         // Erase unused viewports
11574         if (n > 0 && viewport->LastFrameActive < g.FrameCount - 2)
11575         {
11576             // Clear references to this viewport in windows (window->ViewportId becomes the master data)
11577             for (int window_n = 0; window_n < g.Windows.Size; window_n++)
11578                 if (g.Windows[window_n]->Viewport == viewport)
11579                 {
11580                     g.Windows[window_n]->Viewport = NULL;
11581                     g.Windows[window_n]->ViewportOwned = false;
11582                 }
11583             if (viewport == g.MouseLastHoveredViewport)
11584                 g.MouseLastHoveredViewport = NULL;
11585             g.Viewports.erase(g.Viewports.Data + n);
11586 
11587             // Destroy
11588             IMGUI_DEBUG_LOG_VIEWPORT("Delete Viewport %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a");
11589             DestroyPlatformWindow(viewport); // In most circumstances the platform window will already be destroyed here.
11590             IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false);
11591             IM_DELETE(viewport);
11592             n--;
11593             continue;
11594         }
11595 
11596         const bool platform_funcs_available = viewport->PlatformWindowCreated;
11597         if (viewports_enabled)
11598         {
11599             // Update Position and Size (from Platform Window to ImGui) if requested.
11600             // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities.
11601             if (!(viewport->Flags & ImGuiViewportFlags_Minimized) && platform_funcs_available)
11602             {
11603                 // Viewport->WorkPos and WorkSize will be updated below
11604                 if (viewport->PlatformRequestMove)
11605                     viewport->Pos = viewport->LastPlatformPos = g.PlatformIO.Platform_GetWindowPos(viewport);
11606                 if (viewport->PlatformRequestResize)
11607                     viewport->Size = viewport->LastPlatformSize = g.PlatformIO.Platform_GetWindowSize(viewport);
11608             }
11609         }
11610 
11611         // Update/copy monitor info
11612         UpdateViewportPlatformMonitor(viewport);
11613 
11614         // Lock down space taken by menu bars and status bars, reset the offset for functions like BeginMainMenuBar() to alter them again.
11615         viewport->WorkOffsetMin = viewport->BuildWorkOffsetMin;
11616         viewport->WorkOffsetMax = viewport->BuildWorkOffsetMax;
11617         viewport->BuildWorkOffsetMin = viewport->BuildWorkOffsetMax = ImVec2(0.0f, 0.0f);
11618         viewport->UpdateWorkRect();
11619 
11620         // Reset alpha every frame. Users of transparency (docking) needs to request a lower alpha back.
11621         viewport->Alpha = 1.0f;
11622 
11623         // Translate imgui windows when a Host Viewport has been moved
11624         // (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!)
11625         const ImVec2 viewport_delta_pos = viewport->Pos - viewport->LastPos;
11626         if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (viewport_delta_pos.x != 0.0f || viewport_delta_pos.y != 0.0f))
11627             TranslateWindowsInViewport(viewport, viewport->LastPos, viewport->Pos);
11628 
11629         // Update DPI scale
11630         float new_dpi_scale;
11631         if (g.PlatformIO.Platform_GetWindowDpiScale && platform_funcs_available)
11632             new_dpi_scale = g.PlatformIO.Platform_GetWindowDpiScale(viewport);
11633         else if (viewport->PlatformMonitor != -1)
11634             new_dpi_scale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale;
11635         else
11636             new_dpi_scale = (viewport->DpiScale != 0.0f) ? viewport->DpiScale : 1.0f;
11637         if (viewport->DpiScale != 0.0f && new_dpi_scale != viewport->DpiScale)
11638         {
11639             float scale_factor = new_dpi_scale / viewport->DpiScale;
11640             if (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports)
11641                 ScaleWindowsInViewport(viewport, scale_factor);
11642             //if (viewport == GetMainViewport())
11643             //    g.PlatformInterface.SetWindowSize(viewport, viewport->Size * scale_factor);
11644 
11645             // Scale our window moving pivot so that the window will rescale roughly around the mouse position.
11646             // FIXME-VIEWPORT: This currently creates a resizing feedback loop when a window is straddling a DPI transition border.
11647             // (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.)
11648             //if (g.MovingWindow != NULL && g.MovingWindow->Viewport == viewport)
11649             //    g.ActiveIdClickOffset = ImFloor(g.ActiveIdClickOffset * scale_factor);
11650         }
11651         viewport->DpiScale = new_dpi_scale;
11652     }
11653 
11654     // Update fallback monitor
11655     if (g.PlatformIO.Monitors.Size == 0)
11656     {
11657         ImGuiPlatformMonitor* monitor = &g.FallbackMonitor;
11658         monitor->MainPos = main_viewport->Pos;
11659         monitor->MainSize = main_viewport->Size;
11660         monitor->WorkPos = main_viewport->WorkPos;
11661         monitor->WorkSize = main_viewport->WorkSize;
11662         monitor->DpiScale = main_viewport->DpiScale;
11663     }
11664 
11665     if (!viewports_enabled)
11666     {
11667         g.MouseViewport = main_viewport;
11668         return;
11669     }
11670 
11671     // Mouse handling: decide on the actual mouse viewport for this frame between the active/focused viewport and the hovered viewport.
11672     // Note that 'viewport_hovered' should skip over any viewport that has the ImGuiViewportFlags_NoInputs flags set.
11673     ImGuiViewportP* viewport_hovered = NULL;
11674     if (g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport)
11675     {
11676         viewport_hovered = g.IO.MouseHoveredViewport ? (ImGuiViewportP*)FindViewportByID(g.IO.MouseHoveredViewport) : NULL;
11677         if (viewport_hovered && (viewport_hovered->Flags & ImGuiViewportFlags_NoInputs))
11678         {
11679             // Backend failed at honoring its contract if it returned a viewport with the _NoInputs flag.
11680             IM_ASSERT(0);
11681             viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos);
11682         }
11683     }
11684     else
11685     {
11686         // If the backend doesn't know how to honor ImGuiViewportFlags_NoInputs, we do a search ourselves. Note that this search:
11687         // A) won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window.
11688         // 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)
11689         viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos);
11690     }
11691     if (viewport_hovered != NULL)
11692         g.MouseLastHoveredViewport = viewport_hovered;
11693     else if (g.MouseLastHoveredViewport == NULL)
11694         g.MouseLastHoveredViewport = g.Viewports[0];
11695 
11696     // Update mouse reference viewport
11697     // (when moving a window we aim at its viewport, but this will be overwritten below if we go in drag and drop mode)
11698     if (g.MovingWindow)
11699         g.MouseViewport = g.MovingWindow->Viewport;
11700     else
11701         g.MouseViewport = g.MouseLastHoveredViewport;
11702 
11703     // When dragging something, always refer to the last hovered viewport.
11704     // - when releasing a moving window we will revert to aiming behind (at viewport_hovered)
11705     // - 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)
11706     // - 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.
11707     // FIXME-VIEWPORT: This is essentially broken, when ImGuiBackendFlags_HasMouseHoveredViewport is set we want to trust when viewport_hovered==NULL and use that.
11708     const bool is_mouse_dragging_with_an_expected_destination = g.DragDropActive;
11709     if (is_mouse_dragging_with_an_expected_destination && viewport_hovered == NULL)
11710         viewport_hovered = g.MouseLastHoveredViewport;
11711     if (is_mouse_dragging_with_an_expected_destination || g.ActiveId == 0 || !IsAnyMouseDown())
11712         if (viewport_hovered != NULL && viewport_hovered != g.MouseViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs))
11713             g.MouseViewport = viewport_hovered;
11714 
11715     IM_ASSERT(g.MouseViewport != NULL);
11716 }
11717 
11718 // Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some)
UpdateViewportsEndFrame()11719 static void ImGui::UpdateViewportsEndFrame()
11720 {
11721     ImGuiContext& g = *GImGui;
11722     g.PlatformIO.Viewports.resize(0);
11723     for (int i = 0; i < g.Viewports.Size; i++)
11724     {
11725         ImGuiViewportP* viewport = g.Viewports[i];
11726         viewport->LastPos = viewport->Pos;
11727         if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0.0f || viewport->Size.y <= 0.0f)
11728             if (i > 0) // Always include main viewport in the list
11729                 continue;
11730         if (viewport->Window && !IsWindowActiveAndVisible(viewport->Window))
11731             continue;
11732         if (i > 0)
11733             IM_ASSERT(viewport->Window != NULL);
11734         g.PlatformIO.Viewports.push_back(viewport);
11735     }
11736     g.Viewports[0]->ClearRequestFlags(); // Clear main viewport flags because UpdatePlatformWindows() won't do it and may not even be called
11737 }
11738 
11739 // 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)11740 ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& pos, const ImVec2& size, ImGuiViewportFlags flags)
11741 {
11742     ImGuiContext& g = *GImGui;
11743     IM_ASSERT(id != 0);
11744 
11745     flags |= ImGuiViewportFlags_IsPlatformWindow;
11746     if (window != NULL)
11747     {
11748         if (g.MovingWindow && g.MovingWindow->RootWindowDockTree == window)
11749             flags |= ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_NoFocusOnAppearing;
11750         if ((window->Flags & ImGuiWindowFlags_NoMouseInputs) && (window->Flags & ImGuiWindowFlags_NoNavInputs))
11751             flags |= ImGuiViewportFlags_NoInputs;
11752         if (window->Flags & ImGuiWindowFlags_NoFocusOnAppearing)
11753             flags |= ImGuiViewportFlags_NoFocusOnAppearing;
11754     }
11755 
11756     ImGuiViewportP* viewport = (ImGuiViewportP*)FindViewportByID(id);
11757     if (viewport)
11758     {
11759         if (!viewport->PlatformRequestMove)
11760             viewport->Pos = pos;
11761         if (!viewport->PlatformRequestResize)
11762             viewport->Size = size;
11763         viewport->Flags = flags | (viewport->Flags & ImGuiViewportFlags_Minimized); // Preserve existing flags
11764     }
11765     else
11766     {
11767         // New viewport
11768         viewport = IM_NEW(ImGuiViewportP)();
11769         viewport->ID = id;
11770         viewport->Idx = g.Viewports.Size;
11771         viewport->Pos = viewport->LastPos = pos;
11772         viewport->Size = size;
11773         viewport->Flags = flags;
11774         UpdateViewportPlatformMonitor(viewport);
11775         g.Viewports.push_back(viewport);
11776         IMGUI_DEBUG_LOG_VIEWPORT("Add Viewport %08X (%s)\n", id, window->Name);
11777 
11778         // We normally setup for all viewports in NewFrame() but here need to handle the mid-frame creation of a new viewport.
11779         // We need to extend the fullscreen clip rect so the OverlayDrawList clip is correct for that the first frame
11780         g.DrawListSharedData.ClipRectFullscreen.x = ImMin(g.DrawListSharedData.ClipRectFullscreen.x, viewport->Pos.x);
11781         g.DrawListSharedData.ClipRectFullscreen.y = ImMin(g.DrawListSharedData.ClipRectFullscreen.y, viewport->Pos.y);
11782         g.DrawListSharedData.ClipRectFullscreen.z = ImMax(g.DrawListSharedData.ClipRectFullscreen.z, viewport->Pos.x + viewport->Size.x);
11783         g.DrawListSharedData.ClipRectFullscreen.w = ImMax(g.DrawListSharedData.ClipRectFullscreen.w, viewport->Pos.y + viewport->Size.y);
11784 
11785         // Store initial DpiScale before the OS platform window creation, based on expected monitor data.
11786         // This is so we can select an appropriate font size on the first frame of our window lifetime
11787         if (viewport->PlatformMonitor != -1)
11788             viewport->DpiScale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale;
11789     }
11790 
11791     viewport->Window = window;
11792     viewport->LastFrameActive = g.FrameCount;
11793     viewport->UpdateWorkRect();
11794     IM_ASSERT(window == NULL || viewport->ID == window->ID);
11795 
11796     if (window != NULL)
11797         window->ViewportOwned = true;
11798 
11799     return viewport;
11800 }
11801 
11802 // FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten.
WindowSelectViewport(ImGuiWindow * window)11803 static void ImGui::WindowSelectViewport(ImGuiWindow* window)
11804 {
11805     ImGuiContext& g = *GImGui;
11806     ImGuiWindowFlags flags = window->Flags;
11807     window->ViewportAllowPlatformMonitorExtend = -1;
11808 
11809     // Restore main viewport if multi-viewport is not supported by the backend
11810     ImGuiViewportP* main_viewport = (ImGuiViewportP*)(void*)GetMainViewport();
11811     if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable))
11812     {
11813         SetWindowViewport(window, main_viewport);
11814         return;
11815     }
11816     window->ViewportOwned = false;
11817 
11818     // Appearing popups reset their viewport so they can inherit again
11819     if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && window->Appearing)
11820     {
11821         window->Viewport = NULL;
11822         window->ViewportId = 0;
11823     }
11824 
11825     if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasViewport) == 0)
11826     {
11827         // By default inherit from parent window
11828         if (window->Viewport == NULL && window->ParentWindow && (!window->ParentWindow->IsFallbackWindow || window->ParentWindow->WasActive))
11829             window->Viewport = window->ParentWindow->Viewport;
11830 
11831         // 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
11832         if (window->Viewport == NULL && window->ViewportId != 0)
11833         {
11834             window->Viewport = (ImGuiViewportP*)FindViewportByID(window->ViewportId);
11835             if (window->Viewport == NULL && window->ViewportPos.x != FLT_MAX && window->ViewportPos.y != FLT_MAX)
11836                 window->Viewport = AddUpdateViewport(window, window->ID, window->ViewportPos, window->Size, ImGuiViewportFlags_None);
11837         }
11838     }
11839 
11840     bool lock_viewport = false;
11841     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasViewport)
11842     {
11843         // Code explicitly request a viewport
11844         window->Viewport = (ImGuiViewportP*)FindViewportByID(g.NextWindowData.ViewportId);
11845         window->ViewportId = g.NextWindowData.ViewportId; // Store ID even if Viewport isn't resolved yet.
11846         lock_viewport = true;
11847     }
11848     else if ((flags & ImGuiWindowFlags_ChildWindow) || (flags & ImGuiWindowFlags_ChildMenu))
11849     {
11850         // Always inherit viewport from parent window
11851         window->Viewport = window->ParentWindow->Viewport;
11852     }
11853     else if (flags & ImGuiWindowFlags_Tooltip)
11854     {
11855         window->Viewport = g.MouseViewport;
11856     }
11857     else if (GetWindowAlwaysWantOwnViewport(window))
11858     {
11859         window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None);
11860     }
11861     else if (g.MovingWindow && g.MovingWindow->RootWindowDockTree == window && IsMousePosValid())
11862     {
11863         if (window->Viewport != NULL && window->Viewport->Window == window)
11864             window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None);
11865     }
11866     else
11867     {
11868         // Merge into host viewport?
11869         // We cannot test window->ViewportOwned as it set lower in the function.
11870         bool try_to_merge_into_host_viewport = (window->Viewport && window == window->Viewport->Window && g.ActiveId == 0);
11871         if (try_to_merge_into_host_viewport)
11872             UpdateTryMergeWindowIntoHostViewports(window);
11873     }
11874 
11875     // Fallback: merge in default viewport if z-order matches, otherwise create a new viewport
11876     if (window->Viewport == NULL)
11877         if (!UpdateTryMergeWindowIntoHostViewport(window, main_viewport))
11878             window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None);
11879 
11880     // Mark window as allowed to protrude outside of its viewport and into the current monitor
11881     if (!lock_viewport)
11882     {
11883         if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
11884         {
11885             // We need to take account of the possibility that mouse may become invalid.
11886             // Popups/Tooltip always set ViewportAllowPlatformMonitorExtend so GetWindowAllowedExtentRect() will return full monitor bounds.
11887             ImVec2 mouse_ref = (flags & ImGuiWindowFlags_Tooltip) ? g.IO.MousePos : g.BeginPopupStack.back().OpenMousePos;
11888             bool use_mouse_ref = (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow);
11889             bool mouse_valid = IsMousePosValid(&mouse_ref);
11890             if ((window->Appearing || (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_ChildMenu))) && (!use_mouse_ref || mouse_valid))
11891                 window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos((use_mouse_ref && mouse_valid) ? mouse_ref : NavCalcPreferredRefPos());
11892             else
11893                 window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor;
11894         }
11895         else if (window->Viewport && window != window->Viewport->Window && window->Viewport->Window && !(flags & ImGuiWindowFlags_ChildWindow))
11896         {
11897             // When called from Begin() we don't have access to a proper version of the Hidden flag yet, so we replicate this code.
11898             const bool will_be_visible = (window->DockIsActive && !window->DockTabIsVisible) ? false : true;
11899             if ((window->Flags & ImGuiWindowFlags_DockNodeHost) && window->Viewport->LastFrameActive < g.FrameCount && will_be_visible)
11900             {
11901                 // Steal/transfer ownership
11902                 IMGUI_DEBUG_LOG_VIEWPORT("Window '%s' steal Viewport %08X from Window '%s'\n", window->Name, window->Viewport->ID, window->Viewport->Window->Name);
11903                 window->Viewport->Window = window;
11904                 window->Viewport->ID = window->ID;
11905                 window->Viewport->LastNameHash = 0;
11906             }
11907             else if (!UpdateTryMergeWindowIntoHostViewports(window)) // Merge?
11908             {
11909                 // New viewport
11910                 window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing);
11911             }
11912         }
11913         else if (window->ViewportAllowPlatformMonitorExtend < 0 && (flags & ImGuiWindowFlags_ChildWindow) == 0)
11914         {
11915             // Regular (non-child, non-popup) windows by default are also allowed to protrude
11916             // Child windows are kept contained within their parent.
11917             window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor;
11918         }
11919     }
11920 
11921     // Update flags
11922     window->ViewportOwned = (window == window->Viewport->Window);
11923     window->ViewportId = window->Viewport->ID;
11924 
11925     // If the OS window has a title bar, hide our imgui title bar
11926     //if (window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration))
11927     //    window->Flags |= ImGuiWindowFlags_NoTitleBar;
11928 }
11929 
WindowSyncOwnedViewport(ImGuiWindow * window,ImGuiWindow * parent_window_in_stack)11930 void ImGui::WindowSyncOwnedViewport(ImGuiWindow* window, ImGuiWindow* parent_window_in_stack)
11931 {
11932     ImGuiContext& g = *GImGui;
11933 
11934     bool viewport_rect_changed = false;
11935 
11936     // Synchronize window --> viewport in most situations
11937     // Synchronize viewport -> window in case the platform window has been moved or resized from the OS/WM
11938     if (window->Viewport->PlatformRequestMove)
11939     {
11940         window->Pos = window->Viewport->Pos;
11941         MarkIniSettingsDirty(window);
11942     }
11943     else if (memcmp(&window->Viewport->Pos, &window->Pos, sizeof(window->Pos)) != 0)
11944     {
11945         viewport_rect_changed = true;
11946         window->Viewport->Pos = window->Pos;
11947     }
11948 
11949     if (window->Viewport->PlatformRequestResize)
11950     {
11951         window->Size = window->SizeFull = window->Viewport->Size;
11952         MarkIniSettingsDirty(window);
11953     }
11954     else if (memcmp(&window->Viewport->Size, &window->Size, sizeof(window->Size)) != 0)
11955     {
11956         viewport_rect_changed = true;
11957         window->Viewport->Size = window->Size;
11958     }
11959     window->Viewport->UpdateWorkRect();
11960 
11961     // The viewport may have changed monitor since the global update in UpdateViewportsNewFrame()
11962     // Either a SetNextWindowPos() call in the current frame or a SetWindowPos() call in the previous frame may have this effect.
11963     if (viewport_rect_changed)
11964         UpdateViewportPlatformMonitor(window->Viewport);
11965 
11966     // Update common viewport flags
11967     const ImGuiViewportFlags viewport_flags_to_clear = ImGuiViewportFlags_TopMost | ImGuiViewportFlags_NoTaskBarIcon | ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoRendererClear;
11968     ImGuiViewportFlags viewport_flags = window->Viewport->Flags & ~viewport_flags_to_clear;
11969     ImGuiWindowFlags window_flags = window->Flags;
11970     const bool is_modal = (window_flags & ImGuiWindowFlags_Modal) != 0;
11971     const bool is_short_lived_floating_window = (window_flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0;
11972     if (window_flags & ImGuiWindowFlags_Tooltip)
11973         viewport_flags |= ImGuiViewportFlags_TopMost;
11974     if ((g.IO.ConfigViewportsNoTaskBarIcon || is_short_lived_floating_window) && !is_modal)
11975         viewport_flags |= ImGuiViewportFlags_NoTaskBarIcon;
11976     if (g.IO.ConfigViewportsNoDecoration || is_short_lived_floating_window)
11977         viewport_flags |= ImGuiViewportFlags_NoDecoration;
11978 
11979     // Not correct to set modal as topmost because:
11980     // - Because other popups can be stacked above a modal (e.g. combo box in a modal)
11981     // - ImGuiViewportFlags_TopMost is currently handled different in backends: in Win32 it is "appear top most" whereas in GLFW and SDL it is "stay topmost"
11982     //if (flags & ImGuiWindowFlags_Modal)
11983     //    viewport_flags |= ImGuiViewportFlags_TopMost;
11984 
11985     // For popups and menus that may be protruding out of their parent viewport, we enable _NoFocusOnClick so that clicking on them
11986     // won't steal the OS focus away from their parent window (which may be reflected in OS the title bar decoration).
11987     // 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,
11988     // but it shouldn't be much of a problem considering those are already popups that are closed when clicking elsewhere.
11989     if (is_short_lived_floating_window && !is_modal)
11990         viewport_flags |= ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoFocusOnClick;
11991 
11992     // We can overwrite viewport flags using ImGuiWindowClass (advanced users)
11993     if (window->WindowClass.ViewportFlagsOverrideSet)
11994         viewport_flags |= window->WindowClass.ViewportFlagsOverrideSet;
11995     if (window->WindowClass.ViewportFlagsOverrideClear)
11996         viewport_flags &= ~window->WindowClass.ViewportFlagsOverrideClear;
11997 
11998     // We can also tell the backend that clearing the platform window won't be necessary,
11999     // as our window background is filling the viewport and we have disabled BgAlpha.
12000     // FIXME: Work on support for per-viewport transparency (#2766)
12001     if (!(window_flags & ImGuiWindowFlags_NoBackground))
12002         viewport_flags |= ImGuiViewportFlags_NoRendererClear;
12003 
12004     window->Viewport->Flags = viewport_flags;
12005 
12006     // Update parent viewport ID
12007     // (the !IsFallbackWindow test mimic the one done in WindowSelectViewport())
12008     if (window->WindowClass.ParentViewportId)
12009         window->Viewport->ParentViewportId = window->WindowClass.ParentViewportId;
12010     else if ((window_flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && parent_window_in_stack && (!parent_window_in_stack->IsFallbackWindow || parent_window_in_stack->WasActive))
12011         window->Viewport->ParentViewportId = parent_window_in_stack->Viewport->ID;
12012     else
12013         window->Viewport->ParentViewportId = g.IO.ConfigViewportsNoDefaultParent ? 0 : IMGUI_VIEWPORT_DEFAULT_ID;
12014 }
12015 
12016 // Called by user at the end of the main loop, after EndFrame()
12017 // This will handle the creation/update of all OS windows via function defined in the ImGuiPlatformIO api.
UpdatePlatformWindows()12018 void ImGui::UpdatePlatformWindows()
12019 {
12020     ImGuiContext& g = *GImGui;
12021     IM_ASSERT(g.FrameCountEnded == g.FrameCount && "Forgot to call Render() or EndFrame() before UpdatePlatformWindows()?");
12022     IM_ASSERT(g.FrameCountPlatformEnded < g.FrameCount);
12023     g.FrameCountPlatformEnded = g.FrameCount;
12024     if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable))
12025         return;
12026 
12027     // Create/resize/destroy platform windows to match each active viewport.
12028     // Skip the main viewport (index 0), which is always fully handled by the application!
12029     for (int i = 1; i < g.Viewports.Size; i++)
12030     {
12031         ImGuiViewportP* viewport = g.Viewports[i];
12032 
12033         // Destroy platform window if the viewport hasn't been submitted or if it is hosting a hidden window
12034         // (the implicit/fallback Debug##Default window will be registering its viewport then be disabled, causing a dummy DestroyPlatformWindow to be made each frame)
12035         bool destroy_platform_window = false;
12036         destroy_platform_window |= (viewport->LastFrameActive < g.FrameCount - 1);
12037         destroy_platform_window |= (viewport->Window && !IsWindowActiveAndVisible(viewport->Window));
12038         if (destroy_platform_window)
12039         {
12040             DestroyPlatformWindow(viewport);
12041             continue;
12042         }
12043 
12044         // New windows that appears directly in a new viewport won't always have a size on their first frame
12045         if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0 || viewport->Size.y <= 0)
12046             continue;
12047 
12048         // Create window
12049         bool is_new_platform_window = (viewport->PlatformWindowCreated == false);
12050         if (is_new_platform_window)
12051         {
12052             IMGUI_DEBUG_LOG_VIEWPORT("Create Platform Window %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a");
12053             g.PlatformIO.Platform_CreateWindow(viewport);
12054             if (g.PlatformIO.Renderer_CreateWindow != NULL)
12055                 g.PlatformIO.Renderer_CreateWindow(viewport);
12056             viewport->LastNameHash = 0;
12057             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?)
12058             viewport->LastRendererSize = viewport->Size;                                       // We don't need to call Renderer_SetWindowSize() as it is expected Renderer_CreateWindow() already did it.
12059             viewport->PlatformWindowCreated = true;
12060         }
12061 
12062         // Apply Position and Size (from ImGui to Platform/Renderer backends)
12063         if ((viewport->LastPlatformPos.x != viewport->Pos.x || viewport->LastPlatformPos.y != viewport->Pos.y) && !viewport->PlatformRequestMove)
12064             g.PlatformIO.Platform_SetWindowPos(viewport, viewport->Pos);
12065         if ((viewport->LastPlatformSize.x != viewport->Size.x || viewport->LastPlatformSize.y != viewport->Size.y) && !viewport->PlatformRequestResize)
12066             g.PlatformIO.Platform_SetWindowSize(viewport, viewport->Size);
12067         if ((viewport->LastRendererSize.x != viewport->Size.x || viewport->LastRendererSize.y != viewport->Size.y) && g.PlatformIO.Renderer_SetWindowSize)
12068             g.PlatformIO.Renderer_SetWindowSize(viewport, viewport->Size);
12069         viewport->LastPlatformPos = viewport->Pos;
12070         viewport->LastPlatformSize = viewport->LastRendererSize = viewport->Size;
12071 
12072         // Update title bar (if it changed)
12073         if (ImGuiWindow* window_for_title = GetWindowForTitleDisplay(viewport->Window))
12074         {
12075             const char* title_begin = window_for_title->Name;
12076             char* title_end = (char*)(intptr_t)FindRenderedTextEnd(title_begin);
12077             const ImGuiID title_hash = ImHashStr(title_begin, title_end - title_begin);
12078             if (viewport->LastNameHash != title_hash)
12079             {
12080                 char title_end_backup_c = *title_end;
12081                 *title_end = 0; // Cut existing buffer short instead of doing an alloc/free, no small gain.
12082                 g.PlatformIO.Platform_SetWindowTitle(viewport, title_begin);
12083                 *title_end = title_end_backup_c;
12084                 viewport->LastNameHash = title_hash;
12085             }
12086         }
12087 
12088         // Update alpha (if it changed)
12089         if (viewport->LastAlpha != viewport->Alpha && g.PlatformIO.Platform_SetWindowAlpha)
12090             g.PlatformIO.Platform_SetWindowAlpha(viewport, viewport->Alpha);
12091         viewport->LastAlpha = viewport->Alpha;
12092 
12093         // Optional, general purpose call to allow the backend to perform general book-keeping even if things haven't changed.
12094         if (g.PlatformIO.Platform_UpdateWindow)
12095             g.PlatformIO.Platform_UpdateWindow(viewport);
12096 
12097         if (is_new_platform_window)
12098         {
12099             // 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)
12100             if (g.FrameCount < 3)
12101                 viewport->Flags |= ImGuiViewportFlags_NoFocusOnAppearing;
12102 
12103             // Show window
12104             g.PlatformIO.Platform_ShowWindow(viewport);
12105 
12106             // Even without focus, we assume the window becomes front-most.
12107             // This is useful for our platform z-order heuristic when io.MouseHoveredViewport is not available.
12108             if (viewport->LastFrontMostStampCount != g.ViewportFrontMostStampCount)
12109                 viewport->LastFrontMostStampCount = ++g.ViewportFrontMostStampCount;
12110             }
12111 
12112         // Clear request flags
12113         viewport->ClearRequestFlags();
12114     }
12115 
12116     // Update our implicit z-order knowledge of platform windows, which is used when the backend cannot provide io.MouseHoveredViewport.
12117     // When setting Platform_GetWindowFocus, it is expected that the platform backend can handle calls without crashing if it doesn't have data stored.
12118     // FIXME-VIEWPORT: We should use this information to also set dear imgui-side focus, allowing us to handle os-level alt+tab.
12119     if (g.PlatformIO.Platform_GetWindowFocus != NULL)
12120     {
12121         ImGuiViewportP* focused_viewport = NULL;
12122         for (int n = 0; n < g.Viewports.Size && focused_viewport == NULL; n++)
12123         {
12124             ImGuiViewportP* viewport = g.Viewports[n];
12125             if (viewport->PlatformWindowCreated)
12126                 if (g.PlatformIO.Platform_GetWindowFocus(viewport))
12127                     focused_viewport = viewport;
12128         }
12129 
12130         // Store a tag so we can infer z-order easily from all our windows
12131         // We compare PlatformLastFocusedViewportId so newly created viewports with _NoFocusOnAppearing flag
12132         // will keep the front most stamp instead of losing it back to their parent viewport.
12133         if (focused_viewport && g.PlatformLastFocusedViewportId != focused_viewport->ID)
12134         {
12135             if (focused_viewport->LastFrontMostStampCount != g.ViewportFrontMostStampCount)
12136                 focused_viewport->LastFrontMostStampCount = ++g.ViewportFrontMostStampCount;
12137             g.PlatformLastFocusedViewportId = focused_viewport->ID;
12138         }
12139     }
12140 }
12141 
12142 // This is a default/basic function for performing the rendering/swap of multiple Platform Windows.
12143 // Custom renderers may prefer to not call this function at all, and instead iterate the publicly exposed platform data and handle rendering/sync themselves.
12144 // The Render/Swap functions stored in ImGuiPlatformIO are merely here to allow for this helper to exist, but you can do it yourself:
12145 //
12146 //    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
12147 //    for (int i = 1; i < platform_io.Viewports.Size; i++)
12148 //        if ((platform_io.Viewports[i]->Flags & ImGuiViewportFlags_Minimized) == 0)
12149 //            MyRenderFunction(platform_io.Viewports[i], my_args);
12150 //    for (int i = 1; i < platform_io.Viewports.Size; i++)
12151 //        if ((platform_io.Viewports[i]->Flags & ImGuiViewportFlags_Minimized) == 0)
12152 //            MySwapBufferFunction(platform_io.Viewports[i], my_args);
12153 //
RenderPlatformWindowsDefault(void * platform_render_arg,void * renderer_render_arg)12154 void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* renderer_render_arg)
12155 {
12156     // Skip the main viewport (index 0), which is always fully handled by the application!
12157     ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
12158     for (int i = 1; i < platform_io.Viewports.Size; i++)
12159     {
12160         ImGuiViewport* viewport = platform_io.Viewports[i];
12161         if (viewport->Flags & ImGuiViewportFlags_Minimized)
12162             continue;
12163         if (platform_io.Platform_RenderWindow) platform_io.Platform_RenderWindow(viewport, platform_render_arg);
12164         if (platform_io.Renderer_RenderWindow) platform_io.Renderer_RenderWindow(viewport, renderer_render_arg);
12165     }
12166     for (int i = 1; i < platform_io.Viewports.Size; i++)
12167     {
12168         ImGuiViewport* viewport = platform_io.Viewports[i];
12169         if (viewport->Flags & ImGuiViewportFlags_Minimized)
12170             continue;
12171         if (platform_io.Platform_SwapBuffers) platform_io.Platform_SwapBuffers(viewport, platform_render_arg);
12172         if (platform_io.Renderer_SwapBuffers) platform_io.Renderer_SwapBuffers(viewport, renderer_render_arg);
12173     }
12174 }
12175 
FindPlatformMonitorForPos(const ImVec2 & pos)12176 static int ImGui::FindPlatformMonitorForPos(const ImVec2& pos)
12177 {
12178     ImGuiContext& g = *GImGui;
12179     for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++)
12180     {
12181         const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n];
12182         if (ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize).Contains(pos))
12183             return monitor_n;
12184     }
12185     return -1;
12186 }
12187 
12188 // Search for the monitor with the largest intersection area with the given rectangle
12189 // We generally try to avoid searching loops but the monitor count should be very small here
12190 // FIXME-OPT: We could test the last monitor used for that viewport first, and early
FindPlatformMonitorForRect(const ImRect & rect)12191 static int ImGui::FindPlatformMonitorForRect(const ImRect& rect)
12192 {
12193     ImGuiContext& g = *GImGui;
12194 
12195     const int monitor_count = g.PlatformIO.Monitors.Size;
12196     if (monitor_count <= 1)
12197         return monitor_count - 1;
12198 
12199     // 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.
12200     // This is necessary for tooltips which always resize down to zero at first.
12201     const float surface_threshold = ImMax(rect.GetWidth() * rect.GetHeight() * 0.5f, 1.0f);
12202     int best_monitor_n = -1;
12203     float best_monitor_surface = 0.001f;
12204 
12205     for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size && best_monitor_surface < surface_threshold; monitor_n++)
12206     {
12207         const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n];
12208         const ImRect monitor_rect = ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize);
12209         if (monitor_rect.Contains(rect))
12210             return monitor_n;
12211         ImRect overlapping_rect = rect;
12212         overlapping_rect.ClipWithFull(monitor_rect);
12213         float overlapping_surface = overlapping_rect.GetWidth() * overlapping_rect.GetHeight();
12214         if (overlapping_surface < best_monitor_surface)
12215             continue;
12216         best_monitor_surface = overlapping_surface;
12217         best_monitor_n = monitor_n;
12218     }
12219     return best_monitor_n;
12220 }
12221 
12222 // Update monitor from viewport rectangle (we'll use this info to clamp windows and save windows lost in a removed monitor)
UpdateViewportPlatformMonitor(ImGuiViewportP * viewport)12223 static void ImGui::UpdateViewportPlatformMonitor(ImGuiViewportP* viewport)
12224 {
12225     viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetMainRect());
12226 }
12227 
12228 // Return value is always != NULL, but don't hold on it across frames.
GetViewportPlatformMonitor(ImGuiViewport * viewport_p)12229 const ImGuiPlatformMonitor* ImGui::GetViewportPlatformMonitor(ImGuiViewport* viewport_p)
12230 {
12231     ImGuiContext& g = *GImGui;
12232     ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)viewport_p;
12233     int monitor_idx = viewport->PlatformMonitor;
12234     if (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size)
12235         return &g.PlatformIO.Monitors[monitor_idx];
12236     return &g.FallbackMonitor;
12237 }
12238 
DestroyPlatformWindow(ImGuiViewportP * viewport)12239 void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport)
12240 {
12241     ImGuiContext& g = *GImGui;
12242     if (viewport->PlatformWindowCreated)
12243     {
12244         if (g.PlatformIO.Renderer_DestroyWindow)
12245             g.PlatformIO.Renderer_DestroyWindow(viewport);
12246         if (g.PlatformIO.Platform_DestroyWindow)
12247             g.PlatformIO.Platform_DestroyWindow(viewport);
12248         IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL);
12249 
12250         // Don't clear PlatformWindowCreated for the main viewport, as we initially set that up to true in Initialize()
12251         // The righter way may be to leave it to the backend to set this flag all-together, and made the flag public.
12252         if (viewport->ID != IMGUI_VIEWPORT_DEFAULT_ID)
12253             viewport->PlatformWindowCreated = false;
12254     }
12255     else
12256     {
12257         IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL);
12258     }
12259     viewport->RendererUserData = viewport->PlatformUserData = viewport->PlatformHandle = NULL;
12260     viewport->ClearRequestFlags();
12261 }
12262 
DestroyPlatformWindows()12263 void ImGui::DestroyPlatformWindows()
12264 {
12265     // We call the destroy window on every viewport (including the main viewport, index 0) to give a chance to the backend
12266     // to clear any data they may have stored in e.g. PlatformUserData, RendererUserData.
12267     // It is convenient for the platform backend code to store something in the main viewport, in order for e.g. the mouse handling
12268     // code to operator a consistent manner.
12269     // It is expected that the backend can handle calls to Renderer_DestroyWindow/Platform_DestroyWindow without
12270     // crashing if it doesn't have data stored.
12271     ImGuiContext& g = *GImGui;
12272     for (int i = 0; i < g.Viewports.Size; i++)
12273         DestroyPlatformWindow(g.Viewports[i]);
12274 }
12275 
12276 
12277 //-----------------------------------------------------------------------------
12278 // [SECTION] DOCKING
12279 //-----------------------------------------------------------------------------
12280 // Docking: Internal Types
12281 // Docking: Forward Declarations
12282 // Docking: ImGuiDockContext
12283 // Docking: ImGuiDockContext Docking/Undocking functions
12284 // Docking: ImGuiDockNode
12285 // Docking: ImGuiDockNode Tree manipulation functions
12286 // Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport)
12287 // Docking: Builder Functions
12288 // Docking: Begin/End Support Functions (called from Begin/End)
12289 // Docking: Settings
12290 //-----------------------------------------------------------------------------
12291 
12292 //-----------------------------------------------------------------------------
12293 // Typical Docking call flow: (root level is generally public API):
12294 //-----------------------------------------------------------------------------
12295 // - NewFrame()                               new dear imgui frame
12296 //    | DockContextNewFrameUpdateUndocking()  - process queued undocking requests
12297 //    | - DockContextProcessUndockWindow()    - process one window undocking request
12298 //    | - DockContextProcessUndockNode()      - process one whole node undocking request
12299 //    | DockContextNewFrameUpdateUndocking()  - process queue docking requests, create floating dock nodes
12300 //    | - update g.HoveredDockNode            - [debug] update node hovered by mouse
12301 //    | - DockContextProcessDock()            - process one docking request
12302 //    | - DockNodeUpdate()
12303 //    |   - DockNodeUpdateForRootNode()
12304 //    |     - DockNodeUpdateVisibleFlagAndInactiveChilds()
12305 //    |     - DockNodeFindInfo()
12306 //    |   - destroy unused node or tab bar
12307 //    |   - create dock node host window
12308 //    |      - Begin() etc.
12309 //    |   - DockNodeStartMouseMovingWindow()
12310 //    |   - DockNodeTreeUpdatePosSize()
12311 //    |   - DockNodeTreeUpdateSplitter()
12312 //    |   - draw node background
12313 //    |   - DockNodeUpdateTabBar()            - create/update tab bar for a docking node
12314 //    |     - DockNodeAddTabBar()
12315 //    |     - DockNodeUpdateWindowMenu()
12316 //    |     - DockNodeCalcTabBarLayout()
12317 //    |     - BeginTabBarEx()
12318 //    |     - TabItemEx() calls
12319 //    |     - EndTabBar()
12320 //    |   - BeginDockableDragDropTarget()
12321 //    |      - DockNodeUpdate()               - recurse into child nodes...
12322 //-----------------------------------------------------------------------------
12323 // - DockSpace()                              user submit a dockspace into a window
12324 //    | Begin(Child)                          - create a child window
12325 //    | DockNodeUpdate()                      - call main dock node update function
12326 //    | End(Child)
12327 //    | ItemSize()
12328 //-----------------------------------------------------------------------------
12329 // - Begin()
12330 //    | BeginDocked()
12331 //    | BeginDockableDragDropSource()
12332 //    | BeginDockableDragDropTarget()
12333 //    | - DockNodePreviewDockRender()
12334 //-----------------------------------------------------------------------------
12335 
12336 //-----------------------------------------------------------------------------
12337 // Docking: Internal Types
12338 //-----------------------------------------------------------------------------
12339 // - ImGuiDockRequestType
12340 // - ImGuiDockRequest
12341 // - ImGuiDockPreviewData
12342 // - ImGuiDockNodeSettings
12343 // - ImGuiDockContext
12344 //-----------------------------------------------------------------------------
12345 
12346 enum ImGuiDockRequestType
12347 {
12348     ImGuiDockRequestType_None = 0,
12349     ImGuiDockRequestType_Dock,
12350     ImGuiDockRequestType_Undock,
12351     ImGuiDockRequestType_Split                  // Split is the same as Dock but without a DockPayload
12352 };
12353 
12354 struct ImGuiDockRequest
12355 {
12356     ImGuiDockRequestType    Type;
12357     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)
12358     ImGuiDockNode*          DockTargetNode;     // Destination/Target Node to dock into
12359     ImGuiWindow*            DockPayload;        // Source/Payload window to dock (may be a loose window or a DockNode), [Optional]
12360     ImGuiDir                DockSplitDir;
12361     float                   DockSplitRatio;
12362     bool                    DockSplitOuter;
12363     ImGuiWindow*            UndockTargetWindow;
12364     ImGuiDockNode*          UndockTargetNode;
12365 
ImGuiDockRequestImGuiDockRequest12366     ImGuiDockRequest()
12367     {
12368         Type = ImGuiDockRequestType_None;
12369         DockTargetWindow = DockPayload = UndockTargetWindow = NULL;
12370         DockTargetNode = UndockTargetNode = NULL;
12371         DockSplitDir = ImGuiDir_None;
12372         DockSplitRatio = 0.5f;
12373         DockSplitOuter = false;
12374     }
12375 };
12376 
12377 struct ImGuiDockPreviewData
12378 {
12379     ImGuiDockNode   FutureNode;
12380     bool            IsDropAllowed;
12381     bool            IsCenterAvailable;
12382     bool            IsSidesAvailable;           // Hold your breath, grammar freaks..
12383     bool            IsSplitDirExplicit;         // Set when hovered the drop rect (vs. implicit SplitDir==None when hovered the window)
12384     ImGuiDockNode*  SplitNode;
12385     ImGuiDir        SplitDir;
12386     float           SplitRatio;
12387     ImRect          DropRectsDraw[ImGuiDir_COUNT + 1];  // May be slightly different from hit-testing drop rects used in DockNodeCalcDropRects()
12388 
ImGuiDockPreviewDataImGuiDockPreviewData12389     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); }
12390 };
12391 
12392 // Persistent Settings data, stored contiguously in SettingsNodes (sizeof() ~32 bytes)
12393 struct ImGuiDockNodeSettings
12394 {
12395     ImGuiID             ID;
12396     ImGuiID             ParentNodeId;
12397     ImGuiID             ParentWindowId;
12398     ImGuiID             SelectedTabId;
12399     signed char         SplitAxis;
12400     char                Depth;
12401     ImGuiDockNodeFlags  Flags;                  // NB: We save individual flags one by one in ascii format (ImGuiDockNodeFlags_SavedFlagsMask_)
12402     ImVec2ih            Pos;
12403     ImVec2ih            Size;
12404     ImVec2ih            SizeRef;
ImGuiDockNodeSettingsImGuiDockNodeSettings12405     ImGuiDockNodeSettings() { memset(this, 0, sizeof(*this)); SplitAxis = ImGuiAxis_None; }
12406 };
12407 
12408 //-----------------------------------------------------------------------------
12409 // Docking: Forward Declarations
12410 //-----------------------------------------------------------------------------
12411 
12412 namespace ImGui
12413 {
12414     // ImGuiDockContext
12415     static ImGuiDockNode*   DockContextAddNode(ImGuiContext* ctx, ImGuiID id);
12416     static void             DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node);
12417     static void             DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node);
12418     static void             DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req);
12419     static void             DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window, bool clear_persistent_docking_ref = true);
12420     static void             DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node);
12421     static void             DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx);
12422     static ImGuiDockNode*   DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id);
12423     static ImGuiDockNode*   DockContextBindNodeToWindow(ImGuiContext* ctx, ImGuiWindow* window);
12424     static void             DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count);
12425     static void             DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id);                            // Use root_id==0 to add all
12426 
12427     // ImGuiDockNode
12428     static void             DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, bool add_to_tab_bar);
12429     static void             DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node);
12430     static void             DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode* src_node);
12431     static ImGuiWindow*     DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id);
12432     static void             DockNodeApplyPosSizeToWindows(ImGuiDockNode* node);
12433     static void             DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id);
12434     static void             DockNodeHideHostWindow(ImGuiDockNode* node);
12435     static void             DockNodeUpdate(ImGuiDockNode* node);
12436     static void             DockNodeUpdateForRootNode(ImGuiDockNode* node);
12437     static void             DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node);
12438     static void             DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window);
12439     static void             DockNodeAddTabBar(ImGuiDockNode* node);
12440     static void             DockNodeRemoveTabBar(ImGuiDockNode* node);
12441     static ImGuiID          DockNodeUpdateWindowMenu(ImGuiDockNode* node, ImGuiTabBar* tab_bar);
12442     static void             DockNodeUpdateVisibleFlag(ImGuiDockNode* node);
12443     static void             DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window);
12444     static bool             DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* payload_window);
12445     static void             DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockPreviewData* preview_data, bool is_explicit_target, bool is_outer_docking);
12446     static void             DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, const ImGuiDockPreviewData* preview_data);
12447     static void             DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos, ImVec2* out_close_button_pos);
12448     static void             DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired);
12449     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)12450     static const char*      DockNodeGetHostWindowTitle(ImGuiDockNode* node, char* buf, int buf_size) { ImFormatString(buf, buf_size, "##DockNode_%02X", node->ID); return buf; }
12451     static int              DockNodeGetTabOrder(ImGuiWindow* window);
12452 
12453     // ImGuiDockNode tree manipulations
12454     static void             DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_first_child, float split_ratio, ImGuiDockNode* new_node);
12455     static void             DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child);
12456     static void             DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, bool only_write_to_marked_nodes = false);
12457     static void             DockNodeTreeUpdateSplitter(ImGuiDockNode* node);
12458     static ImGuiDockNode*   DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos);
12459     static ImGuiDockNode*   DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node);
12460 
12461     // Settings
12462     static void             DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id);
12463     static void             DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ids_count);
12464     static ImGuiDockNodeSettings*   DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID node_id);
12465     static void             DockSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
12466     static void             DockSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);
12467     static void*            DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
12468     static void             DockSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
12469     static void             DockSettingsHandler_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf);
12470 }
12471 
12472 //-----------------------------------------------------------------------------
12473 // Docking: ImGuiDockContext
12474 //-----------------------------------------------------------------------------
12475 // The lifetime model is different from the one of regular windows: we always create a ImGuiDockNode for each ImGuiDockNodeSettings,
12476 // 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.
12477 // At boot time only, we run a simple GC to remove nodes that have no references.
12478 // Because dock node settings (which are small, contiguous structures) are always mirrored by their corresponding dock nodes (more complete structures),
12479 // we can also very easily recreate the nodes from scratch given the settings data (this is what DockContextRebuild() does).
12480 // This is convenient as docking reconfiguration can be implemented by mostly poking at the simpler settings data.
12481 //-----------------------------------------------------------------------------
12482 // - DockContextInitialize()
12483 // - DockContextShutdown()
12484 // - DockContextClearNodes()
12485 // - DockContextRebuildNodes()
12486 // - DockContextNewFrameUpdateUndocking()
12487 // - DockContextNewFrameUpdateDocking()
12488 // - DockContextFindNodeByID()
12489 // - DockContextBindNodeToWindow()
12490 // - DockContextGenNodeID()
12491 // - DockContextAddNode()
12492 // - DockContextRemoveNode()
12493 // - ImGuiDockContextPruneNodeData
12494 // - DockContextPruneUnusedSettingsNodes()
12495 // - DockContextBuildNodesFromSettings()
12496 // - DockContextBuildAddWindowsToNodes()
12497 //-----------------------------------------------------------------------------
12498 
DockContextInitialize(ImGuiContext * ctx)12499 void ImGui::DockContextInitialize(ImGuiContext* ctx)
12500 {
12501     ImGuiContext& g = *ctx;
12502 
12503     // Add .ini handle for persistent docking data
12504     ImGuiSettingsHandler ini_handler;
12505     ini_handler.TypeName = "Docking";
12506     ini_handler.TypeHash = ImHashStr("Docking");
12507     ini_handler.ClearAllFn = DockSettingsHandler_ClearAll;
12508     ini_handler.ReadInitFn = DockSettingsHandler_ClearAll; // Also clear on read
12509     ini_handler.ReadOpenFn = DockSettingsHandler_ReadOpen;
12510     ini_handler.ReadLineFn = DockSettingsHandler_ReadLine;
12511     ini_handler.ApplyAllFn = DockSettingsHandler_ApplyAll;
12512     ini_handler.WriteAllFn = DockSettingsHandler_WriteAll;
12513     g.SettingsHandlers.push_back(ini_handler);
12514 }
12515 
DockContextShutdown(ImGuiContext * ctx)12516 void ImGui::DockContextShutdown(ImGuiContext* ctx)
12517 {
12518     ImGuiDockContext* dc  = &ctx->DockContext;
12519     for (int n = 0; n < dc->Nodes.Data.Size; n++)
12520         if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
12521             IM_DELETE(node);
12522 }
12523 
DockContextClearNodes(ImGuiContext * ctx,ImGuiID root_id,bool clear_settings_refs)12524 void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_settings_refs)
12525 {
12526     IM_UNUSED(ctx);
12527     IM_ASSERT(ctx == GImGui);
12528     DockBuilderRemoveNodeDockedWindows(root_id, clear_settings_refs);
12529     DockBuilderRemoveNodeChildNodes(root_id);
12530 }
12531 
12532 // [DEBUG] This function also acts as a defacto test to make sure we can rebuild from scratch without a glitch
12533 // (Different from DockSettingsHandler_ClearAll() + DockSettingsHandler_ApplyAll() because this reuses current settings!)
DockContextRebuildNodes(ImGuiContext * ctx)12534 void ImGui::DockContextRebuildNodes(ImGuiContext* ctx)
12535 {
12536     IMGUI_DEBUG_LOG_DOCKING("DockContextRebuild()\n");
12537     ImGuiDockContext* dc  = &ctx->DockContext;
12538     SaveIniSettingsToMemory();
12539     ImGuiID root_id = 0; // Rebuild all
12540     DockContextClearNodes(ctx, root_id, false);
12541     DockContextBuildNodesFromSettings(ctx, dc->NodesSettings.Data, dc->NodesSettings.Size);
12542     DockContextBuildAddWindowsToNodes(ctx, root_id);
12543 }
12544 
12545 // Docking context update function, called by NewFrame()
DockContextNewFrameUpdateUndocking(ImGuiContext * ctx)12546 void ImGui::DockContextNewFrameUpdateUndocking(ImGuiContext* ctx)
12547 {
12548     ImGuiContext& g = *ctx;
12549     ImGuiDockContext* dc  = &ctx->DockContext;
12550     if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
12551     {
12552         if (dc->Nodes.Data.Size > 0 || dc->Requests.Size > 0)
12553             DockContextClearNodes(ctx, 0, true);
12554         return;
12555     }
12556 
12557     // Setting NoSplit at runtime merges all nodes
12558     if (g.IO.ConfigDockingNoSplit)
12559         for (int n = 0; n < dc->Nodes.Data.Size; n++)
12560             if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
12561                 if (node->IsRootNode() && node->IsSplitNode())
12562                 {
12563                     DockBuilderRemoveNodeChildNodes(node->ID);
12564                     //dc->WantFullRebuild = true;
12565                 }
12566 
12567     // Process full rebuild
12568 #if 0
12569     if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C)))
12570         dc->WantFullRebuild = true;
12571 #endif
12572     if (dc->WantFullRebuild)
12573     {
12574         DockContextRebuildNodes(ctx);
12575         dc->WantFullRebuild = false;
12576     }
12577 
12578     // Process Undocking requests (we need to process them _before_ the UpdateMouseMovingWindowNewFrame call in NewFrame)
12579     for (int n = 0; n < dc->Requests.Size; n++)
12580     {
12581         ImGuiDockRequest* req = &dc->Requests[n];
12582         if (req->Type == ImGuiDockRequestType_Undock && req->UndockTargetWindow)
12583             DockContextProcessUndockWindow(ctx, req->UndockTargetWindow);
12584         else if (req->Type == ImGuiDockRequestType_Undock && req->UndockTargetNode)
12585             DockContextProcessUndockNode(ctx, req->UndockTargetNode);
12586     }
12587 }
12588 
12589 // Docking context update function, called by NewFrame()
DockContextNewFrameUpdateDocking(ImGuiContext * ctx)12590 void ImGui::DockContextNewFrameUpdateDocking(ImGuiContext* ctx)
12591 {
12592     ImGuiContext& g = *ctx;
12593     ImGuiDockContext* dc  = &ctx->DockContext;
12594     if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
12595         return;
12596 
12597     // [DEBUG] Store hovered dock node.
12598     // We could in theory use DockNodeTreeFindVisibleNodeByPos() on the root host dock node, but using ->DockNode is a good shortcut.
12599     // Note this is mostly a debug thing and isn't actually used for docking target, because docking involve more detailed filtering.
12600     g.HoveredDockNode = NULL;
12601     if (ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow)
12602     {
12603         if (hovered_window->DockNodeAsHost)
12604             g.HoveredDockNode = DockNodeTreeFindVisibleNodeByPos(hovered_window->DockNodeAsHost, g.IO.MousePos);
12605         else if (hovered_window->RootWindow->DockNode)
12606             g.HoveredDockNode = hovered_window->RootWindow->DockNode;
12607     }
12608 
12609     // Process Docking requests
12610     for (int n = 0; n < dc->Requests.Size; n++)
12611         if (dc->Requests[n].Type == ImGuiDockRequestType_Dock)
12612             DockContextProcessDock(ctx, &dc->Requests[n]);
12613     dc->Requests.resize(0);
12614 
12615     // Create windows for each automatic docking nodes
12616     // 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)
12617     for (int n = 0; n < dc->Nodes.Data.Size; n++)
12618         if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
12619             if (node->IsFloatingNode())
12620                 DockNodeUpdate(node);
12621 }
12622 
DockContextFindNodeByID(ImGuiContext * ctx,ImGuiID id)12623 static ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id)
12624 {
12625     return (ImGuiDockNode*)ctx->DockContext.Nodes.GetVoidPtr(id);
12626 }
12627 
DockContextGenNodeID(ImGuiContext * ctx)12628 ImGuiID ImGui::DockContextGenNodeID(ImGuiContext* ctx)
12629 {
12630     // Generate an ID for new node (the exact ID value doesn't matter as long as it is not already used)
12631     // FIXME-OPT FIXME-DOCK: This is suboptimal, even if the node count is small enough not to be a worry.0
12632     // We should poke in ctx->Nodes to find a suitable ID faster. Even more so trivial that ctx->Nodes lookup is already sorted.
12633     ImGuiID id = 0x0001;
12634     while (DockContextFindNodeByID(ctx, id) != NULL)
12635         id++;
12636     return id;
12637 }
12638 
DockContextAddNode(ImGuiContext * ctx,ImGuiID id)12639 static ImGuiDockNode* ImGui::DockContextAddNode(ImGuiContext* ctx, ImGuiID id)
12640 {
12641     // 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.
12642     if (id == 0)
12643         id = DockContextGenNodeID(ctx);
12644     else
12645         IM_ASSERT(DockContextFindNodeByID(ctx, id) == NULL);
12646 
12647     // We don't set node->LastFrameAlive on construction. Nodes are always created at all time to reflect .ini settings!
12648     IMGUI_DEBUG_LOG_DOCKING("DockContextAddNode 0x%08X\n", id);
12649     ImGuiDockNode* node = IM_NEW(ImGuiDockNode)(id);
12650     ctx->DockContext.Nodes.SetVoidPtr(node->ID, node);
12651     return node;
12652 }
12653 
DockContextRemoveNode(ImGuiContext * ctx,ImGuiDockNode * node,bool merge_sibling_into_parent_node)12654 static void ImGui::DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node)
12655 {
12656     ImGuiContext& g = *ctx;
12657     ImGuiDockContext* dc  = &ctx->DockContext;
12658 
12659     IMGUI_DEBUG_LOG_DOCKING("DockContextRemoveNode 0x%08X\n", node->ID);
12660     IM_ASSERT(DockContextFindNodeByID(ctx, node->ID) == node);
12661     IM_ASSERT(node->ChildNodes[0] == NULL && node->ChildNodes[1] == NULL);
12662     IM_ASSERT(node->Windows.Size == 0);
12663 
12664     if (node->HostWindow)
12665         node->HostWindow->DockNodeAsHost = NULL;
12666 
12667     ImGuiDockNode* parent_node = node->ParentNode;
12668     const bool merge = (merge_sibling_into_parent_node && parent_node != NULL);
12669     if (merge)
12670     {
12671         IM_ASSERT(parent_node->ChildNodes[0] == node || parent_node->ChildNodes[1] == node);
12672         ImGuiDockNode* sibling_node = (parent_node->ChildNodes[0] == node ? parent_node->ChildNodes[1] : parent_node->ChildNodes[0]);
12673         DockNodeTreeMerge(&g, parent_node, sibling_node);
12674     }
12675     else
12676     {
12677         for (int n = 0; parent_node && n < IM_ARRAYSIZE(parent_node->ChildNodes); n++)
12678             if (parent_node->ChildNodes[n] == node)
12679                 node->ParentNode->ChildNodes[n] = NULL;
12680         dc->Nodes.SetVoidPtr(node->ID, NULL);
12681         IM_DELETE(node);
12682     }
12683 }
12684 
DockNodeComparerDepthMostFirst(const void * lhs,const void * rhs)12685 static int IMGUI_CDECL DockNodeComparerDepthMostFirst(const void* lhs, const void* rhs)
12686 {
12687     const ImGuiDockNode* a = *(const ImGuiDockNode* const*)lhs;
12688     const ImGuiDockNode* b = *(const ImGuiDockNode* const*)rhs;
12689     return ImGui::DockNodeGetDepth(b) - ImGui::DockNodeGetDepth(a);
12690 }
12691 
12692 // Pre C++0x doesn't allow us to use a function-local type (without linkage) as template parameter, so we moved this here.
12693 struct ImGuiDockContextPruneNodeData
12694 {
12695     int         CountWindows, CountChildWindows, CountChildNodes;
12696     ImGuiID     RootId;
ImGuiDockContextPruneNodeDataImGuiDockContextPruneNodeData12697     ImGuiDockContextPruneNodeData() { CountWindows = CountChildWindows = CountChildNodes = 0; RootId = 0; }
12698 };
12699 
12700 // Garbage collect unused nodes (run once at init time)
DockContextPruneUnusedSettingsNodes(ImGuiContext * ctx)12701 static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx)
12702 {
12703     ImGuiContext& g = *ctx;
12704     ImGuiDockContext* dc  = &ctx->DockContext;
12705     IM_ASSERT(g.Windows.Size == 0);
12706 
12707     ImPool<ImGuiDockContextPruneNodeData> pool;
12708     pool.Reserve(dc->NodesSettings.Size);
12709 
12710     // Count child nodes and compute RootID
12711     for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++)
12712     {
12713         ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n];
12714         ImGuiDockContextPruneNodeData* parent_data = settings->ParentNodeId ? pool.GetByKey(settings->ParentNodeId) : 0;
12715         pool.GetOrAddByKey(settings->ID)->RootId = parent_data ? parent_data->RootId : settings->ID;
12716         if (settings->ParentNodeId)
12717             pool.GetOrAddByKey(settings->ParentNodeId)->CountChildNodes++;
12718     }
12719 
12720     // Count reference to dock ids from dockspaces
12721     // We track the 'auto-DockNode <- manual-Window <- manual-DockSpace' in order to avoid 'auto-DockNode' being ditched by DockContextPruneUnusedSettingsNodes()
12722     for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++)
12723     {
12724         ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n];
12725         if (settings->ParentWindowId != 0)
12726             if (ImGuiWindowSettings* window_settings = FindWindowSettings(settings->ParentWindowId))
12727                 if (window_settings->DockId)
12728                     if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(window_settings->DockId))
12729                         data->CountChildNodes++;
12730     }
12731 
12732     // Count reference to dock ids from window settings
12733     // We guard against the possibility of an invalid .ini file (RootID may point to a missing node)
12734     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
12735         if (ImGuiID dock_id = settings->DockId)
12736             if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(dock_id))
12737             {
12738                 data->CountWindows++;
12739                 if (ImGuiDockContextPruneNodeData* data_root = (data->RootId == dock_id) ? data : pool.GetByKey(data->RootId))
12740                     data_root->CountChildWindows++;
12741             }
12742 
12743     // Prune
12744     for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++)
12745     {
12746         ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n];
12747         ImGuiDockContextPruneNodeData* data = pool.GetByKey(settings->ID);
12748         if (data->CountWindows > 1)
12749             continue;
12750         ImGuiDockContextPruneNodeData* data_root = (data->RootId == settings->ID) ? data : pool.GetByKey(data->RootId);
12751 
12752         bool remove = false;
12753         remove |= (data->CountWindows == 1 && settings->ParentNodeId == 0 && data->CountChildNodes == 0 && !(settings->Flags & ImGuiDockNodeFlags_CentralNode));  // Floating root node with only 1 window
12754         remove |= (data->CountWindows == 0 && settings->ParentNodeId == 0 && data->CountChildNodes == 0); // Leaf nodes with 0 window
12755         remove |= (data_root->CountChildWindows == 0);
12756         if (remove)
12757         {
12758             IMGUI_DEBUG_LOG_DOCKING("DockContextPruneUnusedSettingsNodes: Prune 0x%08X\n", settings->ID);
12759             DockSettingsRemoveNodeReferences(&settings->ID, 1);
12760             settings->ID = 0;
12761         }
12762     }
12763 }
12764 
DockContextBuildNodesFromSettings(ImGuiContext * ctx,ImGuiDockNodeSettings * node_settings_array,int node_settings_count)12765 static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count)
12766 {
12767     // Build nodes
12768     for (int node_n = 0; node_n < node_settings_count; node_n++)
12769     {
12770         ImGuiDockNodeSettings* settings = &node_settings_array[node_n];
12771         if (settings->ID == 0)
12772             continue;
12773         ImGuiDockNode* node = DockContextAddNode(ctx, settings->ID);
12774         node->ParentNode = settings->ParentNodeId ? DockContextFindNodeByID(ctx, settings->ParentNodeId) : NULL;
12775         node->Pos = ImVec2(settings->Pos.x, settings->Pos.y);
12776         node->Size = ImVec2(settings->Size.x, settings->Size.y);
12777         node->SizeRef = ImVec2(settings->SizeRef.x, settings->SizeRef.y);
12778         node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_DockNode;
12779         if (node->ParentNode && node->ParentNode->ChildNodes[0] == NULL)
12780             node->ParentNode->ChildNodes[0] = node;
12781         else if (node->ParentNode && node->ParentNode->ChildNodes[1] == NULL)
12782             node->ParentNode->ChildNodes[1] = node;
12783         node->SelectedTabId = settings->SelectedTabId;
12784         node->SplitAxis = (ImGuiAxis)settings->SplitAxis;
12785         node->SetLocalFlags(settings->Flags & ImGuiDockNodeFlags_SavedFlagsMask_);
12786 
12787         // Bind host window immediately if it already exist (in case of a rebuild)
12788         // This is useful as the RootWindowForTitleBarHighlight links necessary to highlight the currently focused node requires node->HostWindow to be set.
12789         char host_window_title[20];
12790         ImGuiDockNode* root_node = DockNodeGetRootNode(node);
12791         node->HostWindow = FindWindowByName(DockNodeGetHostWindowTitle(root_node, host_window_title, IM_ARRAYSIZE(host_window_title)));
12792     }
12793 }
12794 
DockContextBuildAddWindowsToNodes(ImGuiContext * ctx,ImGuiID root_id)12795 void ImGui::DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id)
12796 {
12797     // Rebind all windows to nodes (they can also lazily rebind but we'll have a visible glitch during the first frame)
12798     ImGuiContext& g = *ctx;
12799     for (int n = 0; n < g.Windows.Size; n++)
12800     {
12801         ImGuiWindow* window = g.Windows[n];
12802         if (window->DockId == 0 || window->LastFrameActive < g.FrameCount - 1)
12803             continue;
12804         if (window->DockNode != NULL)
12805             continue;
12806 
12807         ImGuiDockNode* node = DockContextFindNodeByID(ctx, window->DockId);
12808         IM_ASSERT(node != NULL);   // This should have been called after DockContextBuildNodesFromSettings()
12809         if (root_id == 0 || DockNodeGetRootNode(node)->ID == root_id)
12810             DockNodeAddWindow(node, window, true);
12811     }
12812 }
12813 
12814 //-----------------------------------------------------------------------------
12815 // Docking: ImGuiDockContext Docking/Undocking functions
12816 //-----------------------------------------------------------------------------
12817 // - DockContextQueueDock()
12818 // - DockContextQueueUndockWindow()
12819 // - DockContextQueueUndockNode()
12820 // - DockContextQueueNotifyRemovedNode()
12821 // - DockContextProcessDock()
12822 // - DockContextProcessUndockWindow()
12823 // - DockContextProcessUndockNode()
12824 // - DockContextCalcDropPosForDocking()
12825 //-----------------------------------------------------------------------------
12826 
DockContextQueueDock(ImGuiContext * ctx,ImGuiWindow * target,ImGuiDockNode * target_node,ImGuiWindow * payload,ImGuiDir split_dir,float split_ratio,bool split_outer)12827 void ImGui::DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer)
12828 {
12829     IM_ASSERT(target != payload);
12830     ImGuiDockRequest req;
12831     req.Type = ImGuiDockRequestType_Dock;
12832     req.DockTargetWindow = target;
12833     req.DockTargetNode = target_node;
12834     req.DockPayload = payload;
12835     req.DockSplitDir = split_dir;
12836     req.DockSplitRatio = split_ratio;
12837     req.DockSplitOuter = split_outer;
12838     ctx->DockContext.Requests.push_back(req);
12839 }
12840 
DockContextQueueUndockWindow(ImGuiContext * ctx,ImGuiWindow * window)12841 void ImGui::DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window)
12842 {
12843     ImGuiDockRequest req;
12844     req.Type = ImGuiDockRequestType_Undock;
12845     req.UndockTargetWindow = window;
12846     ctx->DockContext.Requests.push_back(req);
12847 }
12848 
DockContextQueueUndockNode(ImGuiContext * ctx,ImGuiDockNode * node)12849 void ImGui::DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node)
12850 {
12851     ImGuiDockRequest req;
12852     req.Type = ImGuiDockRequestType_Undock;
12853     req.UndockTargetNode = node;
12854     ctx->DockContext.Requests.push_back(req);
12855 }
12856 
DockContextQueueNotifyRemovedNode(ImGuiContext * ctx,ImGuiDockNode * node)12857 void ImGui::DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node)
12858 {
12859     ImGuiDockContext* dc  = &ctx->DockContext;
12860     for (int n = 0; n < dc->Requests.Size; n++)
12861         if (dc->Requests[n].DockTargetNode == node)
12862             dc->Requests[n].Type = ImGuiDockRequestType_None;
12863 }
12864 
DockContextProcessDock(ImGuiContext * ctx,ImGuiDockRequest * req)12865 void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req)
12866 {
12867     IM_ASSERT((req->Type == ImGuiDockRequestType_Dock && req->DockPayload != NULL) || (req->Type == ImGuiDockRequestType_Split && req->DockPayload == NULL));
12868     IM_ASSERT(req->DockTargetWindow != NULL || req->DockTargetNode != NULL);
12869 
12870     ImGuiContext& g = *ctx;
12871     IM_UNUSED(g);
12872 
12873     ImGuiWindow* payload_window = req->DockPayload;     // Optional
12874     ImGuiWindow* target_window = req->DockTargetWindow;
12875     ImGuiDockNode* node = req->DockTargetNode;
12876     if (payload_window)
12877         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);
12878     else
12879         IMGUI_DEBUG_LOG_DOCKING("DockContextProcessDock node 0x%08X, split_dir %d\n", node ? node->ID : 0, req->DockSplitDir);
12880 
12881     // Decide which Tab will be selected at the end of the operation
12882     ImGuiID next_selected_id = 0;
12883     ImGuiDockNode* payload_node = NULL;
12884     if (payload_window)
12885     {
12886         payload_node = payload_window->DockNodeAsHost;
12887         payload_window->DockNodeAsHost = NULL; // Important to clear this as the node will have its life as a child which might be merged/deleted later.
12888         if (payload_node && payload_node->IsLeafNode())
12889             next_selected_id = payload_node->TabBar->NextSelectedTabId ? payload_node->TabBar->NextSelectedTabId : payload_node->TabBar->SelectedTabId;
12890         if (payload_node == NULL)
12891             next_selected_id = payload_window->ID;
12892     }
12893 
12894     // FIXME-DOCK: When we are trying to dock an existing single-window node into a loose window, transfer Node ID as well
12895     // When processing an interactive split, usually LastFrameAlive will be < g.FrameCount. But DockBuilder operations can make it ==.
12896     if (node)
12897         IM_ASSERT(node->LastFrameAlive <= g.FrameCount);
12898     if (node && target_window && node == target_window->DockNodeAsHost)
12899         IM_ASSERT(node->Windows.Size > 0 || node->IsSplitNode() || node->IsCentralNode());
12900 
12901     // Create new node and add existing window to it
12902     if (node == NULL)
12903     {
12904         node = DockContextAddNode(ctx, 0);
12905         node->Pos = target_window->Pos;
12906         node->Size = target_window->Size;
12907         if (target_window->DockNodeAsHost == NULL)
12908         {
12909             DockNodeAddWindow(node, target_window, true);
12910             node->TabBar->Tabs[0].Flags &= ~ImGuiTabItemFlags_Unsorted;
12911             target_window->DockIsActive = true;
12912         }
12913     }
12914 
12915     ImGuiDir split_dir = req->DockSplitDir;
12916     if (split_dir != ImGuiDir_None)
12917     {
12918         // Split into two, one side will be our payload node unless we are dropping a loose window
12919         const ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
12920         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
12921         const float split_ratio = req->DockSplitRatio;
12922         DockNodeTreeSplit(ctx, node, split_axis, split_inheritor_child_idx, split_ratio, payload_node);  // payload_node may be NULL here!
12923         ImGuiDockNode* new_node = node->ChildNodes[split_inheritor_child_idx ^ 1];
12924         new_node->HostWindow = node->HostWindow;
12925         node = new_node;
12926     }
12927     node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_HiddenTabBar);
12928 
12929     if (node != payload_node)
12930     {
12931         // 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!)
12932         if (node->Windows.Size > 0 && node->TabBar == NULL)
12933         {
12934             DockNodeAddTabBar(node);
12935             for (int n = 0; n < node->Windows.Size; n++)
12936                 TabBarAddTab(node->TabBar, ImGuiTabItemFlags_None, node->Windows[n]);
12937         }
12938 
12939         if (payload_node != NULL)
12940         {
12941             // Transfer full payload node (with 1+ child windows or child nodes)
12942             if (payload_node->IsSplitNode())
12943             {
12944                 if (node->Windows.Size > 0)
12945                 {
12946                     // 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.
12947                     // In this situation, we move the windows of the target node into the currently visible node of the payload.
12948                     // This allows us to preserve some of the underlying dock tree settings nicely.
12949                     IM_ASSERT(payload_node->OnlyNodeWithWindows != NULL); // The docking should have been blocked by DockNodePreviewDockSetup() early on and never submitted.
12950                     ImGuiDockNode* visible_node = payload_node->OnlyNodeWithWindows;
12951                     if (visible_node->TabBar)
12952                         IM_ASSERT(visible_node->TabBar->Tabs.Size > 0);
12953                     DockNodeMoveWindows(node, visible_node);
12954                     DockNodeMoveWindows(visible_node, node);
12955                     DockSettingsRenameNodeReferences(node->ID, visible_node->ID);
12956                 }
12957                 if (node->IsCentralNode())
12958                 {
12959                     // Central node property needs to be moved to a leaf node, pick the last focused one.
12960                     // FIXME-DOCK: If we had to transfer other flags here, what would the policy be?
12961                     ImGuiDockNode* last_focused_node = DockContextFindNodeByID(ctx, payload_node->LastFocusedNodeId);
12962                     IM_ASSERT(last_focused_node != NULL);
12963                     ImGuiDockNode* last_focused_root_node = DockNodeGetRootNode(last_focused_node);
12964                     IM_ASSERT(last_focused_root_node == DockNodeGetRootNode(payload_node));
12965                     last_focused_node->SetLocalFlags(last_focused_node->LocalFlags |= ImGuiDockNodeFlags_CentralNode);
12966                     node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_CentralNode);
12967                     last_focused_root_node->CentralNode = last_focused_node;
12968                 }
12969 
12970                 IM_ASSERT(node->Windows.Size == 0);
12971                 DockNodeMoveChildNodes(node, payload_node);
12972             }
12973             else
12974             {
12975                 const ImGuiID payload_dock_id = payload_node->ID;
12976                 DockNodeMoveWindows(node, payload_node);
12977                 DockSettingsRenameNodeReferences(payload_dock_id, node->ID);
12978             }
12979             DockContextRemoveNode(ctx, payload_node, true);
12980         }
12981         else if (payload_window)
12982         {
12983             // Transfer single window
12984             const ImGuiID payload_dock_id = payload_window->DockId;
12985             node->VisibleWindow = payload_window;
12986             DockNodeAddWindow(node, payload_window, true);
12987             if (payload_dock_id != 0)
12988                 DockSettingsRenameNodeReferences(payload_dock_id, node->ID);
12989         }
12990     }
12991     else
12992     {
12993         // When docking a floating single window node we want to reevaluate auto-hiding of the tab bar
12994         node->WantHiddenTabBarUpdate = true;
12995     }
12996 
12997     // Update selection immediately
12998     if (ImGuiTabBar* tab_bar = node->TabBar)
12999         tab_bar->NextSelectedTabId = next_selected_id;
13000     MarkIniSettingsDirty();
13001 }
13002 
13003 // Problem:
13004 //   Undocking a large (~full screen) window would leave it so large that the bottom right sizing corner would more
13005 //   than likely be off the screen and the window would be hard to resize to fit on screen. This can be particularly problematic
13006 //   with 'ConfigWindowsMoveFromTitleBarOnly=true' and/or with 'ConfigWindowsResizeFromEdges=false' as well (the later can be
13007 //   due to missing ImGuiBackendFlags_HasMouseCursors backend flag).
13008 // Solution:
13009 //   When undocking a window we currently force its maximum size to 90% of the host viewport or monitor.
13010 // Reevaluate this when we implement preserving docked/undocked size ("docking_wip/undocked_size" branch).
FixLargeWindowsWhenUndocking(const ImVec2 & size,ImGuiViewport * ref_viewport)13011 static ImVec2 FixLargeWindowsWhenUndocking(const ImVec2& size, ImGuiViewport* ref_viewport)
13012 {
13013     if (ref_viewport == NULL)
13014         return size;
13015 
13016     ImGuiContext& g = *GImGui;
13017     ImVec2 max_size = ImFloor(ref_viewport->WorkSize * 0.90f);
13018     if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)
13019     {
13020         const ImGuiPlatformMonitor* monitor = ImGui::GetViewportPlatformMonitor(ref_viewport);
13021         max_size = ImFloor(monitor->WorkSize * 0.90f);
13022     }
13023     return ImMin(size, max_size);
13024 }
13025 
DockContextProcessUndockWindow(ImGuiContext * ctx,ImGuiWindow * window,bool clear_persistent_docking_ref)13026 void ImGui::DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window, bool clear_persistent_docking_ref)
13027 {
13028     IMGUI_DEBUG_LOG_DOCKING("DockContextProcessUndockWindow window '%s', clear_persistent_docking_ref = %d\n", window->Name, clear_persistent_docking_ref);
13029     IM_UNUSED(ctx);
13030     if (window->DockNode)
13031         DockNodeRemoveWindow(window->DockNode, window, clear_persistent_docking_ref ? 0 : window->DockId);
13032     else
13033         window->DockId = 0;
13034     window->Collapsed = false;
13035     window->DockIsActive = false;
13036     window->DockNodeIsVisible = window->DockTabIsVisible = false;
13037     window->Size = window->SizeFull = FixLargeWindowsWhenUndocking(window->SizeFull, window->Viewport);
13038 
13039     MarkIniSettingsDirty();
13040 }
13041 
DockContextProcessUndockNode(ImGuiContext * ctx,ImGuiDockNode * node)13042 void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node)
13043 {
13044     IMGUI_DEBUG_LOG_DOCKING("DockContextProcessUndockNode node %08X\n", node->ID);
13045     IM_ASSERT(node->IsLeafNode());
13046     IM_ASSERT(node->Windows.Size >= 1);
13047 
13048     if (node->IsRootNode() || node->IsCentralNode())
13049     {
13050         // 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.
13051         ImGuiDockNode* new_node = DockContextAddNode(ctx, 0);
13052         new_node->Pos = node->Pos;
13053         new_node->Size = node->Size;
13054         new_node->SizeRef = node->SizeRef;
13055         DockNodeMoveWindows(new_node, node);
13056         DockSettingsRenameNodeReferences(node->ID, new_node->ID);
13057         for (int n = 0; n < new_node->Windows.Size; n++)
13058             UpdateWindowParentAndRootLinks(new_node->Windows[n], new_node->Windows[n]->Flags, NULL);
13059         node = new_node;
13060     }
13061     else
13062     {
13063         // Otherwise extract our node and merge our sibling back into the parent node.
13064         IM_ASSERT(node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node);
13065         int index_in_parent = (node->ParentNode->ChildNodes[0] == node) ? 0 : 1;
13066         node->ParentNode->ChildNodes[index_in_parent] = NULL;
13067         DockNodeTreeMerge(ctx, node->ParentNode, node->ParentNode->ChildNodes[index_in_parent ^ 1]);
13068         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
13069         node->ParentNode = NULL;
13070     }
13071     node->AuthorityForPos = node->AuthorityForSize = ImGuiDataAuthority_DockNode;
13072     node->Size = FixLargeWindowsWhenUndocking(node->Size, node->Windows[0]->Viewport);
13073     node->WantMouseMove = true;
13074     MarkIniSettingsDirty();
13075 }
13076 
13077 // This is mostly used for automation.
DockContextCalcDropPosForDocking(ImGuiWindow * target,ImGuiDockNode * target_node,ImGuiWindow * payload,ImGuiDir split_dir,bool split_outer,ImVec2 * out_pos)13078 bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos)
13079 {
13080     // In DockNodePreviewDockSetup() for a root central node instead of showing both "inner" and "outer" drop rects
13081     // (which would be functionally identical) we only show the outer one. Reflect this here.
13082     if (target_node && target_node->ParentNode == NULL && target_node->IsCentralNode() && split_dir != ImGuiDir_None)
13083         split_outer = true;
13084     ImGuiDockPreviewData split_data;
13085     DockNodePreviewDockSetup(target, target_node, payload, &split_data, false, split_outer);
13086     if (split_data.DropRectsDraw[split_dir+1].IsInverted())
13087         return false;
13088     *out_pos = split_data.DropRectsDraw[split_dir+1].GetCenter();
13089     return true;
13090 }
13091 
13092 //-----------------------------------------------------------------------------
13093 // Docking: ImGuiDockNode
13094 //-----------------------------------------------------------------------------
13095 // - DockNodeGetTabOrder()
13096 // - DockNodeAddWindow()
13097 // - DockNodeRemoveWindow()
13098 // - DockNodeMoveChildNodes()
13099 // - DockNodeMoveWindows()
13100 // - DockNodeApplyPosSizeToWindows()
13101 // - DockNodeHideHostWindow()
13102 // - ImGuiDockNodeFindInfoResults
13103 // - DockNodeFindInfo()
13104 // - DockNodeFindWindowByID()
13105 // - DockNodeUpdateVisibleFlagAndInactiveChilds()
13106 // - DockNodeUpdateVisibleFlag()
13107 // - DockNodeStartMouseMovingWindow()
13108 // - DockNodeUpdate()
13109 // - DockNodeUpdateWindowMenu()
13110 // - DockNodeBeginAmendTabBar()
13111 // - DockNodeEndAmendTabBar()
13112 // - DockNodeUpdateTabBar()
13113 // - DockNodeAddTabBar()
13114 // - DockNodeRemoveTabBar()
13115 // - DockNodeIsDropAllowedOne()
13116 // - DockNodeIsDropAllowed()
13117 // - DockNodeCalcTabBarLayout()
13118 // - DockNodeCalcSplitRects()
13119 // - DockNodeCalcDropRectsAndTestMousePos()
13120 // - DockNodePreviewDockSetup()
13121 // - DockNodePreviewDockRender()
13122 //-----------------------------------------------------------------------------
13123 
ImGuiDockNode(ImGuiID id)13124 ImGuiDockNode::ImGuiDockNode(ImGuiID id)
13125 {
13126     ID = id;
13127     SharedFlags = LocalFlags = LocalFlagsInWindows = MergedFlags = ImGuiDockNodeFlags_None;
13128     ParentNode = ChildNodes[0] = ChildNodes[1] = NULL;
13129     TabBar = NULL;
13130     SplitAxis = ImGuiAxis_None;
13131 
13132     State = ImGuiDockNodeState_Unknown;
13133     HostWindow = VisibleWindow = NULL;
13134     CentralNode = OnlyNodeWithWindows = NULL;
13135     LastFrameAlive = LastFrameActive = LastFrameFocused = -1;
13136     LastFocusedNodeId = 0;
13137     SelectedTabId = 0;
13138     WantCloseTabId = 0;
13139     AuthorityForPos = AuthorityForSize = ImGuiDataAuthority_DockNode;
13140     AuthorityForViewport = ImGuiDataAuthority_Auto;
13141     IsVisible = true;
13142     IsFocused = HasCloseButton = HasWindowMenuButton = false;
13143     WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false;
13144     MarkedForPosSizeWrite = false;
13145 }
13146 
~ImGuiDockNode()13147 ImGuiDockNode::~ImGuiDockNode()
13148 {
13149     IM_DELETE(TabBar);
13150     TabBar = NULL;
13151     ChildNodes[0] = ChildNodes[1] = NULL;
13152 }
13153 
DockNodeGetTabOrder(ImGuiWindow * window)13154 int ImGui::DockNodeGetTabOrder(ImGuiWindow* window)
13155 {
13156     ImGuiTabBar* tab_bar = window->DockNode->TabBar;
13157     if (tab_bar == NULL)
13158         return -1;
13159     ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, window->ID);
13160     return tab ? tab_bar->GetTabOrder(tab) : -1;
13161 }
13162 
DockNodeHideWindowDuringHostWindowCreation(ImGuiWindow * window)13163 static void DockNodeHideWindowDuringHostWindowCreation(ImGuiWindow* window)
13164 {
13165     window->Hidden = true;
13166     window->HiddenFramesCanSkipItems = window->Active ? 1 : 2;
13167 }
13168 
DockNodeAddWindow(ImGuiDockNode * node,ImGuiWindow * window,bool add_to_tab_bar)13169 static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, bool add_to_tab_bar)
13170 {
13171     ImGuiContext& g = *GImGui; (void)g;
13172     if (window->DockNode)
13173     {
13174         // Can overwrite an existing window->DockNode (e.g. pointing to a disabled DockSpace node)
13175         IM_ASSERT(window->DockNode->ID != node->ID);
13176         DockNodeRemoveWindow(window->DockNode, window, 0);
13177     }
13178     IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL);
13179     IMGUI_DEBUG_LOG_DOCKING("DockNodeAddWindow node 0x%08X window '%s'\n", node->ID, window->Name);
13180 
13181     // If more than 2 windows appeared on the same frame leading to the creation of a new hosting window,
13182     // we'll hide windows until the host window is ready. Hide the 1st window after its been output (so it is not visible for one frame).
13183     // We will call DockNodeHideWindowDuringHostWindowCreation() on ourselves in Begin()
13184     if (node->HostWindow == NULL && node->Windows.Size == 1 && node->Windows[0]->WasActive == false)
13185         DockNodeHideWindowDuringHostWindowCreation(node->Windows[0]);
13186 
13187     node->Windows.push_back(window);
13188     node->WantHiddenTabBarUpdate = true;
13189     window->DockNode = node;
13190     window->DockId = node->ID;
13191     window->DockIsActive = (node->Windows.Size > 1);
13192     window->DockTabWantClose = false;
13193 
13194     // When reactivating a node with one or two loose window, the window pos/size/viewport are authoritative over the node storage.
13195     // In particular it is important we init the viewport from the first window so we don't create two viewports and drop one.
13196     if (node->HostWindow == NULL && node->IsFloatingNode())
13197     {
13198         if (node->AuthorityForPos == ImGuiDataAuthority_Auto)
13199             node->AuthorityForPos = ImGuiDataAuthority_Window;
13200         if (node->AuthorityForSize == ImGuiDataAuthority_Auto)
13201             node->AuthorityForSize = ImGuiDataAuthority_Window;
13202         if (node->AuthorityForViewport == ImGuiDataAuthority_Auto)
13203             node->AuthorityForViewport = ImGuiDataAuthority_Window;
13204     }
13205 
13206     // Add to tab bar if requested
13207     if (add_to_tab_bar)
13208     {
13209         if (node->TabBar == NULL)
13210         {
13211             DockNodeAddTabBar(node);
13212             node->TabBar->SelectedTabId = node->TabBar->NextSelectedTabId = node->SelectedTabId;
13213 
13214             // Add existing windows
13215             for (int n = 0; n < node->Windows.Size - 1; n++)
13216                 TabBarAddTab(node->TabBar, ImGuiTabItemFlags_None, node->Windows[n]);
13217         }
13218         TabBarAddTab(node->TabBar, ImGuiTabItemFlags_Unsorted, window);
13219     }
13220 
13221     DockNodeUpdateVisibleFlag(node);
13222 
13223     // 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.
13224     if (node->HostWindow)
13225         UpdateWindowParentAndRootLinks(window, window->Flags | ImGuiWindowFlags_ChildWindow, node->HostWindow);
13226 }
13227 
DockNodeRemoveWindow(ImGuiDockNode * node,ImGuiWindow * window,ImGuiID save_dock_id)13228 static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id)
13229 {
13230     ImGuiContext& g = *GImGui;
13231     IM_ASSERT(window->DockNode == node);
13232     //IM_ASSERT(window->RootWindowDockTree == node->HostWindow);
13233     //IM_ASSERT(window->LastFrameActive < g.FrameCount);    // We may call this from Begin()
13234     IM_ASSERT(save_dock_id == 0 || save_dock_id == node->ID);
13235     IMGUI_DEBUG_LOG_DOCKING("DockNodeRemoveWindow node 0x%08X window '%s'\n", node->ID, window->Name);
13236 
13237     window->DockNode = NULL;
13238     window->DockIsActive = window->DockTabWantClose = false;
13239     window->DockId = save_dock_id;
13240     UpdateWindowParentAndRootLinks(window, window->Flags & ~ImGuiWindowFlags_ChildWindow, NULL); // Update immediately
13241 
13242     // Remove window
13243     bool erased = false;
13244     for (int n = 0; n < node->Windows.Size; n++)
13245         if (node->Windows[n] == window)
13246         {
13247             node->Windows.erase(node->Windows.Data + n);
13248             erased = true;
13249             break;
13250         }
13251     IM_ASSERT(erased);
13252     if (node->VisibleWindow == window)
13253         node->VisibleWindow = NULL;
13254 
13255     // Remove tab and possibly tab bar
13256     node->WantHiddenTabBarUpdate = true;
13257     if (node->TabBar)
13258     {
13259         TabBarRemoveTab(node->TabBar, window->ID);
13260         const int tab_count_threshold_for_tab_bar = node->IsCentralNode() ? 1 : 2;
13261         if (node->Windows.Size < tab_count_threshold_for_tab_bar)
13262             DockNodeRemoveTabBar(node);
13263     }
13264 
13265     if (node->Windows.Size == 0 && !node->IsCentralNode() && !node->IsDockSpace() && window->DockId != node->ID)
13266     {
13267         // Automatic dock node delete themselves if they are not holding at least one tab
13268         DockContextRemoveNode(&g, node, true);
13269         return;
13270     }
13271 
13272     if (node->Windows.Size == 1 && !node->IsCentralNode() && node->HostWindow)
13273     {
13274         ImGuiWindow* remaining_window = node->Windows[0];
13275         if (node->HostWindow->ViewportOwned && node->IsRootNode())
13276         {
13277             // Transfer viewport back to the remaining loose window
13278             IM_ASSERT(node->HostWindow->Viewport->Window == node->HostWindow);
13279             node->HostWindow->Viewport->Window = remaining_window;
13280             node->HostWindow->Viewport->ID = remaining_window->ID;
13281         }
13282         remaining_window->Collapsed = node->HostWindow->Collapsed;
13283     }
13284 
13285     // Update visibility immediately is required so the DockNodeUpdateRemoveInactiveChilds() processing can reflect changes up the tree
13286     DockNodeUpdateVisibleFlag(node);
13287 }
13288 
DockNodeMoveChildNodes(ImGuiDockNode * dst_node,ImGuiDockNode * src_node)13289 static void ImGui::DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode* src_node)
13290 {
13291     IM_ASSERT(dst_node->Windows.Size == 0);
13292     dst_node->ChildNodes[0] = src_node->ChildNodes[0];
13293     dst_node->ChildNodes[1] = src_node->ChildNodes[1];
13294     if (dst_node->ChildNodes[0])
13295         dst_node->ChildNodes[0]->ParentNode = dst_node;
13296     if (dst_node->ChildNodes[1])
13297         dst_node->ChildNodes[1]->ParentNode = dst_node;
13298     dst_node->SplitAxis = src_node->SplitAxis;
13299     dst_node->SizeRef = src_node->SizeRef;
13300     src_node->ChildNodes[0] = src_node->ChildNodes[1] = NULL;
13301 }
13302 
DockNodeMoveWindows(ImGuiDockNode * dst_node,ImGuiDockNode * src_node)13303 static void ImGui::DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node)
13304 {
13305     // Insert tabs in the same orders as currently ordered (node->Windows isn't ordered)
13306     IM_ASSERT(src_node && dst_node && dst_node != src_node);
13307     ImGuiTabBar* src_tab_bar = src_node->TabBar;
13308     if (src_tab_bar != NULL)
13309         IM_ASSERT(src_node->Windows.Size <= src_node->TabBar->Tabs.Size);
13310 
13311     // If the dst_node is empty we can just move the entire tab bar (to preserve selection, scrolling, etc.)
13312     bool move_tab_bar = (src_tab_bar != NULL) && (dst_node->TabBar == NULL);
13313     if (move_tab_bar)
13314     {
13315         dst_node->TabBar = src_node->TabBar;
13316         src_node->TabBar = NULL;
13317     }
13318 
13319     for (int n = 0; n < src_node->Windows.Size; n++)
13320     {
13321         // DockNode's TabBar may have non-window Tabs manually appended by user
13322         if (ImGuiWindow* window = src_tab_bar ? src_tab_bar->Tabs[n].Window : src_node->Windows[n])
13323         {
13324             window->DockNode = NULL;
13325             window->DockIsActive = false;
13326             DockNodeAddWindow(dst_node, window, move_tab_bar ? false : true);
13327         }
13328     }
13329     src_node->Windows.clear();
13330 
13331     if (!move_tab_bar && src_node->TabBar)
13332     {
13333         if (dst_node->TabBar)
13334             dst_node->TabBar->SelectedTabId = src_node->TabBar->SelectedTabId;
13335         DockNodeRemoveTabBar(src_node);
13336     }
13337 }
13338 
DockNodeApplyPosSizeToWindows(ImGuiDockNode * node)13339 static void ImGui::DockNodeApplyPosSizeToWindows(ImGuiDockNode* node)
13340 {
13341     for (int n = 0; n < node->Windows.Size; n++)
13342     {
13343         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
13344         SetWindowSize(node->Windows[n], node->Size, ImGuiCond_Always);
13345     }
13346 }
13347 
DockNodeHideHostWindow(ImGuiDockNode * node)13348 static void ImGui::DockNodeHideHostWindow(ImGuiDockNode* node)
13349 {
13350     if (node->HostWindow)
13351     {
13352         if (node->HostWindow->DockNodeAsHost == node)
13353             node->HostWindow->DockNodeAsHost = NULL;
13354         node->HostWindow = NULL;
13355     }
13356 
13357     if (node->Windows.Size == 1)
13358     {
13359         node->VisibleWindow = node->Windows[0];
13360         node->Windows[0]->DockIsActive = false;
13361     }
13362 
13363     if (node->TabBar)
13364         DockNodeRemoveTabBar(node);
13365 }
13366 
13367 // Search function called once by root node in DockNodeUpdate()
13368 struct ImGuiDockNodeFindInfoResults
13369 {
13370     ImGuiDockNode*      CentralNode;
13371     ImGuiDockNode*      FirstNodeWithWindows;
13372     int                 CountNodesWithWindows;
13373     //ImGuiWindowClass  WindowClassForMerges;
13374 
ImGuiDockNodeFindInfoResultsImGuiDockNodeFindInfoResults13375     ImGuiDockNodeFindInfoResults() { CentralNode = FirstNodeWithWindows = NULL; CountNodesWithWindows = 0; }
13376 };
13377 
DockNodeFindInfo(ImGuiDockNode * node,ImGuiDockNodeFindInfoResults * results)13378 static void DockNodeFindInfo(ImGuiDockNode* node, ImGuiDockNodeFindInfoResults* results)
13379 {
13380     if (node->Windows.Size > 0)
13381     {
13382         if (results->FirstNodeWithWindows == NULL)
13383             results->FirstNodeWithWindows = node;
13384         results->CountNodesWithWindows++;
13385     }
13386     if (node->IsCentralNode())
13387     {
13388         IM_ASSERT(results->CentralNode == NULL); // Should be only one
13389         IM_ASSERT(node->IsLeafNode() && "If you get this assert: please submit .ini file + repro of actions leading to this.");
13390         results->CentralNode = node;
13391     }
13392     if (results->CountNodesWithWindows > 1 && results->CentralNode != NULL)
13393         return;
13394     if (node->ChildNodes[0])
13395         DockNodeFindInfo(node->ChildNodes[0], results);
13396     if (node->ChildNodes[1])
13397         DockNodeFindInfo(node->ChildNodes[1], results);
13398 }
13399 
DockNodeFindWindowByID(ImGuiDockNode * node,ImGuiID id)13400 static ImGuiWindow* ImGui::DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id)
13401 {
13402     IM_ASSERT(id != 0);
13403     for (int n = 0; n < node->Windows.Size; n++)
13404         if (node->Windows[n]->ID == id)
13405             return node->Windows[n];
13406     return NULL;
13407 }
13408 
13409 // - Remove inactive windows/nodes.
13410 // - Update visibility flag.
DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode * node)13411 static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node)
13412 {
13413     ImGuiContext& g = *GImGui;
13414     IM_ASSERT(node->ParentNode == NULL || node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node);
13415 
13416     // Inherit most flags
13417     if (node->ParentNode)
13418         node->SharedFlags = node->ParentNode->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_;
13419 
13420     // Recurse into children
13421     // There is the possibility that one of our child becoming empty will delete itself and moving its sibling contents into 'node'.
13422     // If 'node->ChildNode[0]' delete itself, then 'node->ChildNode[1]->Windows' will be moved into 'node'
13423     // 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)
13424     if (node->ChildNodes[0])
13425         DockNodeUpdateVisibleFlagAndInactiveChilds(node->ChildNodes[0]);
13426     if (node->ChildNodes[1])
13427         DockNodeUpdateVisibleFlagAndInactiveChilds(node->ChildNodes[1]);
13428 
13429     // Remove inactive windows
13430     // Merge node flags overrides stored in windows
13431     node->LocalFlagsInWindows = ImGuiDockNodeFlags_None;
13432     for (int window_n = 0; window_n < node->Windows.Size; window_n++)
13433     {
13434         ImGuiWindow* window = node->Windows[window_n];
13435         IM_ASSERT(window->DockNode == node);
13436 
13437         bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount);
13438         bool remove = false;
13439         remove |= node_was_active && (window->LastFrameActive + 1 < g.FrameCount);
13440         remove |= node_was_active && (node->WantCloseAll || node->WantCloseTabId == window->ID) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument);  // Submit all _expected_ closure from last frame
13441         remove |= (window->DockTabWantClose);
13442         if (remove)
13443         {
13444             window->DockTabWantClose = false;
13445             if (node->Windows.Size == 1 && !node->IsCentralNode())
13446             {
13447                 DockNodeHideHostWindow(node);
13448                 node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow;
13449                 DockNodeRemoveWindow(node, window, node->ID); // Will delete the node so it'll be invalid on return
13450                 return;
13451             }
13452             DockNodeRemoveWindow(node, window, node->ID);
13453             window_n--;
13454         }
13455         else
13456         {
13457             // FIXME-DOCKING: Missing policies for conflict resolution, hence the "Experimental" tag on this.
13458             //node->LocalFlagsInWindow &= ~window->WindowClass.DockNodeFlagsOverrideClear;
13459             node->LocalFlagsInWindows |= window->WindowClass.DockNodeFlagsOverrideSet;
13460         }
13461     }
13462     node->UpdateMergedFlags();
13463 
13464     // Auto-hide tab bar option
13465     ImGuiDockNodeFlags node_flags = node->MergedFlags;
13466     if (node->WantHiddenTabBarUpdate && node->Windows.Size == 1 && (node_flags & ImGuiDockNodeFlags_AutoHideTabBar) && !node->IsHiddenTabBar())
13467         node->WantHiddenTabBarToggle = true;
13468     node->WantHiddenTabBarUpdate = false;
13469 
13470     // Cancel toggling if we know our tab bar is enforced to be hidden at all times
13471     if (node->WantHiddenTabBarToggle && node->VisibleWindow && (node->VisibleWindow->WindowClass.DockNodeFlagsOverrideSet & ImGuiDockNodeFlags_HiddenTabBar))
13472         node->WantHiddenTabBarToggle = false;
13473 
13474     // Apply toggles at a single point of the frame (here!)
13475     if (node->Windows.Size > 1)
13476         node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_HiddenTabBar);
13477     else if (node->WantHiddenTabBarToggle)
13478         node->SetLocalFlags(node->LocalFlags ^ ImGuiDockNodeFlags_HiddenTabBar);
13479     node->WantHiddenTabBarToggle = false;
13480 
13481     DockNodeUpdateVisibleFlag(node);
13482 }
13483 
DockNodeUpdateVisibleFlag(ImGuiDockNode * node)13484 static void ImGui::DockNodeUpdateVisibleFlag(ImGuiDockNode* node)
13485 {
13486     // Update visibility flag
13487     bool is_visible = (node->ParentNode == NULL) ? node->IsDockSpace() : node->IsCentralNode();
13488     is_visible |= (node->Windows.Size > 0);
13489     is_visible |= (node->ChildNodes[0] && node->ChildNodes[0]->IsVisible);
13490     is_visible |= (node->ChildNodes[1] && node->ChildNodes[1]->IsVisible);
13491     node->IsVisible = is_visible;
13492 }
13493 
DockNodeStartMouseMovingWindow(ImGuiDockNode * node,ImGuiWindow * window)13494 static void ImGui::DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window)
13495 {
13496     ImGuiContext& g = *GImGui;
13497     IM_ASSERT(node->WantMouseMove == true);
13498     StartMouseMovingWindow(window);
13499     g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - node->Pos;
13500     g.MovingWindow = window; // If we are docked into a non moveable root window, StartMouseMovingWindow() won't set g.MovingWindow. Override that decision.
13501     node->WantMouseMove = false;
13502 }
13503 
13504 // Update CentralNode, OnlyNodeWithWindows, LastFocusedNodeID. Copy window class.
DockNodeUpdateForRootNode(ImGuiDockNode * node)13505 static void ImGui::DockNodeUpdateForRootNode(ImGuiDockNode* node)
13506 {
13507     DockNodeUpdateVisibleFlagAndInactiveChilds(node);
13508 
13509     // FIXME-DOCK: Merge this scan into the one above.
13510     // - Setup central node pointers
13511     // - 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!)
13512     ImGuiDockNodeFindInfoResults results;
13513     DockNodeFindInfo(node, &results);
13514     node->CentralNode = results.CentralNode;
13515     node->OnlyNodeWithWindows = (results.CountNodesWithWindows == 1) ? results.FirstNodeWithWindows : NULL;
13516     if (node->LastFocusedNodeId == 0 && results.FirstNodeWithWindows != NULL)
13517         node->LastFocusedNodeId = results.FirstNodeWithWindows->ID;
13518 
13519     // Copy the window class from of our first window so it can be used for proper dock filtering.
13520     // When node has mixed windows, prioritize the class with the most constraint (DockingAllowUnclassed = false) as the reference to copy.
13521     // FIXME-DOCK: We don't recurse properly, this code could be reworked to work from DockNodeUpdateScanRec.
13522     if (ImGuiDockNode* first_node_with_windows = results.FirstNodeWithWindows)
13523     {
13524         node->WindowClass = first_node_with_windows->Windows[0]->WindowClass;
13525         for (int n = 1; n < first_node_with_windows->Windows.Size; n++)
13526             if (first_node_with_windows->Windows[n]->WindowClass.DockingAllowUnclassed == false)
13527             {
13528                 node->WindowClass = first_node_with_windows->Windows[n]->WindowClass;
13529                 break;
13530             }
13531     }
13532 }
13533 
DockNodeSetupHostWindow(ImGuiDockNode * node,ImGuiWindow * host_window)13534 static void DockNodeSetupHostWindow(ImGuiDockNode* node, ImGuiWindow* host_window)
13535 {
13536     // Remove ourselves from any previous different host window
13537     // This can happen if a user mistakenly does (see #4295 for details):
13538     //  - N+0: DockBuilderAddNode(id, 0)    // missing ImGuiDockNodeFlags_DockSpace
13539     //  - N+1: NewFrame()                   // will create floating host window for that node
13540     //  - N+1: DockSpace(id)                // requalify node as dockspace, moving host window
13541     if (node->HostWindow && node->HostWindow != host_window && node->HostWindow->DockNodeAsHost == node)
13542         node->HostWindow->DockNodeAsHost = NULL;
13543 
13544     host_window->DockNodeAsHost = node;
13545     node->HostWindow = host_window;
13546 }
13547 
DockNodeUpdate(ImGuiDockNode * node)13548 static void ImGui::DockNodeUpdate(ImGuiDockNode* node)
13549 {
13550     ImGuiContext& g = *GImGui;
13551     IM_ASSERT(node->LastFrameActive != g.FrameCount);
13552     node->LastFrameAlive = g.FrameCount;
13553     node->MarkedForPosSizeWrite = false;
13554 
13555     node->CentralNode = node->OnlyNodeWithWindows = NULL;
13556     if (node->IsRootNode())
13557         DockNodeUpdateForRootNode(node);
13558 
13559     // Remove tab bar if not needed
13560     if (node->TabBar && node->IsNoTabBar())
13561         DockNodeRemoveTabBar(node);
13562 
13563     // 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)
13564     bool want_to_hide_host_window = false;
13565     if (node->Windows.Size <= 1 && node->IsFloatingNode() && node->IsLeafNode())
13566         if (!g.IO.ConfigDockingAlwaysTabBar && (node->Windows.Size == 0 || !node->Windows[0]->WindowClass.DockingAlwaysTabBar))
13567             want_to_hide_host_window = true;
13568     if (want_to_hide_host_window)
13569     {
13570         if (node->Windows.Size == 1)
13571         {
13572             // Floating window pos/size is authoritative
13573             ImGuiWindow* single_window = node->Windows[0];
13574             node->Pos = single_window->Pos;
13575             node->Size = single_window->SizeFull;
13576             node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window;
13577 
13578             // Transfer focus immediately so when we revert to a regular window it is immediately selected
13579             if (node->HostWindow && g.NavWindow == node->HostWindow)
13580                 FocusWindow(single_window);
13581             if (node->HostWindow)
13582             {
13583                 single_window->Viewport = node->HostWindow->Viewport;
13584                 single_window->ViewportId = node->HostWindow->ViewportId;
13585                 if (node->HostWindow->ViewportOwned)
13586                 {
13587                     single_window->Viewport->Window = single_window;
13588                     single_window->ViewportOwned = true;
13589                 }
13590             }
13591         }
13592 
13593         DockNodeHideHostWindow(node);
13594         node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow;
13595         node->WantCloseAll = false;
13596         node->WantCloseTabId = 0;
13597         node->HasCloseButton = node->HasWindowMenuButton = false;
13598         node->LastFrameActive = g.FrameCount;
13599 
13600         if (node->WantMouseMove && node->Windows.Size == 1)
13601             DockNodeStartMouseMovingWindow(node, node->Windows[0]);
13602         return;
13603     }
13604 
13605     // In some circumstance we will defer creating the host window (so everything will be kept hidden),
13606     // while the expected visible window is resizing itself.
13607     // This is important for first-time (no ini settings restored) single window when io.ConfigDockingAlwaysTabBar is enabled,
13608     // otherwise the node ends up using the minimum window size. Effectively those windows will take an extra frame to show up:
13609     //   N+0: Begin(): window created (with no known size), node is created
13610     //   N+1: DockNodeUpdate(): node skip creating host window / Begin(): window size applied, not visible
13611     //   N+2: DockNodeUpdate(): node can create host window / Begin(): window becomes visible
13612     // We could remove this frame if we could reliably calculate the expected window size during node update, before the Begin() code.
13613     // It would require a generalization of CalcWindowExpectedSize(), probably extracting code away from Begin().
13614     // In reality it isn't very important as user quickly ends up with size data in .ini file.
13615     if (node->IsVisible && node->HostWindow == NULL && node->IsFloatingNode() && node->IsLeafNode())
13616     {
13617         IM_ASSERT(node->Windows.Size > 0);
13618         ImGuiWindow* ref_window = NULL;
13619         if (node->SelectedTabId != 0) // Note that we prune single-window-node settings on .ini loading, so this is generally 0 for them!
13620             ref_window = DockNodeFindWindowByID(node, node->SelectedTabId);
13621         if (ref_window == NULL)
13622             ref_window = node->Windows[0];
13623         if (ref_window->AutoFitFramesX > 0 || ref_window->AutoFitFramesY > 0)
13624         {
13625             node->State = ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing;
13626             return;
13627         }
13628     }
13629 
13630     const ImGuiDockNodeFlags node_flags = node->MergedFlags;
13631 
13632     // Decide if the node will have a close button and a window menu button
13633     node->HasWindowMenuButton = (node->Windows.Size > 0) && (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0;
13634     node->HasCloseButton = false;
13635     for (int window_n = 0; window_n < node->Windows.Size; window_n++)
13636     {
13637         // FIXME-DOCK: Setting DockIsActive here means that for single active window in a leaf node, DockIsActive will be cleared until the next Begin() call.
13638         ImGuiWindow* window = node->Windows[window_n];
13639         node->HasCloseButton |= window->HasCloseButton;
13640         window->DockIsActive = (node->Windows.Size > 1);
13641     }
13642     if (node_flags & ImGuiDockNodeFlags_NoCloseButton)
13643         node->HasCloseButton = false;
13644 
13645     // Bind or create host window
13646     ImGuiWindow* host_window = NULL;
13647     bool beginned_into_host_window = false;
13648     if (node->IsDockSpace())
13649     {
13650         // [Explicit root dockspace node]
13651         IM_ASSERT(node->HostWindow);
13652         host_window = node->HostWindow;
13653     }
13654     else
13655     {
13656         // [Automatic root or child nodes]
13657         if (node->IsRootNode() && node->IsVisible)
13658         {
13659             ImGuiWindow* ref_window = (node->Windows.Size > 0) ? node->Windows[0] : NULL;
13660 
13661             // Sync Pos
13662             if (node->AuthorityForPos == ImGuiDataAuthority_Window && ref_window)
13663                 SetNextWindowPos(ref_window->Pos);
13664             else if (node->AuthorityForPos == ImGuiDataAuthority_DockNode)
13665                 SetNextWindowPos(node->Pos);
13666 
13667             // Sync Size
13668             if (node->AuthorityForSize == ImGuiDataAuthority_Window && ref_window)
13669                 SetNextWindowSize(ref_window->SizeFull);
13670             else if (node->AuthorityForSize == ImGuiDataAuthority_DockNode)
13671                 SetNextWindowSize(node->Size);
13672 
13673             // Sync Collapsed
13674             if (node->AuthorityForSize == ImGuiDataAuthority_Window && ref_window)
13675                 SetNextWindowCollapsed(ref_window->Collapsed);
13676 
13677             // Sync Viewport
13678             if (node->AuthorityForViewport == ImGuiDataAuthority_Window && ref_window)
13679                 SetNextWindowViewport(ref_window->ViewportId);
13680 
13681             SetNextWindowClass(&node->WindowClass);
13682 
13683             // Begin into the host window
13684             char window_label[20];
13685             DockNodeGetHostWindowTitle(node, window_label, IM_ARRAYSIZE(window_label));
13686             ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_DockNodeHost;
13687             window_flags |= ImGuiWindowFlags_NoFocusOnAppearing;
13688             window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoCollapse;
13689             window_flags |= ImGuiWindowFlags_NoTitleBar;
13690 
13691             PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
13692             Begin(window_label, NULL, window_flags);
13693             PopStyleVar();
13694             beginned_into_host_window = true;
13695 
13696             host_window = g.CurrentWindow;
13697             DockNodeSetupHostWindow(node, host_window);
13698             host_window->DC.CursorPos = host_window->Pos;
13699             node->Pos = host_window->Pos;
13700             node->Size = host_window->Size;
13701 
13702             // We set ImGuiWindowFlags_NoFocusOnAppearing because we don't want the host window to take full focus (e.g. steal NavWindow)
13703             // But we still it bring it to the front of display. There's no way to choose this precise behavior via window flags.
13704             // 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.
13705             // 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
13706             // 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
13707             // after the dock host window, losing their top-most status.
13708             if (node->HostWindow->Appearing)
13709                 BringWindowToDisplayFront(node->HostWindow);
13710 
13711             node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Auto;
13712         }
13713         else if (node->ParentNode)
13714         {
13715             node->HostWindow = host_window = node->ParentNode->HostWindow;
13716             node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Auto;
13717         }
13718         if (node->WantMouseMove && node->HostWindow)
13719             DockNodeStartMouseMovingWindow(node, node->HostWindow);
13720     }
13721 
13722     // Update focused node (the one whose title bar is highlight) within a node tree
13723     if (node->IsSplitNode())
13724         IM_ASSERT(node->TabBar == NULL);
13725     if (node->IsRootNode())
13726         if (g.NavWindow && g.NavWindow->RootWindow->DockNode && g.NavWindow->RootWindow->ParentWindow == host_window)
13727             node->LastFocusedNodeId = g.NavWindow->RootWindow->DockNode->ID;
13728 
13729     // We need to draw a background at the root level if requested by ImGuiDockNodeFlags_PassthruCentralNode, but we will only know the correct pos/size
13730     // _after_ processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order!
13731     const bool render_dockspace_bg = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0;
13732     if (render_dockspace_bg)
13733     {
13734         host_window->DrawList->ChannelsSplit(2);
13735         host_window->DrawList->ChannelsSetCurrent(1);
13736     }
13737 
13738     // Register a hit-test hole in the window unless we are currently dragging a window that is compatible with our dockspace
13739     ImGuiDockNode* central_node = node->CentralNode;
13740     const bool central_node_hole = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0 && central_node != NULL && central_node->IsEmpty();
13741     bool central_node_hole_register_hit_test_hole = central_node_hole;
13742     if (central_node_hole)
13743         if (const ImGuiPayload* payload = ImGui::GetDragDropPayload())
13744             if (payload->IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) && DockNodeIsDropAllowed(host_window, *(ImGuiWindow**)payload->Data))
13745                 central_node_hole_register_hit_test_hole = false;
13746     if (central_node_hole_register_hit_test_hole)
13747     {
13748         // We add a little padding to match the "resize from edges" behavior and allow grabbing the splitter easily.
13749         // (But we only add it if there's something else on the other side of the hole, otherwise for e.g. fullscreen
13750         // covering passthru node we'd have a gap on the edge not covered by the hole)
13751         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
13752         ImGuiDockNode* root_node = DockNodeGetRootNode(central_node);
13753         ImRect root_rect(root_node->Pos, root_node->Pos + root_node->Size);
13754         ImRect hole_rect(central_node->Pos, central_node->Pos + central_node->Size);
13755         if (hole_rect.Min.x > root_rect.Min.x) { hole_rect.Min.x += WINDOWS_HOVER_PADDING; }
13756         if (hole_rect.Max.x < root_rect.Max.x) { hole_rect.Max.x -= WINDOWS_HOVER_PADDING; }
13757         if (hole_rect.Min.y > root_rect.Min.y) { hole_rect.Min.y += WINDOWS_HOVER_PADDING; }
13758         if (hole_rect.Max.y < root_rect.Max.y) { hole_rect.Max.y -= WINDOWS_HOVER_PADDING; }
13759         //GetForegroundDrawList()->AddRect(hole_rect.Min, hole_rect.Max, IM_COL32(255, 0, 0, 255));
13760         if (central_node_hole && !hole_rect.IsInverted())
13761         {
13762             SetWindowHitTestHole(host_window, hole_rect.Min, hole_rect.Max - hole_rect.Min);
13763             SetWindowHitTestHole(host_window->ParentWindow, hole_rect.Min, hole_rect.Max - hole_rect.Min);
13764         }
13765     }
13766 
13767     // Update position/size, process and draw resizing splitters
13768     if (node->IsRootNode() && host_window)
13769     {
13770         DockNodeTreeUpdatePosSize(node, host_window->Pos, host_window->Size);
13771         DockNodeTreeUpdateSplitter(node);
13772     }
13773 
13774     // Draw empty node background (currently can only be the Central Node)
13775     if (host_window && node->IsEmpty() && node->IsVisible && !(node_flags & ImGuiDockNodeFlags_PassthruCentralNode))
13776         host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_DockingEmptyBg));
13777 
13778     // Draw whole dockspace background if ImGuiDockNodeFlags_PassthruCentralNode if set.
13779     if (render_dockspace_bg && node->IsVisible)
13780     {
13781         host_window->DrawList->ChannelsSetCurrent(0);
13782         if (central_node_hole)
13783             RenderRectFilledWithHole(host_window->DrawList, node->Rect(), central_node->Rect(), GetColorU32(ImGuiCol_WindowBg), 0.0f);
13784         else
13785             host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_WindowBg), 0.0f);
13786         host_window->DrawList->ChannelsMerge();
13787     }
13788 
13789     // Draw and populate Tab Bar
13790     if (host_window && node->Windows.Size > 0)
13791     {
13792         DockNodeUpdateTabBar(node, host_window);
13793     }
13794     else
13795     {
13796         node->WantCloseAll = false;
13797         node->WantCloseTabId = 0;
13798         node->IsFocused = false;
13799     }
13800     if (node->TabBar && node->TabBar->SelectedTabId)
13801         node->SelectedTabId = node->TabBar->SelectedTabId;
13802     else if (node->Windows.Size > 0)
13803         node->SelectedTabId = node->Windows[0]->ID;
13804 
13805     // Draw payload drop target
13806     if (host_window && node->IsVisible)
13807         if (node->IsRootNode() && (g.MovingWindow == NULL || g.MovingWindow->RootWindowDockTree != host_window))
13808             BeginDockableDragDropTarget(host_window);
13809 
13810     // We update this after DockNodeUpdateTabBar()
13811     node->LastFrameActive = g.FrameCount;
13812 
13813     // Recurse into children
13814     // FIXME-DOCK FIXME-OPT: Should not need to recurse into children
13815     if (host_window)
13816     {
13817         if (node->ChildNodes[0])
13818             DockNodeUpdate(node->ChildNodes[0]);
13819         if (node->ChildNodes[1])
13820             DockNodeUpdate(node->ChildNodes[1]);
13821 
13822         // Render outer borders last (after the tab bar)
13823         if (node->IsRootNode())
13824             RenderWindowOuterBorders(host_window);
13825     }
13826 
13827     // End host window
13828     if (beginned_into_host_window) //-V1020
13829         End();
13830 }
13831 
13832 // 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)13833 static int IMGUI_CDECL TabItemComparerByDockOrder(const void* lhs, const void* rhs)
13834 {
13835     ImGuiWindow* a = ((const ImGuiTabItem*)lhs)->Window;
13836     ImGuiWindow* b = ((const ImGuiTabItem*)rhs)->Window;
13837     if (int d = ((a->DockOrder == -1) ? INT_MAX : a->DockOrder) - ((b->DockOrder == -1) ? INT_MAX : b->DockOrder))
13838         return d;
13839     return (a->BeginOrderWithinContext - b->BeginOrderWithinContext);
13840 }
13841 
DockNodeUpdateWindowMenu(ImGuiDockNode * node,ImGuiTabBar * tab_bar)13842 static ImGuiID ImGui::DockNodeUpdateWindowMenu(ImGuiDockNode* node, ImGuiTabBar* tab_bar)
13843 {
13844     // Try to position the menu so it is more likely to stays within the same viewport
13845     ImGuiContext& g = *GImGui;
13846     ImGuiID ret_tab_id = 0;
13847     if (g.Style.WindowMenuButtonPosition == ImGuiDir_Left)
13848         SetNextWindowPos(ImVec2(node->Pos.x, node->Pos.y + GetFrameHeight()), ImGuiCond_Always, ImVec2(0.0f, 0.0f));
13849     else
13850         SetNextWindowPos(ImVec2(node->Pos.x + node->Size.x, node->Pos.y + GetFrameHeight()), ImGuiCond_Always, ImVec2(1.0f, 0.0f));
13851     if (BeginPopup("#WindowMenu"))
13852     {
13853         node->IsFocused = true;
13854         if (tab_bar->Tabs.Size == 1)
13855         {
13856             if (MenuItem("Hide tab bar", NULL, node->IsHiddenTabBar()))
13857                 node->WantHiddenTabBarToggle = true;
13858         }
13859         else
13860         {
13861             for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
13862             {
13863                 ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
13864                 if (tab->Flags & ImGuiTabItemFlags_Button)
13865                     continue;
13866                 if (Selectable(tab_bar->GetTabName(tab), tab->ID == tab_bar->SelectedTabId))
13867                     ret_tab_id = tab->ID;
13868                 SameLine();
13869                 Text("   ");
13870             }
13871         }
13872         EndPopup();
13873     }
13874     return ret_tab_id;
13875 }
13876 
13877 // User helper to append/amend into a dock node tab bar. Most commonly used to add e.g. a "+" button.
DockNodeBeginAmendTabBar(ImGuiDockNode * node)13878 bool ImGui::DockNodeBeginAmendTabBar(ImGuiDockNode* node)
13879 {
13880     if (node->TabBar == NULL || node->HostWindow == NULL)
13881         return false;
13882     if (node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly)
13883         return false;
13884     Begin(node->HostWindow->Name);
13885     PushOverrideID(node->ID);
13886     bool ret = BeginTabBarEx(node->TabBar, node->TabBar->BarRect, node->TabBar->Flags, node);
13887     IM_UNUSED(ret);
13888     IM_ASSERT(ret);
13889     return true;
13890 }
13891 
DockNodeEndAmendTabBar()13892 void ImGui::DockNodeEndAmendTabBar()
13893 {
13894     EndTabBar();
13895     PopID();
13896     End();
13897 }
13898 
13899 // Submit the tab bar corresponding to a dock node and various housekeeping details.
DockNodeUpdateTabBar(ImGuiDockNode * node,ImGuiWindow * host_window)13900 static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window)
13901 {
13902     ImGuiContext& g = *GImGui;
13903     ImGuiStyle& style = g.Style;
13904 
13905     const bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount);
13906     const bool closed_all = node->WantCloseAll && node_was_active;
13907     const ImGuiID closed_one = node->WantCloseTabId && node_was_active;
13908     node->WantCloseAll = false;
13909     node->WantCloseTabId = 0;
13910 
13911     // Decide if we should use a focused title bar color
13912     bool is_focused = false;
13913     ImGuiDockNode* root_node = DockNodeGetRootNode(node);
13914     if (g.NavWindowingTarget)
13915         is_focused = (g.NavWindowingTarget->DockNode == node);
13916     else if (g.NavWindow && g.NavWindow->RootWindowForTitleBarHighlight == host_window->RootWindowDockTree && root_node->LastFocusedNodeId == node->ID)
13917         is_focused = true;
13918 
13919     // Hidden tab bar will show a triangle on the upper-left (in Begin)
13920     if (node->IsHiddenTabBar() || node->IsNoTabBar())
13921     {
13922         node->VisibleWindow = (node->Windows.Size > 0) ? node->Windows[0] : NULL;
13923         node->IsFocused = is_focused;
13924         if (is_focused)
13925             node->LastFrameFocused = g.FrameCount;
13926         if (node->VisibleWindow)
13927         {
13928             // Notify root of visible window (used to display title in OS task bar)
13929             if (is_focused || root_node->VisibleWindow == NULL)
13930                 root_node->VisibleWindow = node->VisibleWindow;
13931             if (node->TabBar)
13932                 node->TabBar->VisibleTabId = node->VisibleWindow->ID;
13933         }
13934         return;
13935     }
13936 
13937     // 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
13938     bool backup_skip_item = host_window->SkipItems;
13939     if (!node->IsDockSpace())
13940     {
13941         host_window->SkipItems = false;
13942         host_window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
13943     }
13944 
13945     // Use PushOverrideID() instead of PushID() to use the node id _without_ the host window ID.
13946     // 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,
13947     // as docked windows themselves will override the stack with their own root ID.
13948     PushOverrideID(node->ID);
13949     ImGuiTabBar* tab_bar = node->TabBar;
13950     bool tab_bar_is_recreated = (tab_bar == NULL); // Tab bar are automatically destroyed when a node gets hidden
13951     if (tab_bar == NULL)
13952     {
13953         DockNodeAddTabBar(node);
13954         tab_bar = node->TabBar;
13955     }
13956 
13957     ImGuiID focus_tab_id = 0;
13958     node->IsFocused = is_focused;
13959 
13960     const ImGuiDockNodeFlags node_flags = node->MergedFlags;
13961     const bool has_window_menu_button = (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0 && (style.WindowMenuButtonPosition != ImGuiDir_None);
13962 
13963     // In a dock node, the Collapse Button turns into the Window Menu button.
13964     // FIXME-DOCK FIXME-OPT: Could we recycle popups id across multiple dock nodes?
13965     if (has_window_menu_button && IsPopupOpen("#WindowMenu"))
13966     {
13967         if (ImGuiID tab_id = DockNodeUpdateWindowMenu(node, tab_bar))
13968             focus_tab_id = tab_bar->NextSelectedTabId = tab_id;
13969         is_focused |= node->IsFocused;
13970     }
13971 
13972     // Layout
13973     ImRect title_bar_rect, tab_bar_rect;
13974     ImVec2 window_menu_button_pos;
13975     ImVec2 close_button_pos;
13976     DockNodeCalcTabBarLayout(node, &title_bar_rect, &tab_bar_rect, &window_menu_button_pos, &close_button_pos);
13977 
13978     // Submit new tabs, they will be added as Unsorted and sorted below based on relative DockOrder value.
13979     const int tabs_count_old = tab_bar->Tabs.Size;
13980     for (int window_n = 0; window_n < node->Windows.Size; window_n++)
13981     {
13982         ImGuiWindow* window = node->Windows[window_n];
13983         if (TabBarFindTabByID(tab_bar, window->ID) == NULL)
13984             TabBarAddTab(tab_bar, ImGuiTabItemFlags_Unsorted, window);
13985     }
13986 
13987     // Title bar
13988     if (is_focused)
13989         node->LastFrameFocused = g.FrameCount;
13990     ImU32 title_bar_col = GetColorU32(host_window->Collapsed ? ImGuiCol_TitleBgCollapsed : is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
13991     host_window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window->WindowRounding, ImDrawFlags_RoundCornersTop);
13992 
13993     // Docking/Collapse button
13994     if (has_window_menu_button)
13995     {
13996         if (CollapseButton(host_window->GetID("#COLLAPSE"), window_menu_button_pos, node)) // == DockNodeGetWindowMenuButtonId(node)
13997             OpenPopup("#WindowMenu");
13998         if (IsItemActive())
13999             focus_tab_id = tab_bar->SelectedTabId;
14000     }
14001 
14002     // If multiple tabs are appearing on the same frame, sort them based on their persistent DockOrder value
14003     int tabs_unsorted_start = tab_bar->Tabs.Size;
14004     for (int tab_n = tab_bar->Tabs.Size - 1; tab_n >= 0 && (tab_bar->Tabs[tab_n].Flags & ImGuiTabItemFlags_Unsorted); tab_n--)
14005     {
14006         // 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?
14007         tab_bar->Tabs[tab_n].Flags &= ~ImGuiTabItemFlags_Unsorted;
14008         tabs_unsorted_start = tab_n;
14009     }
14010     if (tab_bar->Tabs.Size > tabs_unsorted_start)
14011     {
14012         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)" : "");
14013         for (int tab_n = tabs_unsorted_start; tab_n < tab_bar->Tabs.Size; tab_n++)
14014             IMGUI_DEBUG_LOG_DOCKING(" - Tab '%s' Order %d\n", tab_bar->Tabs[tab_n].Window->Name, tab_bar->Tabs[tab_n].Window->DockOrder);
14015         if (tab_bar->Tabs.Size > tabs_unsorted_start + 1)
14016             ImQsort(tab_bar->Tabs.Data + tabs_unsorted_start, tab_bar->Tabs.Size - tabs_unsorted_start, sizeof(ImGuiTabItem), TabItemComparerByDockOrder);
14017     }
14018 
14019     // Apply NavWindow focus back to the tab bar
14020     if (g.NavWindow && g.NavWindow->RootWindow->DockNode == node)
14021         tab_bar->SelectedTabId = g.NavWindow->RootWindow->ID;
14022 
14023     // Selected newly added tabs, or persistent tab ID if the tab bar was just recreated
14024     if (tab_bar_is_recreated && TabBarFindTabByID(tab_bar, node->SelectedTabId) != NULL)
14025         tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = node->SelectedTabId;
14026     else if (tab_bar->Tabs.Size > tabs_count_old)
14027         tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = tab_bar->Tabs.back().Window->ID;
14028 
14029     // Begin tab bar
14030     ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_AutoSelectNewTabs; // | ImGuiTabBarFlags_NoTabListScrollingButtons);
14031     tab_bar_flags |= ImGuiTabBarFlags_SaveSettings | ImGuiTabBarFlags_DockNode;
14032     if (!host_window->Collapsed && is_focused)
14033         tab_bar_flags |= ImGuiTabBarFlags_IsFocused;
14034     BeginTabBarEx(tab_bar, tab_bar_rect, tab_bar_flags, node);
14035     //host_window->DrawList->AddRect(tab_bar_rect.Min, tab_bar_rect.Max, IM_COL32(255,0,255,255));
14036 
14037     // Backup style colors
14038     ImVec4 backup_style_cols[ImGuiWindowDockStyleCol_COUNT];
14039     for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
14040         backup_style_cols[color_n] = g.Style.Colors[GWindowDockStyleColors[color_n]];
14041 
14042     // Submit actual tabs
14043     node->VisibleWindow = NULL;
14044     for (int window_n = 0; window_n < node->Windows.Size; window_n++)
14045     {
14046         ImGuiWindow* window = node->Windows[window_n];
14047         if ((closed_all || closed_one == window->ID) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument))
14048             continue;
14049         if (window->LastFrameActive + 1 >= g.FrameCount || !node_was_active)
14050         {
14051             ImGuiTabItemFlags tab_item_flags = 0;
14052             tab_item_flags |= window->WindowClass.TabItemFlagsOverrideSet;
14053             if (window->Flags & ImGuiWindowFlags_UnsavedDocument)
14054                 tab_item_flags |= ImGuiTabItemFlags_UnsavedDocument;
14055             if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)
14056                 tab_item_flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton;
14057 
14058             // Apply stored style overrides for the window
14059             for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
14060                 g.Style.Colors[GWindowDockStyleColors[color_n]] = ColorConvertU32ToFloat4(window->DockStyle.Colors[color_n]);
14061 
14062             bool tab_open = true;
14063             TabItemEx(tab_bar, window->Name, window->HasCloseButton ? &tab_open : NULL, tab_item_flags, window);
14064             if (!tab_open)
14065                 node->WantCloseTabId = window->ID;
14066             if (tab_bar->VisibleTabId == window->ID)
14067                 node->VisibleWindow = window;
14068 
14069             // Store last item data so it can be queried with IsItemXXX functions after the user Begin() call
14070             window->DockTabItemStatusFlags = host_window->DC.LastItemStatusFlags;
14071             window->DockTabItemRect = host_window->DC.LastItemRect;
14072 
14073             // Update navigation ID on menu layer
14074             if (g.NavWindow && g.NavWindow->RootWindow == window && (window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0)
14075                 host_window->NavLastIds[1] = window->ID;
14076         }
14077     }
14078 
14079     // Restore style colors
14080     for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
14081         g.Style.Colors[GWindowDockStyleColors[color_n]] = backup_style_cols[color_n];
14082 
14083     // Notify root of visible window (used to display title in OS task bar)
14084     if (node->VisibleWindow)
14085         if (is_focused || root_node->VisibleWindow == NULL)
14086             root_node->VisibleWindow = node->VisibleWindow;
14087 
14088     // Close button (after VisibleWindow was updated)
14089     // Note that VisibleWindow may have been overrided by CTRL+Tabbing, so VisibleWindow->ID may be != from tab_bar->SelectedTabId
14090     const bool close_button_is_enabled = node->HasCloseButton && node->VisibleWindow && node->VisibleWindow->HasCloseButton;
14091     const bool close_button_is_visible = node->HasCloseButton;
14092     //const bool close_button_is_visible = close_button_is_enabled; // Most people would expect this behavior of not even showing the button (leaving a hole since we can't claim that space as other windows in the tba bar have one)
14093     if (close_button_is_visible)
14094     {
14095         if (!close_button_is_enabled)
14096         {
14097             PushItemFlag(ImGuiItemFlags_Disabled, true);
14098             PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_Text] * ImVec4(1.0f,1.0f,1.0f,0.4f));
14099         }
14100         if (CloseButton(host_window->GetID("#CLOSE"), close_button_pos))
14101         {
14102             node->WantCloseAll = true;
14103             for (int n = 0; n < tab_bar->Tabs.Size; n++)
14104                 TabBarCloseTab(tab_bar, &tab_bar->Tabs[n]);
14105         }
14106         //if (IsItemActive())
14107         //    focus_tab_id = tab_bar->SelectedTabId;
14108         if (!close_button_is_enabled)
14109         {
14110             PopStyleColor();
14111             PopItemFlag();
14112         }
14113     }
14114 
14115     // When clicking on the title bar outside of tabs, we still focus the selected tab for that node
14116     // FIXME: TabItem use AllowItemOverlap so we manually perform a more specific test for now (hovered || held)
14117     ImGuiID title_bar_id = host_window->GetID("#TITLEBAR");
14118     if (g.HoveredId == 0 || g.HoveredId == title_bar_id || g.ActiveId == title_bar_id)
14119     {
14120         bool held;
14121         ButtonBehavior(title_bar_rect, title_bar_id, NULL, &held, ImGuiButtonFlags_AllowItemOverlap);
14122         if (g.HoveredId == title_bar_id)
14123         {
14124             // ImGuiButtonFlags_AllowItemOverlap + SetItemAllowOverlap() required for appending into dock node tab bar,
14125             // otherwise dragging window will steal HoveredId and amended tabs cannot get them.
14126             host_window->DC.LastItemId = title_bar_id;
14127             SetItemAllowOverlap();
14128         }
14129         if (held)
14130         {
14131             if (IsMouseClicked(0))
14132                 focus_tab_id = tab_bar->SelectedTabId;
14133 
14134             // Forward moving request to selected window
14135             if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId))
14136                 StartMouseMovingWindowOrNode(tab->Window ? tab->Window : node->HostWindow, node, false);
14137         }
14138     }
14139 
14140     // Forward focus from host node to selected window
14141     //if (is_focused && g.NavWindow == host_window && !g.NavWindowingTarget)
14142     //    focus_tab_id = tab_bar->SelectedTabId;
14143 
14144     // When clicked on a tab we requested focus to the docked child
14145     // This overrides the value set by "forward focus from host node to selected window".
14146     if (tab_bar->NextSelectedTabId)
14147         focus_tab_id = tab_bar->NextSelectedTabId;
14148 
14149     // Apply navigation focus
14150     if (focus_tab_id != 0)
14151         if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, focus_tab_id))
14152             if (tab->Window)
14153             {
14154                 FocusWindow(tab->Window);
14155                 NavInitWindow(tab->Window, false);
14156             }
14157 
14158     EndTabBar();
14159     PopID();
14160 
14161     // Restore SkipItems flag
14162     if (!node->IsDockSpace())
14163     {
14164         host_window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
14165         host_window->SkipItems = backup_skip_item;
14166     }
14167 }
14168 
DockNodeAddTabBar(ImGuiDockNode * node)14169 static void ImGui::DockNodeAddTabBar(ImGuiDockNode* node)
14170 {
14171     IM_ASSERT(node->TabBar == NULL);
14172     node->TabBar = IM_NEW(ImGuiTabBar);
14173 }
14174 
DockNodeRemoveTabBar(ImGuiDockNode * node)14175 static void ImGui::DockNodeRemoveTabBar(ImGuiDockNode* node)
14176 {
14177     if (node->TabBar == NULL)
14178         return;
14179     IM_DELETE(node->TabBar);
14180     node->TabBar = NULL;
14181 }
14182 
DockNodeIsDropAllowedOne(ImGuiWindow * payload,ImGuiWindow * host_window)14183 static bool DockNodeIsDropAllowedOne(ImGuiWindow* payload, ImGuiWindow* host_window)
14184 {
14185     if (host_window->DockNodeAsHost && host_window->DockNodeAsHost->IsDockSpace() && payload->BeginOrderWithinContext < host_window->BeginOrderWithinContext)
14186         return false;
14187 
14188     ImGuiWindowClass* host_class = host_window->DockNodeAsHost ? &host_window->DockNodeAsHost->WindowClass : &host_window->WindowClass;
14189     ImGuiWindowClass* payload_class = &payload->WindowClass;
14190     if (host_class->ClassId != payload_class->ClassId)
14191     {
14192         if (host_class->ClassId != 0 && host_class->DockingAllowUnclassed && payload_class->ClassId == 0)
14193             return true;
14194         if (payload_class->ClassId != 0 && payload_class->DockingAllowUnclassed && host_class->ClassId == 0)
14195             return true;
14196         return false;
14197     }
14198 
14199     return true;
14200 }
14201 
DockNodeIsDropAllowed(ImGuiWindow * host_window,ImGuiWindow * root_payload)14202 static bool ImGui::DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* root_payload)
14203 {
14204     if (root_payload->DockNodeAsHost && root_payload->DockNodeAsHost->IsSplitNode())
14205         return true;
14206 
14207     const int payload_count = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows.Size : 1;
14208     for (int payload_n = 0; payload_n < payload_count; payload_n++)
14209     {
14210         ImGuiWindow* payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows[payload_n] : root_payload;
14211         if (DockNodeIsDropAllowedOne(payload, host_window))
14212             return true;
14213     }
14214     return false;
14215 }
14216 
14217 // window menu button == collapse button when not in a dock node.
14218 // 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,ImVec2 * out_close_button_pos)14219 static void ImGui::DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos, ImVec2* out_close_button_pos)
14220 {
14221     ImGuiContext& g = *GImGui;
14222     ImGuiStyle& style = g.Style;
14223 
14224     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);
14225     if (out_title_rect) { *out_title_rect = r; }
14226 
14227     r.Min.x += style.WindowBorderSize;
14228     r.Max.x -= style.WindowBorderSize;
14229 
14230     float button_sz = g.FontSize;
14231 
14232     ImVec2 window_menu_button_pos = r.Min;
14233     r.Min.x += style.FramePadding.x;
14234     r.Max.x -= style.FramePadding.x;
14235     if (node->HasCloseButton)
14236     {
14237         r.Max.x -= button_sz;
14238         if (out_close_button_pos) *out_close_button_pos = ImVec2(r.Max.x - style.FramePadding.x, r.Min.y);
14239     }
14240     if (node->HasWindowMenuButton && style.WindowMenuButtonPosition == ImGuiDir_Left)
14241     {
14242         r.Min.x += button_sz + style.ItemInnerSpacing.x;
14243     }
14244     else if (node->HasWindowMenuButton && style.WindowMenuButtonPosition == ImGuiDir_Right)
14245     {
14246         r.Max.x -= button_sz + style.FramePadding.x;
14247         window_menu_button_pos = ImVec2(r.Max.x, r.Min.y);
14248     }
14249     if (out_tab_bar_rect) { *out_tab_bar_rect = r; }
14250     if (out_window_menu_button_pos) { *out_window_menu_button_pos = window_menu_button_pos; }
14251 }
14252 
DockNodeCalcSplitRects(ImVec2 & pos_old,ImVec2 & size_old,ImVec2 & pos_new,ImVec2 & size_new,ImGuiDir dir,ImVec2 size_new_desired)14253 void ImGui::DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired)
14254 {
14255     ImGuiContext& g = *GImGui;
14256     const float dock_spacing = g.Style.ItemInnerSpacing.x;
14257     const ImGuiAxis axis = (dir == ImGuiDir_Left || dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
14258     pos_new[axis ^ 1] = pos_old[axis ^ 1];
14259     size_new[axis ^ 1] = size_old[axis ^ 1];
14260 
14261     // Distribute size on given axis (with a desired size or equally)
14262     const float w_avail = size_old[axis] - dock_spacing;
14263     if (size_new_desired[axis] > 0.0f && size_new_desired[axis] <= w_avail * 0.5f)
14264     {
14265         size_new[axis] = size_new_desired[axis];
14266         size_old[axis] = IM_FLOOR(w_avail - size_new[axis]);
14267     }
14268     else
14269     {
14270         size_new[axis] = IM_FLOOR(w_avail * 0.5f);
14271         size_old[axis] = IM_FLOOR(w_avail - size_new[axis]);
14272     }
14273 
14274     // Position each node
14275     if (dir == ImGuiDir_Right || dir == ImGuiDir_Down)
14276     {
14277         pos_new[axis] = pos_old[axis] + size_old[axis] + dock_spacing;
14278     }
14279     else if (dir == ImGuiDir_Left || dir == ImGuiDir_Up)
14280     {
14281         pos_new[axis] = pos_old[axis];
14282         pos_old[axis] = pos_new[axis] + size_new[axis] + dock_spacing;
14283     }
14284 }
14285 
14286 // 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)14287 bool ImGui::DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_r, bool outer_docking, ImVec2* test_mouse_pos)
14288 {
14289     ImGuiContext& g = *GImGui;
14290 
14291     const float parent_smaller_axis = ImMin(parent.GetWidth(), parent.GetHeight());
14292     const float hs_for_central_nodes = ImMin(g.FontSize * 1.5f, ImMax(g.FontSize * 0.5f, parent_smaller_axis / 8.0f));
14293     float hs_w; // Half-size, longer axis
14294     float hs_h; // Half-size, smaller axis
14295     ImVec2 off; // Distance from edge or center
14296     if (outer_docking)
14297     {
14298         //hs_w = ImFloor(ImClamp(parent_smaller_axis - hs_for_central_nodes * 4.0f, g.FontSize * 0.5f, g.FontSize * 8.0f));
14299         //hs_h = ImFloor(hs_w * 0.15f);
14300         //off = ImVec2(ImFloor(parent.GetWidth() * 0.5f - GetFrameHeightWithSpacing() * 1.4f - hs_h), ImFloor(parent.GetHeight() * 0.5f - GetFrameHeightWithSpacing() * 1.4f - hs_h));
14301         hs_w = ImFloor(hs_for_central_nodes * 1.50f);
14302         hs_h = ImFloor(hs_for_central_nodes * 0.80f);
14303         off = ImVec2(ImFloor(parent.GetWidth() * 0.5f - hs_h), ImFloor(parent.GetHeight() * 0.5f - hs_h));
14304     }
14305     else
14306     {
14307         hs_w = ImFloor(hs_for_central_nodes);
14308         hs_h = ImFloor(hs_for_central_nodes * 0.90f);
14309         off = ImVec2(ImFloor(hs_w * 2.40f), ImFloor(hs_w * 2.40f));
14310     }
14311 
14312     ImVec2 c = ImFloor(parent.GetCenter());
14313     if      (dir == ImGuiDir_None)  { out_r = ImRect(c.x - hs_w, c.y - hs_w,         c.x + hs_w, c.y + hs_w);         }
14314     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); }
14315     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); }
14316     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); }
14317     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); }
14318 
14319     if (test_mouse_pos == NULL)
14320         return false;
14321 
14322     ImRect hit_r = out_r;
14323     if (!outer_docking)
14324     {
14325         // Custom hit testing for the 5-way selection, designed to reduce flickering when moving diagonally between sides
14326         hit_r.Expand(ImFloor(hs_w * 0.30f));
14327         ImVec2 mouse_delta = (*test_mouse_pos - c);
14328         float mouse_delta_len2 = ImLengthSqr(mouse_delta);
14329         float r_threshold_center = hs_w * 1.4f;
14330         float r_threshold_sides = hs_w * (1.4f + 1.2f);
14331         if (mouse_delta_len2 < r_threshold_center * r_threshold_center)
14332             return (dir == ImGuiDir_None);
14333         if (mouse_delta_len2 < r_threshold_sides * r_threshold_sides)
14334             return (dir == ImGetDirQuadrantFromDelta(mouse_delta.x, mouse_delta.y));
14335     }
14336     return hit_r.Contains(*test_mouse_pos);
14337 }
14338 
14339 // host_node may be NULL if the window doesn't have a DockNode already.
14340 // 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)14341 static void ImGui::DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, ImGuiDockPreviewData* data, bool is_explicit_target, bool is_outer_docking)
14342 {
14343     ImGuiContext& g = *GImGui;
14344 
14345     // There is an edge case when docking into a dockspace which only has inactive nodes.
14346     // In this case DockNodeTreeFindNodeByPos() will have selected a leaf node which is inactive.
14347     // Because the inactive leaf node doesn't have proper pos/size yet, we'll use the root node as reference.
14348     ImGuiDockNode* root_payload_as_host = root_payload->DockNodeAsHost;
14349     ImGuiDockNode* ref_node_for_rect = (host_node && !host_node->IsVisible) ? DockNodeGetRootNode(host_node) : host_node;
14350     if (ref_node_for_rect)
14351         IM_ASSERT(ref_node_for_rect->IsVisible);
14352 
14353     // Filter, figure out where we are allowed to dock
14354     ImGuiDockNodeFlags src_node_flags = root_payload_as_host ? root_payload_as_host->MergedFlags : root_payload->WindowClass.DockNodeFlagsOverrideSet;
14355     ImGuiDockNodeFlags dst_node_flags = host_node ? host_node->MergedFlags : host_window->WindowClass.DockNodeFlagsOverrideSet;
14356     data->IsCenterAvailable = true;
14357     if (is_outer_docking)
14358         data->IsCenterAvailable = false;
14359     else if (dst_node_flags & ImGuiDockNodeFlags_NoDocking)
14360         data->IsCenterAvailable = false;
14361     else if (host_node && (dst_node_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) && host_node->IsCentralNode())
14362         data->IsCenterAvailable = false;
14363     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?
14364         data->IsCenterAvailable = false;
14365     else if (dst_node_flags & ImGuiDockNodeFlags_NoDockingOverMe)
14366         data->IsCenterAvailable = false;
14367     else if ((src_node_flags & ImGuiDockNodeFlags_NoDockingOverOther) && (!host_node || !host_node->IsEmpty()))
14368         data->IsCenterAvailable = false;
14369     else if ((src_node_flags & ImGuiDockNodeFlags_NoDockingOverEmpty) && host_node && host_node->IsEmpty())
14370         data->IsCenterAvailable = false;
14371 
14372     data->IsSidesAvailable = true;
14373     if ((dst_node_flags & ImGuiDockNodeFlags_NoSplit) || g.IO.ConfigDockingNoSplit)
14374         data->IsSidesAvailable = false;
14375     else if (!is_outer_docking && host_node && host_node->ParentNode == NULL && host_node->IsCentralNode())
14376         data->IsSidesAvailable = false;
14377     else if ((dst_node_flags & ImGuiDockNodeFlags_NoDockingSplitMe) || (src_node_flags & ImGuiDockNodeFlags_NoDockingSplitOther))
14378         data->IsSidesAvailable = false;
14379 
14380     // Build a tentative future node (reuse same structure because it is practical. Shape will be readjusted when previewing a split)
14381     data->FutureNode.HasCloseButton = (host_node ? host_node->HasCloseButton : host_window->HasCloseButton) || (root_payload->HasCloseButton);
14382     data->FutureNode.HasWindowMenuButton = host_node ? true : ((host_window->Flags & ImGuiWindowFlags_NoCollapse) == 0);
14383     data->FutureNode.Pos = ref_node_for_rect ? ref_node_for_rect->Pos : host_window->Pos;
14384     data->FutureNode.Size = ref_node_for_rect ? ref_node_for_rect->Size : host_window->Size;
14385 
14386     // Calculate drop shapes geometry for allowed splitting directions
14387     IM_ASSERT(ImGuiDir_None == -1);
14388     data->SplitNode = host_node;
14389     data->SplitDir = ImGuiDir_None;
14390     data->IsSplitDirExplicit = false;
14391     if (!host_window->Collapsed)
14392         for (int dir = ImGuiDir_None; dir < ImGuiDir_COUNT; dir++)
14393         {
14394             if (dir == ImGuiDir_None && !data->IsCenterAvailable)
14395                 continue;
14396             if (dir != ImGuiDir_None && !data->IsSidesAvailable)
14397                 continue;
14398             if (DockNodeCalcDropRectsAndTestMousePos(data->FutureNode.Rect(), (ImGuiDir)dir, data->DropRectsDraw[dir+1], is_outer_docking, &g.IO.MousePos))
14399             {
14400                 data->SplitDir = (ImGuiDir)dir;
14401                 data->IsSplitDirExplicit = true;
14402             }
14403         }
14404 
14405     // We only allow and preview docking when hovering over a drop rect or over the title bar
14406     data->IsDropAllowed = (data->SplitDir != ImGuiDir_None) || (data->IsCenterAvailable);
14407     if (!is_explicit_target && !data->IsSplitDirExplicit)
14408         data->IsDropAllowed = false;
14409 
14410     // Calculate split area
14411     data->SplitRatio = 0.0f;
14412     if (data->SplitDir != ImGuiDir_None)
14413     {
14414         ImGuiDir split_dir = data->SplitDir;
14415         ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
14416         ImVec2 pos_new, pos_old = data->FutureNode.Pos;
14417         ImVec2 size_new, size_old = data->FutureNode.Size;
14418         DockNodeCalcSplitRects(pos_old, size_old, pos_new, size_new, split_dir, root_payload->Size);
14419 
14420         // Calculate split ratio so we can pass it down the docking request
14421         float split_ratio = ImSaturate(size_new[split_axis] / data->FutureNode.Size[split_axis]);
14422         data->FutureNode.Pos = pos_new;
14423         data->FutureNode.Size = size_new;
14424         data->SplitRatio = (split_dir == ImGuiDir_Right || split_dir == ImGuiDir_Down) ? (1.0f - split_ratio) : (split_ratio);
14425     }
14426 }
14427 
DockNodePreviewDockRender(ImGuiWindow * host_window,ImGuiDockNode * host_node,ImGuiWindow * root_payload,const ImGuiDockPreviewData * data)14428 static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, const ImGuiDockPreviewData* data)
14429 {
14430     ImGuiContext& g = *GImGui;
14431     IM_ASSERT(g.CurrentWindow == host_window);   // Because we rely on font size to calculate tab sizes
14432 
14433     // With this option, we only display the preview on the target viewport, and the payload viewport is made transparent.
14434     // To compensate for the single layer obstructed by the payload, we'll increase the alpha of the preview nodes.
14435     const bool is_transparent_payload = g.IO.ConfigDockingTransparentPayload;
14436 
14437     // In case the two windows involved are on different viewports, we will draw the overlay on each of them.
14438     int overlay_draw_lists_count = 0;
14439     ImDrawList* overlay_draw_lists[2];
14440     overlay_draw_lists[overlay_draw_lists_count++] = GetForegroundDrawList(host_window->Viewport);
14441     if (host_window->Viewport != root_payload->Viewport && !is_transparent_payload)
14442         overlay_draw_lists[overlay_draw_lists_count++] = GetForegroundDrawList(root_payload->Viewport);
14443 
14444     // Draw main preview rectangle
14445     const ImU32 overlay_col_main = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.60f : 0.40f);
14446     const ImU32 overlay_col_drop = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.90f : 0.70f);
14447     const ImU32 overlay_col_drop_hovered = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 1.20f : 1.00f);
14448     const ImU32 overlay_col_lines = GetColorU32(ImGuiCol_NavWindowingHighlight, is_transparent_payload ? 0.80f : 0.60f);
14449 
14450     // Display area preview
14451     const bool can_preview_tabs = (root_payload->DockNodeAsHost == NULL || root_payload->DockNodeAsHost->Windows.Size > 0);
14452     if (data->IsDropAllowed)
14453     {
14454         ImRect overlay_rect = data->FutureNode.Rect();
14455         if (data->SplitDir == ImGuiDir_None && can_preview_tabs)
14456             overlay_rect.Min.y += GetFrameHeight();
14457         if (data->SplitDir != ImGuiDir_None || data->IsCenterAvailable)
14458             for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++)
14459                 overlay_draw_lists[overlay_n]->AddRectFilled(overlay_rect.Min, overlay_rect.Max, overlay_col_main, host_window->WindowRounding);
14460     }
14461 
14462     // Display tab shape/label preview unless we are splitting node (it generally makes the situation harder to read)
14463     if (data->IsDropAllowed && can_preview_tabs && data->SplitDir == ImGuiDir_None && data->IsCenterAvailable)
14464     {
14465         // Compute target tab bar geometry so we can locate our preview tabs
14466         ImRect tab_bar_rect;
14467         DockNodeCalcTabBarLayout(&data->FutureNode, NULL, &tab_bar_rect, NULL, NULL);
14468         ImVec2 tab_pos = tab_bar_rect.Min;
14469         if (host_node && host_node->TabBar)
14470         {
14471             if (!host_node->IsHiddenTabBar() && !host_node->IsNoTabBar())
14472                 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.
14473             else
14474                 tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_node->Windows[0]->Name, host_node->Windows[0]->HasCloseButton).x;
14475         }
14476         else if (!(host_window->Flags & ImGuiWindowFlags_DockNodeHost))
14477         {
14478             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
14479         }
14480 
14481         // Draw tab shape/label preview (payload may be a loose window or a host window carrying multiple tabbed windows)
14482         if (root_payload->DockNodeAsHost)
14483             IM_ASSERT(root_payload->DockNodeAsHost->Windows.Size <= root_payload->DockNodeAsHost->TabBar->Tabs.Size);
14484         ImGuiTabBar* tab_bar_with_payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->TabBar : NULL;
14485         const int payload_count = tab_bar_with_payload ? tab_bar_with_payload->Tabs.Size : 1;
14486         for (int payload_n = 0; payload_n < payload_count; payload_n++)
14487         {
14488             // DockNode's TabBar may have non-window Tabs manually appended by user
14489             ImGuiWindow* payload_window = tab_bar_with_payload ? tab_bar_with_payload->Tabs[payload_n].Window : root_payload;
14490             if (tab_bar_with_payload && payload_window == NULL)
14491                 continue;
14492             if (!DockNodeIsDropAllowedOne(payload_window, host_window))
14493                 continue;
14494 
14495             // Calculate the tab bounding box for each payload window
14496             ImVec2 tab_size = TabItemCalcSize(payload_window->Name, payload_window->HasCloseButton);
14497             ImRect tab_bb(tab_pos.x, tab_pos.y, tab_pos.x + tab_size.x, tab_pos.y + tab_size.y);
14498             tab_pos.x += tab_size.x + g.Style.ItemInnerSpacing.x;
14499             const ImU32 overlay_col_text = GetColorU32(payload_window->DockStyle.Colors[ImGuiWindowDockStyleCol_Text]);
14500             const ImU32 overlay_col_tabs = GetColorU32(payload_window->DockStyle.Colors[ImGuiWindowDockStyleCol_TabActive]);
14501             PushStyleColor(ImGuiCol_Text, overlay_col_text);
14502             for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++)
14503             {
14504                 ImGuiTabItemFlags tab_flags = ImGuiTabItemFlags_Preview | ((payload_window->Flags & ImGuiWindowFlags_UnsavedDocument) ? ImGuiTabItemFlags_UnsavedDocument : 0);
14505                 if (!tab_bar_rect.Contains(tab_bb))
14506                     overlay_draw_lists[overlay_n]->PushClipRect(tab_bar_rect.Min, tab_bar_rect.Max);
14507                 TabItemBackground(overlay_draw_lists[overlay_n], tab_bb, tab_flags, overlay_col_tabs);
14508                 TabItemLabelAndCloseButton(overlay_draw_lists[overlay_n], tab_bb, tab_flags, g.Style.FramePadding, payload_window->Name, 0, 0, false, NULL, NULL);
14509                 if (!tab_bar_rect.Contains(tab_bb))
14510                     overlay_draw_lists[overlay_n]->PopClipRect();
14511             }
14512             PopStyleColor();
14513         }
14514     }
14515 
14516     // Display drop boxes
14517     const float overlay_rounding = ImMax(3.0f, g.Style.FrameRounding);
14518     for (int dir = ImGuiDir_None; dir < ImGuiDir_COUNT; dir++)
14519     {
14520         if (!data->DropRectsDraw[dir + 1].IsInverted())
14521         {
14522             ImRect draw_r = data->DropRectsDraw[dir + 1];
14523             ImRect draw_r_in = draw_r;
14524             draw_r_in.Expand(-2.0f);
14525             ImU32 overlay_col = (data->SplitDir == (ImGuiDir)dir && data->IsSplitDirExplicit) ? overlay_col_drop_hovered : overlay_col_drop;
14526             for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++)
14527             {
14528                 ImVec2 center = ImFloor(draw_r_in.GetCenter());
14529                 overlay_draw_lists[overlay_n]->AddRectFilled(draw_r.Min, draw_r.Max, overlay_col, overlay_rounding);
14530                 overlay_draw_lists[overlay_n]->AddRect(draw_r_in.Min, draw_r_in.Max, overlay_col_lines, overlay_rounding);
14531                 if (dir == ImGuiDir_Left || dir == ImGuiDir_Right)
14532                     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);
14533                 if (dir == ImGuiDir_Up || dir == ImGuiDir_Down)
14534                     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);
14535             }
14536         }
14537 
14538         // Stop after ImGuiDir_None
14539         if ((host_node && (host_node->MergedFlags & ImGuiDockNodeFlags_NoSplit)) || g.IO.ConfigDockingNoSplit)
14540             return;
14541     }
14542 }
14543 
14544 //-----------------------------------------------------------------------------
14545 // Docking: ImGuiDockNode Tree manipulation functions
14546 //-----------------------------------------------------------------------------
14547 // - DockNodeTreeSplit()
14548 // - DockNodeTreeMerge()
14549 // - DockNodeTreeUpdatePosSize()
14550 // - DockNodeTreeUpdateSplitterFindTouchingNode()
14551 // - DockNodeTreeUpdateSplitter()
14552 // - DockNodeTreeFindFallbackLeafNode()
14553 // - DockNodeTreeFindNodeByPos()
14554 //-----------------------------------------------------------------------------
14555 
DockNodeTreeSplit(ImGuiContext * ctx,ImGuiDockNode * parent_node,ImGuiAxis split_axis,int split_inheritor_child_idx,float split_ratio,ImGuiDockNode * new_node)14556 void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_inheritor_child_idx, float split_ratio, ImGuiDockNode* new_node)
14557 {
14558     ImGuiContext& g = *GImGui;
14559     IM_ASSERT(split_axis != ImGuiAxis_None);
14560 
14561     ImGuiDockNode* child_0 = (new_node && split_inheritor_child_idx != 0) ? new_node : DockContextAddNode(ctx, 0);
14562     child_0->ParentNode = parent_node;
14563 
14564     ImGuiDockNode* child_1 = (new_node && split_inheritor_child_idx != 1) ? new_node : DockContextAddNode(ctx, 0);
14565     child_1->ParentNode = parent_node;
14566 
14567     ImGuiDockNode* child_inheritor = (split_inheritor_child_idx == 0) ? child_0 : child_1;
14568     DockNodeMoveChildNodes(child_inheritor, parent_node);
14569     parent_node->ChildNodes[0] = child_0;
14570     parent_node->ChildNodes[1] = child_1;
14571     parent_node->ChildNodes[split_inheritor_child_idx]->VisibleWindow = parent_node->VisibleWindow;
14572     parent_node->SplitAxis = split_axis;
14573     parent_node->VisibleWindow = NULL;
14574     parent_node->AuthorityForPos = parent_node->AuthorityForSize = ImGuiDataAuthority_DockNode;
14575 
14576     float size_avail = (parent_node->Size[split_axis] - DOCKING_SPLITTER_SIZE);
14577     size_avail = ImMax(size_avail, g.Style.WindowMinSize[split_axis] * 2.0f);
14578     IM_ASSERT(size_avail > 0.0f); // If you created a node manually with DockBuilderAddNode(), you need to also call DockBuilderSetNodeSize() before splitting.
14579     child_0->SizeRef = child_1->SizeRef = parent_node->Size;
14580     child_0->SizeRef[split_axis] = ImFloor(size_avail * split_ratio);
14581     child_1->SizeRef[split_axis] = ImFloor(size_avail - child_0->SizeRef[split_axis]);
14582 
14583     DockNodeMoveWindows(parent_node->ChildNodes[split_inheritor_child_idx], parent_node);
14584     DockSettingsRenameNodeReferences(parent_node->ID, parent_node->ChildNodes[split_inheritor_child_idx]->ID);
14585     DockNodeTreeUpdatePosSize(parent_node, parent_node->Pos, parent_node->Size);
14586 
14587     // Flags transfer (e.g. this is where we transfer the ImGuiDockNodeFlags_CentralNode property)
14588     child_0->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_;
14589     child_1->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_;
14590     child_inheritor->LocalFlags = parent_node->LocalFlags & ImGuiDockNodeFlags_LocalFlagsTransferMask_;
14591     parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_;
14592     child_0->UpdateMergedFlags();
14593     child_1->UpdateMergedFlags();
14594     parent_node->UpdateMergedFlags();
14595     if (child_inheritor->IsCentralNode())
14596         DockNodeGetRootNode(parent_node)->CentralNode = child_inheritor;
14597 }
14598 
DockNodeTreeMerge(ImGuiContext * ctx,ImGuiDockNode * parent_node,ImGuiDockNode * merge_lead_child)14599 void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child)
14600 {
14601     // When called from DockContextProcessUndockNode() it is possible that one of the child is NULL.
14602     ImGuiDockNode* child_0 = parent_node->ChildNodes[0];
14603     ImGuiDockNode* child_1 = parent_node->ChildNodes[1];
14604     IM_ASSERT(child_0 || child_1);
14605     IM_ASSERT(merge_lead_child == child_0 || merge_lead_child == child_1);
14606     if ((child_0 && child_0->Windows.Size > 0) || (child_1 && child_1->Windows.Size > 0))
14607     {
14608         IM_ASSERT(parent_node->TabBar == NULL);
14609         IM_ASSERT(parent_node->Windows.Size == 0);
14610     }
14611     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);
14612 
14613     ImVec2 backup_last_explicit_size = parent_node->SizeRef;
14614     DockNodeMoveChildNodes(parent_node, merge_lead_child);
14615     if (child_0)
14616     {
14617         DockNodeMoveWindows(parent_node, child_0); // Generally only 1 of the 2 child node will have windows
14618         DockSettingsRenameNodeReferences(child_0->ID, parent_node->ID);
14619     }
14620     if (child_1)
14621     {
14622         DockNodeMoveWindows(parent_node, child_1);
14623         DockSettingsRenameNodeReferences(child_1->ID, parent_node->ID);
14624     }
14625     DockNodeApplyPosSizeToWindows(parent_node);
14626     parent_node->AuthorityForPos = parent_node->AuthorityForSize = parent_node->AuthorityForViewport = ImGuiDataAuthority_Auto;
14627     parent_node->VisibleWindow = merge_lead_child->VisibleWindow;
14628     parent_node->SizeRef = backup_last_explicit_size;
14629 
14630     // Flags transfer
14631     parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_; // Preserve Dockspace flag
14632     parent_node->LocalFlags |= (child_0 ? child_0->LocalFlags : 0) & ImGuiDockNodeFlags_LocalFlagsTransferMask_;
14633     parent_node->LocalFlags |= (child_1 ? child_1->LocalFlags : 0) & ImGuiDockNodeFlags_LocalFlagsTransferMask_;
14634     parent_node->LocalFlagsInWindows = (child_0 ? child_0->LocalFlagsInWindows : 0) | (child_1 ? child_1->LocalFlagsInWindows : 0); // FIXME: Would be more consistent to update from actual windows
14635     parent_node->UpdateMergedFlags();
14636 
14637     if (child_0)
14638     {
14639         ctx->DockContext.Nodes.SetVoidPtr(child_0->ID, NULL);
14640         IM_DELETE(child_0);
14641     }
14642     if (child_1)
14643     {
14644         ctx->DockContext.Nodes.SetVoidPtr(child_1->ID, NULL);
14645         IM_DELETE(child_1);
14646     }
14647 }
14648 
14649 // 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)14650 void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, bool only_write_to_marked_nodes)
14651 {
14652     // During the regular dock node update we write to all nodes.
14653     // 'only_write_to_marked_nodes' is only set when turning a node visible mid-frame and we need its size right-away.
14654     const bool write_to_node = (only_write_to_marked_nodes == false) || (node->MarkedForPosSizeWrite);
14655     if (write_to_node)
14656     {
14657         node->Pos = pos;
14658         node->Size = size;
14659     }
14660 
14661     if (node->IsLeafNode())
14662         return;
14663 
14664     ImGuiDockNode* child_0 = node->ChildNodes[0];
14665     ImGuiDockNode* child_1 = node->ChildNodes[1];
14666     ImVec2 child_0_pos = pos, child_1_pos = pos;
14667     ImVec2 child_0_size = size, child_1_size = size;
14668     if (child_0->IsVisible && child_1->IsVisible)
14669     {
14670         const float spacing = DOCKING_SPLITTER_SIZE;
14671         const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis;
14672         const float size_avail = ImMax(size[axis] - spacing, 0.0f);
14673 
14674         // Size allocation policy
14675         // 1) The first 0..WindowMinSize[axis]*2 are allocated evenly to both windows.
14676         ImGuiContext& g = *GImGui;
14677         const float size_min_each = ImFloor(ImMin(size_avail, g.Style.WindowMinSize[axis] * 2.0f) * 0.5f);
14678 
14679         // 2) Process locked absolute size (during a splitter resize we preserve the child of nodes not touching the splitter edge)
14680         if (child_0->WantLockSizeOnce && !child_1->WantLockSizeOnce)
14681         {
14682             child_0_size[axis] = child_0->SizeRef[axis] = ImMin(size_avail - 1.0f, child_0->Size[axis]);
14683             child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]);
14684             IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f);
14685         }
14686         else if (child_1->WantLockSizeOnce && !child_0->WantLockSizeOnce)
14687         {
14688             child_1_size[axis] = child_1->SizeRef[axis] = ImMin(size_avail - 1.0f, child_1->Size[axis]);
14689             child_0_size[axis] = child_0->SizeRef[axis] = (size_avail - child_1_size[axis]);
14690             IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f);
14691         }
14692         else if (child_0->WantLockSizeOnce && child_1->WantLockSizeOnce)
14693         {
14694             // FIXME-DOCK: We cannot honor the requested size, so apply ratio.
14695             // Currently this path will only be taken if code programmatically sets WantLockSizeOnce
14696             float ratio_0 = child_0_size[axis] / (child_0_size[axis] + child_1_size[axis]);
14697             child_0_size[axis] = child_0->SizeRef[axis] = ImFloor(size_avail * ratio_0);
14698             child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]);
14699             IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f);
14700         }
14701 
14702         // 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
14703         else if (child_1->IsCentralNode() && child_0->SizeRef[axis] != 0.0f)
14704         {
14705             child_0_size[axis] = ImMin(size_avail - size_min_each, child_0->SizeRef[axis]);
14706             child_1_size[axis] = (size_avail - child_0_size[axis]);
14707         }
14708         else if (child_0->IsCentralNode() && child_1->SizeRef[axis] != 0.0f)
14709         {
14710             child_1_size[axis] = ImMin(size_avail - size_min_each, child_1->SizeRef[axis]);
14711             child_0_size[axis] = (size_avail - child_1_size[axis]);
14712         }
14713         else
14714         {
14715             // 4) Otherwise distribute according to the relative ratio of each SizeRef value
14716             float split_ratio = child_0->SizeRef[axis] / (child_0->SizeRef[axis] + child_1->SizeRef[axis]);
14717             child_0_size[axis] = ImMax(size_min_each, ImFloor(size_avail * split_ratio + 0.5F));
14718             child_1_size[axis] = (size_avail - child_0_size[axis]);
14719         }
14720 
14721         child_1_pos[axis] += spacing + child_0_size[axis];
14722     }
14723     child_0->WantLockSizeOnce = child_1->WantLockSizeOnce = false;
14724 
14725     if (child_0->IsVisible)
14726         DockNodeTreeUpdatePosSize(child_0, child_0_pos, child_0_size);
14727     if (child_1->IsVisible)
14728         DockNodeTreeUpdatePosSize(child_1, child_1_pos, child_1_size);
14729 }
14730 
DockNodeTreeUpdateSplitterFindTouchingNode(ImGuiDockNode * node,ImGuiAxis axis,int side,ImVector<ImGuiDockNode * > * touching_nodes)14731 static void DockNodeTreeUpdateSplitterFindTouchingNode(ImGuiDockNode* node, ImGuiAxis axis, int side, ImVector<ImGuiDockNode*>* touching_nodes)
14732 {
14733     if (node->IsLeafNode())
14734     {
14735         touching_nodes->push_back(node);
14736         return;
14737     }
14738     if (node->ChildNodes[0]->IsVisible)
14739         if (node->SplitAxis != axis || side == 0 || !node->ChildNodes[1]->IsVisible)
14740             DockNodeTreeUpdateSplitterFindTouchingNode(node->ChildNodes[0], axis, side, touching_nodes);
14741     if (node->ChildNodes[1]->IsVisible)
14742         if (node->SplitAxis != axis || side == 1 || !node->ChildNodes[0]->IsVisible)
14743             DockNodeTreeUpdateSplitterFindTouchingNode(node->ChildNodes[1], axis, side, touching_nodes);
14744 }
14745 
DockNodeTreeUpdateSplitter(ImGuiDockNode * node)14746 void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node)
14747 {
14748     if (node->IsLeafNode())
14749         return;
14750 
14751     ImGuiContext& g = *GImGui;
14752 
14753     ImGuiDockNode* child_0 = node->ChildNodes[0];
14754     ImGuiDockNode* child_1 = node->ChildNodes[1];
14755     if (child_0->IsVisible && child_1->IsVisible)
14756     {
14757         // Bounding box of the splitter cover the space between both nodes (w = Spacing, h = Size[xy^1] for when splitting horizontally)
14758         const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis;
14759         IM_ASSERT(axis != ImGuiAxis_None);
14760         ImRect bb;
14761         bb.Min = child_0->Pos;
14762         bb.Max = child_1->Pos;
14763         bb.Min[axis] += child_0->Size[axis];
14764         bb.Max[axis ^ 1] += child_1->Size[axis ^ 1];
14765         //if (g.IO.KeyCtrl) GetForegroundDrawList(g.CurrentWindow->Viewport)->AddRect(bb.Min, bb.Max, IM_COL32(255,0,255,255));
14766 
14767         const ImGuiDockNodeFlags merged_flags = child_0->MergedFlags | child_1->MergedFlags; // Merged flags for BOTH childs
14768         const ImGuiDockNodeFlags no_resize_axis_flag = (axis == ImGuiAxis_X) ? ImGuiDockNodeFlags_NoResizeX : ImGuiDockNodeFlags_NoResizeY;
14769         if ((merged_flags & ImGuiDockNodeFlags_NoResize) || (merged_flags & no_resize_axis_flag))
14770         {
14771             ImGuiWindow* window = g.CurrentWindow;
14772             window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator), g.Style.FrameRounding);
14773         }
14774         else
14775         {
14776             //bb.Min[axis] += 1; // Display a little inward so highlight doesn't connect with nearby tabs on the neighbor node.
14777             //bb.Max[axis] -= 1;
14778             PushID(node->ID);
14779 
14780             // Gather list of nodes that are touching the splitter line. Find resizing limits based on those nodes.
14781             ImVector<ImGuiDockNode*> touching_nodes[2];
14782             float min_size = g.Style.WindowMinSize[axis];
14783             float resize_limits[2];
14784             resize_limits[0] = node->ChildNodes[0]->Pos[axis] + min_size;
14785             resize_limits[1] = node->ChildNodes[1]->Pos[axis] + node->ChildNodes[1]->Size[axis] - min_size;
14786 
14787             ImGuiID splitter_id = GetID("##Splitter");
14788             if (g.ActiveId == splitter_id)
14789             {
14790                 // Only process when splitter is active
14791                 DockNodeTreeUpdateSplitterFindTouchingNode(child_0, axis, 1, &touching_nodes[0]);
14792                 DockNodeTreeUpdateSplitterFindTouchingNode(child_1, axis, 0, &touching_nodes[1]);
14793                 for (int touching_node_n = 0; touching_node_n < touching_nodes[0].Size; touching_node_n++)
14794                     resize_limits[0] = ImMax(resize_limits[0], touching_nodes[0][touching_node_n]->Rect().Min[axis] + min_size);
14795                 for (int touching_node_n = 0; touching_node_n < touching_nodes[1].Size; touching_node_n++)
14796                     resize_limits[1] = ImMin(resize_limits[1], touching_nodes[1][touching_node_n]->Rect().Max[axis] - min_size);
14797 
14798                 /*
14799                 // [DEBUG] Render limits
14800                 ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport());
14801                 for (int n = 0; n < 2; n++)
14802                     if (axis == ImGuiAxis_X)
14803                         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);
14804                     else
14805                         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);
14806                 */
14807             }
14808 
14809             // Use a short delay before highlighting the splitter (and changing the mouse cursor) in order for regular mouse movement to not highlight many splitters
14810             float cur_size_0 = child_0->Size[axis];
14811             float cur_size_1 = child_1->Size[axis];
14812             float min_size_0 = resize_limits[0] - child_0->Pos[axis];
14813             float min_size_1 = child_1->Pos[axis] + child_1->Size[axis] - resize_limits[1];
14814             if (SplitterBehavior(bb, GetID("##Splitter"), axis, &cur_size_0, &cur_size_1, min_size_0, min_size_1, WINDOWS_HOVER_PADDING, WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER))
14815             {
14816                 if (touching_nodes[0].Size > 0 && touching_nodes[1].Size > 0)
14817                 {
14818                     child_0->Size[axis] = child_0->SizeRef[axis] = cur_size_0;
14819                     child_1->Pos[axis] -= cur_size_1 - child_1->Size[axis];
14820                     child_1->Size[axis] = child_1->SizeRef[axis] = cur_size_1;
14821 
14822                     // Lock the size of every node that is a sibling of the node we are touching
14823                     // This might be less desirable if we can merge sibling of a same axis into the same parental level.
14824                     for (int side_n = 0; side_n < 2; side_n++)
14825                         for (int touching_node_n = 0; touching_node_n < touching_nodes[side_n].Size; touching_node_n++)
14826                         {
14827                             ImGuiDockNode* touching_node = touching_nodes[side_n][touching_node_n];
14828                             //ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport());
14829                             //draw_list->AddRect(touching_node->Pos, touching_node->Pos + touching_node->Size, IM_COL32(255, 128, 0, 255));
14830                             while (touching_node->ParentNode != node)
14831                             {
14832                                 if (touching_node->ParentNode->SplitAxis == axis)
14833                                 {
14834                                     // Mark other node so its size will be preserved during the upcoming call to DockNodeTreeUpdatePosSize().
14835                                     ImGuiDockNode* node_to_preserve = touching_node->ParentNode->ChildNodes[side_n];
14836                                     node_to_preserve->WantLockSizeOnce = true;
14837                                     //draw_list->AddRect(touching_node->Pos, touching_node->Rect().Max, IM_COL32(255, 0, 0, 255));
14838                                     //draw_list->AddRectFilled(node_to_preserve->Pos, node_to_preserve->Rect().Max, IM_COL32(0, 255, 0, 100));
14839                                 }
14840                                 touching_node = touching_node->ParentNode;
14841                             }
14842                         }
14843 
14844                     DockNodeTreeUpdatePosSize(child_0, child_0->Pos, child_0->Size);
14845                     DockNodeTreeUpdatePosSize(child_1, child_1->Pos, child_1->Size);
14846                     MarkIniSettingsDirty();
14847                 }
14848             }
14849             PopID();
14850         }
14851     }
14852 
14853     if (child_0->IsVisible)
14854         DockNodeTreeUpdateSplitter(child_0);
14855     if (child_1->IsVisible)
14856         DockNodeTreeUpdateSplitter(child_1);
14857 }
14858 
DockNodeTreeFindFallbackLeafNode(ImGuiDockNode * node)14859 ImGuiDockNode* ImGui::DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node)
14860 {
14861     if (node->IsLeafNode())
14862         return node;
14863     if (ImGuiDockNode* leaf_node = DockNodeTreeFindFallbackLeafNode(node->ChildNodes[0]))
14864         return leaf_node;
14865     if (ImGuiDockNode* leaf_node = DockNodeTreeFindFallbackLeafNode(node->ChildNodes[1]))
14866         return leaf_node;
14867     return NULL;
14868 }
14869 
DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode * node,ImVec2 pos)14870 ImGuiDockNode* ImGui::DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos)
14871 {
14872     if (!node->IsVisible)
14873         return NULL;
14874 
14875     const float dock_spacing = 0.0f;// g.Style.ItemInnerSpacing.x; // FIXME: Relation to DOCKING_SPLITTER_SIZE?
14876     ImRect r(node->Pos, node->Pos + node->Size);
14877     r.Expand(dock_spacing * 0.5f);
14878     bool inside = r.Contains(pos);
14879     if (!inside)
14880         return NULL;
14881 
14882     if (node->IsLeafNode())
14883         return node;
14884     if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(node->ChildNodes[0], pos))
14885         return hovered_node;
14886     if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(node->ChildNodes[1], pos))
14887         return hovered_node;
14888 
14889     return NULL;
14890 }
14891 
14892 //-----------------------------------------------------------------------------
14893 // Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport)
14894 //-----------------------------------------------------------------------------
14895 // - SetWindowDock() [Internal]
14896 // - DockSpace()
14897 // - DockSpaceOverViewport()
14898 //-----------------------------------------------------------------------------
14899 
14900 // [Internal] Called via SetNextWindowDockID()
SetWindowDock(ImGuiWindow * window,ImGuiID dock_id,ImGuiCond cond)14901 void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond)
14902 {
14903     // Test condition (NB: bit 0 is always true) and clear flags for next time
14904     if (cond && (window->SetWindowDockAllowFlags & cond) == 0)
14905         return;
14906     window->SetWindowDockAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
14907 
14908     if (window->DockId == dock_id)
14909         return;
14910 
14911     // If the user attempt to set a dock id that is a split node, we'll dig within to find a suitable docking spot
14912     ImGuiContext* ctx = GImGui;
14913     if (ImGuiDockNode* new_node = DockContextFindNodeByID(ctx, dock_id))
14914         if (new_node->IsSplitNode())
14915         {
14916             // Policy: Find central node or latest focused node. We first move back to our root node.
14917             new_node = DockNodeGetRootNode(new_node);
14918             if (new_node->CentralNode)
14919             {
14920                 IM_ASSERT(new_node->CentralNode->IsCentralNode());
14921                 dock_id = new_node->CentralNode->ID;
14922             }
14923             else
14924             {
14925                 dock_id = new_node->LastFocusedNodeId;
14926             }
14927         }
14928 
14929     if (window->DockId == dock_id)
14930         return;
14931 
14932     if (window->DockNode)
14933         DockNodeRemoveWindow(window->DockNode, window, 0);
14934     window->DockId = dock_id;
14935 }
14936 
14937 // Create an explicit dockspace node within an existing window. Also expose dock node flags and creates a CentralNode by default.
14938 // The Central Node is always displayed even when empty and shrink/extend according to the requested size of its neighbors.
14939 // 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)14940 ImGuiID ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags flags, const ImGuiWindowClass* window_class)
14941 {
14942     ImGuiContext* ctx = GImGui;
14943     ImGuiContext& g = *ctx;
14944     ImGuiWindow* window = GetCurrentWindow();
14945     if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
14946         return 0;
14947 
14948     // Early out if parent window is hidden/collapsed
14949     // This is faster but also DockNodeUpdateTabBar() relies on TabBarLayout() running (which won't if SkipItems=true) to set NextSelectedTabId = 0). See #2960.
14950     // If for whichever reason this is causing problem we would need to ensure that DockNodeUpdateTabBar() ends up clearing NextSelectedTabId even if SkipItems=true.
14951     if (window->SkipItems)
14952         flags |= ImGuiDockNodeFlags_KeepAliveOnly;
14953 
14954     IM_ASSERT((flags & ImGuiDockNodeFlags_DockSpace) == 0);
14955     IM_ASSERT(id != 0);
14956     ImGuiDockNode* node = DockContextFindNodeByID(ctx, id);
14957     if (!node)
14958     {
14959         IMGUI_DEBUG_LOG_DOCKING("DockSpace: dockspace node 0x%08X created\n", id);
14960         node = DockContextAddNode(ctx, id);
14961         node->SetLocalFlags(ImGuiDockNodeFlags_CentralNode);
14962     }
14963     if (window_class && window_class->ClassId != node->WindowClass.ClassId)
14964         IMGUI_DEBUG_LOG_DOCKING("DockSpace: dockspace node 0x%08X: setup WindowClass 0x%08X -> 0x%08X\n", id, node->WindowClass.ClassId, window_class->ClassId);
14965     node->SharedFlags = flags;
14966     node->WindowClass = window_class ? *window_class : ImGuiWindowClass();
14967 
14968     // When a DockSpace transitioned form implicit to explicit this may be called a second time
14969     // 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.
14970     if (node->LastFrameActive == g.FrameCount && !(flags & ImGuiDockNodeFlags_KeepAliveOnly))
14971     {
14972         IM_ASSERT(node->IsDockSpace() == false && "Cannot call DockSpace() twice a frame with the same ID");
14973         node->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_DockSpace);
14974         return id;
14975     }
14976     node->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_DockSpace);
14977 
14978     // Keep alive mode, this is allow windows docked into this node so stay docked even if they are not visible
14979     if (flags & ImGuiDockNodeFlags_KeepAliveOnly)
14980     {
14981         node->LastFrameAlive = g.FrameCount;
14982         return id;
14983     }
14984 
14985     const ImVec2 content_avail = GetContentRegionAvail();
14986     ImVec2 size = ImFloor(size_arg);
14987     if (size.x <= 0.0f)
14988         size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
14989     if (size.y <= 0.0f)
14990         size.y = ImMax(content_avail.y + size.y, 4.0f);
14991     IM_ASSERT(size.x > 0.0f && size.y > 0.0f);
14992 
14993     node->Pos = window->DC.CursorPos;
14994     node->Size = node->SizeRef = size;
14995     SetNextWindowPos(node->Pos);
14996     SetNextWindowSize(node->Size);
14997     g.NextWindowData.PosUndock = false;
14998 
14999     // FIXME-DOCK: Why do we need a child window to host a dockspace, could we host it in the existing window?
15000     // FIXME-DOCK: What is the reason for not simply calling BeginChild()? (OK to have a reason but should be commented)
15001     ImGuiWindowFlags window_flags = ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_DockNodeHost;
15002     window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar;
15003     window_flags |= ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;
15004     window_flags |= ImGuiWindowFlags_NoBackground;
15005 
15006     char title[256];
15007     ImFormatString(title, IM_ARRAYSIZE(title), "%s/DockSpace_%08X", window->Name, id);
15008 
15009     PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0.0f);
15010     Begin(title, NULL, window_flags);
15011     PopStyleVar();
15012 
15013     ImGuiWindow* host_window = g.CurrentWindow;
15014     DockNodeSetupHostWindow(node, host_window);
15015     host_window->ChildId = window->GetID(title);
15016     node->OnlyNodeWithWindows = NULL;
15017 
15018     IM_ASSERT(node->IsRootNode());
15019 
15020     // We need to handle the rare case were a central node is missing.
15021     // This can happen if the node was first created manually with DockBuilderAddNode() but _without_ the ImGuiDockNodeFlags_Dockspace.
15022     // Doing it correctly would set the _CentralNode flags, which would then propagate according to subsequent split.
15023     // 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.
15024     // The specific sub-property of _CentralNode we are interested in recovering here is the "Don't delete when empty" property,
15025     // as it doesn't make sense for an empty dockspace to not have this property.
15026     if (node->IsLeafNode() && !node->IsCentralNode())
15027         node->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_CentralNode);
15028 
15029     // Update the node
15030     DockNodeUpdate(node);
15031 
15032     End();
15033     ItemSize(size);
15034     return id;
15035 }
15036 
15037 // Tips: Use with ImGuiDockNodeFlags_PassthruCentralNode!
15038 // The limitation with this call is that your window won't have a menu bar.
15039 // 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.
15040 // 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(const ImGuiViewport * viewport,ImGuiDockNodeFlags dockspace_flags,const ImGuiWindowClass * window_class)15041 ImGuiID ImGui::DockSpaceOverViewport(const ImGuiViewport* viewport, ImGuiDockNodeFlags dockspace_flags, const ImGuiWindowClass* window_class)
15042 {
15043     if (viewport == NULL)
15044         viewport = GetMainViewport();
15045 
15046     SetNextWindowPos(viewport->WorkPos);
15047     SetNextWindowSize(viewport->WorkSize);
15048     SetNextWindowViewport(viewport->ID);
15049 
15050     ImGuiWindowFlags host_window_flags = 0;
15051     host_window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking;
15052     host_window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
15053     if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode)
15054         host_window_flags |= ImGuiWindowFlags_NoBackground;
15055 
15056     char label[32];
15057     ImFormatString(label, IM_ARRAYSIZE(label), "DockSpaceViewport_%08X", viewport->ID);
15058 
15059     PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
15060     PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
15061     PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
15062     Begin(label, NULL, host_window_flags);
15063     PopStyleVar(3);
15064 
15065     ImGuiID dockspace_id = GetID("DockSpace");
15066     DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags, window_class);
15067     End();
15068 
15069     return dockspace_id;
15070 }
15071 
15072 //-----------------------------------------------------------------------------
15073 // Docking: Builder Functions
15074 //-----------------------------------------------------------------------------
15075 // Very early end-user API to manipulate dock nodes.
15076 // Only available in imgui_internal.h. Expect this API to change/break!
15077 // It is expected that those functions are all called _before_ the dockspace node submission.
15078 //-----------------------------------------------------------------------------
15079 // - DockBuilderDockWindow()
15080 // - DockBuilderGetNode()
15081 // - DockBuilderSetNodePos()
15082 // - DockBuilderSetNodeSize()
15083 // - DockBuilderAddNode()
15084 // - DockBuilderRemoveNode()
15085 // - DockBuilderRemoveNodeChildNodes()
15086 // - DockBuilderRemoveNodeDockedWindows()
15087 // - DockBuilderSplitNode()
15088 // - DockBuilderCopyNodeRec()
15089 // - DockBuilderCopyNode()
15090 // - DockBuilderCopyWindowSettings()
15091 // - DockBuilderCopyDockSpace()
15092 // - DockBuilderFinish()
15093 //-----------------------------------------------------------------------------
15094 
DockBuilderDockWindow(const char * window_name,ImGuiID node_id)15095 void ImGui::DockBuilderDockWindow(const char* window_name, ImGuiID node_id)
15096 {
15097     // We don't preserve relative order of multiple docked windows (by clearing DockOrder back to -1)
15098     ImGuiID window_id = ImHashStr(window_name);
15099     if (ImGuiWindow* window = FindWindowByID(window_id))
15100     {
15101         // Apply to created window
15102         SetWindowDock(window, node_id, ImGuiCond_Always);
15103         window->DockOrder = -1;
15104     }
15105     else
15106     {
15107         // Apply to settings
15108         ImGuiWindowSettings* settings = FindWindowSettings(window_id);
15109         if (settings == NULL)
15110             settings = CreateNewWindowSettings(window_name);
15111         settings->DockId = node_id;
15112         settings->DockOrder = -1;
15113     }
15114 }
15115 
DockBuilderGetNode(ImGuiID node_id)15116 ImGuiDockNode* ImGui::DockBuilderGetNode(ImGuiID node_id)
15117 {
15118     ImGuiContext* ctx = GImGui;
15119     return DockContextFindNodeByID(ctx, node_id);
15120 }
15121 
DockBuilderSetNodePos(ImGuiID node_id,ImVec2 pos)15122 void ImGui::DockBuilderSetNodePos(ImGuiID node_id, ImVec2 pos)
15123 {
15124     ImGuiContext* ctx = GImGui;
15125     ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_id);
15126     if (node == NULL)
15127         return;
15128     node->Pos = pos;
15129     node->AuthorityForPos = ImGuiDataAuthority_DockNode;
15130 }
15131 
DockBuilderSetNodeSize(ImGuiID node_id,ImVec2 size)15132 void ImGui::DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size)
15133 {
15134     ImGuiContext* ctx = GImGui;
15135     ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_id);
15136     if (node == NULL)
15137         return;
15138     IM_ASSERT(size.x > 0.0f && size.y > 0.0f);
15139     node->Size = node->SizeRef = size;
15140     node->AuthorityForSize = ImGuiDataAuthority_DockNode;
15141 }
15142 
15143 // Make sure to use the ImGuiDockNodeFlags_DockSpace flag to create a dockspace node! Otherwise this will create a floating node!
15144 // - Floating node: you can then call DockBuilderSetNodePos()/DockBuilderSetNodeSize() to position and size the floating node.
15145 // - Dockspace node: calling DockBuilderSetNodePos() is unnecessary.
15146 // - If you intend to split a node immediately after creation using DockBuilderSplitNode(), make sure to call DockBuilderSetNodeSize() beforehand!
15147 //   For various reason, the splitting code currently needs a base size otherwise space may not be allocated as precisely as you would expect.
15148 // - Use (id == 0) to let the system allocate a node identifier.
15149 // - Existing node with a same id will be removed.
DockBuilderAddNode(ImGuiID id,ImGuiDockNodeFlags flags)15150 ImGuiID ImGui::DockBuilderAddNode(ImGuiID id, ImGuiDockNodeFlags flags)
15151 {
15152     ImGuiContext* ctx = GImGui;
15153 
15154     if (id != 0)
15155         DockBuilderRemoveNode(id);
15156 
15157     ImGuiDockNode* node = NULL;
15158     if (flags & ImGuiDockNodeFlags_DockSpace)
15159     {
15160         DockSpace(id, ImVec2(0, 0), (flags & ~ImGuiDockNodeFlags_DockSpace) | ImGuiDockNodeFlags_KeepAliveOnly);
15161         node = DockContextFindNodeByID(ctx, id);
15162     }
15163     else
15164     {
15165         node = DockContextAddNode(ctx, id);
15166         node->SetLocalFlags(flags);
15167     }
15168     node->LastFrameAlive = ctx->FrameCount;   // Set this otherwise BeginDocked will undock during the same frame.
15169     return node->ID;
15170 }
15171 
DockBuilderRemoveNode(ImGuiID node_id)15172 void ImGui::DockBuilderRemoveNode(ImGuiID node_id)
15173 {
15174     ImGuiContext* ctx = GImGui;
15175     ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_id);
15176     if (node == NULL)
15177         return;
15178     DockBuilderRemoveNodeDockedWindows(node_id, true);
15179     DockBuilderRemoveNodeChildNodes(node_id);
15180     // Node may have moved or deleted if e.g. any merge happened
15181     node = DockContextFindNodeByID(ctx, node_id);
15182     if (node == NULL)
15183         return;
15184     if (node->IsCentralNode() && node->ParentNode)
15185         node->ParentNode->SetLocalFlags(node->ParentNode->LocalFlags | ImGuiDockNodeFlags_CentralNode);
15186     DockContextRemoveNode(ctx, node, true);
15187 }
15188 
15189 // root_id = 0 to remove all, root_id != 0 to remove child of given node.
DockBuilderRemoveNodeChildNodes(ImGuiID root_id)15190 void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id)
15191 {
15192     ImGuiContext* ctx = GImGui;
15193     ImGuiDockContext* dc  = &ctx->DockContext;
15194 
15195     ImGuiDockNode* root_node = root_id ? DockContextFindNodeByID(ctx, root_id) : NULL;
15196     if (root_id && root_node == NULL)
15197         return;
15198     bool has_central_node = false;
15199 
15200     ImGuiDataAuthority backup_root_node_authority_for_pos = root_node ? root_node->AuthorityForPos : ImGuiDataAuthority_Auto;
15201     ImGuiDataAuthority backup_root_node_authority_for_size = root_node ? root_node->AuthorityForSize : ImGuiDataAuthority_Auto;
15202 
15203     // Process active windows
15204     ImVector<ImGuiDockNode*> nodes_to_remove;
15205     for (int n = 0; n < dc->Nodes.Data.Size; n++)
15206         if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
15207         {
15208             bool want_removal = (root_id == 0) || (node->ID != root_id && DockNodeGetRootNode(node)->ID == root_id);
15209             if (want_removal)
15210             {
15211                 if (node->IsCentralNode())
15212                     has_central_node = true;
15213                 if (root_id != 0)
15214                     DockContextQueueNotifyRemovedNode(ctx, node);
15215                 if (root_node)
15216                 {
15217                     DockNodeMoveWindows(root_node, node);
15218                     DockSettingsRenameNodeReferences(node->ID, root_node->ID);
15219                 }
15220                 nodes_to_remove.push_back(node);
15221             }
15222         }
15223 
15224     // DockNodeMoveWindows->DockNodeAddWindow will normally set those when reaching two windows (which is only adequate during interactive merge)
15225     // Make sure we don't lose our current pos/size. (FIXME-DOCK: Consider tidying up that code in DockNodeAddWindow instead)
15226     if (root_node)
15227     {
15228         root_node->AuthorityForPos = backup_root_node_authority_for_pos;
15229         root_node->AuthorityForSize = backup_root_node_authority_for_size;
15230     }
15231 
15232     // Apply to settings
15233     for (ImGuiWindowSettings* settings = ctx->SettingsWindows.begin(); settings != NULL; settings = ctx->SettingsWindows.next_chunk(settings))
15234         if (ImGuiID window_settings_dock_id = settings->DockId)
15235             for (int n = 0; n < nodes_to_remove.Size; n++)
15236                 if (nodes_to_remove[n]->ID == window_settings_dock_id)
15237                 {
15238                     settings->DockId = root_id;
15239                     break;
15240                 }
15241 
15242     // Not really efficient, but easier to destroy a whole hierarchy considering DockContextRemoveNode is attempting to merge nodes
15243     if (nodes_to_remove.Size > 1)
15244         ImQsort(nodes_to_remove.Data, nodes_to_remove.Size, sizeof(ImGuiDockNode*), DockNodeComparerDepthMostFirst);
15245     for (int n = 0; n < nodes_to_remove.Size; n++)
15246         DockContextRemoveNode(ctx, nodes_to_remove[n], false);
15247 
15248     if (root_id == 0)
15249     {
15250         dc->Nodes.Clear();
15251         dc->Requests.clear();
15252     }
15253     else if (has_central_node)
15254     {
15255         root_node->CentralNode = root_node;
15256         root_node->SetLocalFlags(root_node->LocalFlags | ImGuiDockNodeFlags_CentralNode);
15257     }
15258 }
15259 
DockBuilderRemoveNodeDockedWindows(ImGuiID root_id,bool clear_settings_refs)15260 void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiID root_id, bool clear_settings_refs)
15261 {
15262     // Clear references in settings
15263     ImGuiContext* ctx = GImGui;
15264     ImGuiContext& g = *ctx;
15265     if (clear_settings_refs)
15266     {
15267         for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
15268         {
15269             bool want_removal = (root_id == 0) || (settings->DockId == root_id);
15270             if (!want_removal && settings->DockId != 0)
15271                 if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, settings->DockId))
15272                     if (DockNodeGetRootNode(node)->ID == root_id)
15273                         want_removal = true;
15274             if (want_removal)
15275                 settings->DockId = 0;
15276         }
15277     }
15278 
15279     // Clear references in windows
15280     for (int n = 0; n < g.Windows.Size; n++)
15281     {
15282         ImGuiWindow* window = g.Windows[n];
15283         bool want_removal = (root_id == 0) || (window->DockNode && DockNodeGetRootNode(window->DockNode)->ID == root_id) || (window->DockNodeAsHost && window->DockNodeAsHost->ID == root_id);
15284         if (want_removal)
15285         {
15286             const ImGuiID backup_dock_id = window->DockId;
15287             IM_UNUSED(backup_dock_id);
15288             DockContextProcessUndockWindow(ctx, window, clear_settings_refs);
15289             if (!clear_settings_refs)
15290                 IM_ASSERT(window->DockId == backup_dock_id);
15291         }
15292     }
15293 }
15294 
15295 // 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.
15296 // Return value is ID of the node at the specified direction, so same as (*out_id_at_dir) if that pointer is set.
15297 // 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)15298 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)
15299 {
15300     ImGuiContext* ctx = GImGui;
15301     IM_ASSERT(split_dir != ImGuiDir_None);
15302     IMGUI_DEBUG_LOG_DOCKING("DockBuilderSplitNode node 0x%08X, split_dir %d\n", id, split_dir);
15303 
15304     ImGuiDockNode* node = DockContextFindNodeByID(ctx, id);
15305     if (node == NULL)
15306     {
15307         IM_ASSERT(node != NULL);
15308         return 0;
15309     }
15310 
15311     IM_ASSERT(!node->IsSplitNode()); // Assert if already Split
15312 
15313     ImGuiDockRequest req;
15314     req.Type = ImGuiDockRequestType_Split;
15315     req.DockTargetWindow = NULL;
15316     req.DockTargetNode = node;
15317     req.DockPayload = NULL;
15318     req.DockSplitDir = split_dir;
15319     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);
15320     req.DockSplitOuter = false;
15321     DockContextProcessDock(ctx, &req);
15322 
15323     ImGuiID id_at_dir = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 0 : 1]->ID;
15324     ImGuiID id_at_opposite_dir = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0]->ID;
15325     if (out_id_at_dir)
15326         *out_id_at_dir = id_at_dir;
15327     if (out_id_at_opposite_dir)
15328         *out_id_at_opposite_dir = id_at_opposite_dir;
15329     return id_at_dir;
15330 }
15331 
DockBuilderCopyNodeRec(ImGuiDockNode * src_node,ImGuiID dst_node_id_if_known,ImVector<ImGuiID> * out_node_remap_pairs)15332 static ImGuiDockNode* DockBuilderCopyNodeRec(ImGuiDockNode* src_node, ImGuiID dst_node_id_if_known, ImVector<ImGuiID>* out_node_remap_pairs)
15333 {
15334     ImGuiContext* ctx = GImGui;
15335     ImGuiDockNode* dst_node = ImGui::DockContextAddNode(ctx, dst_node_id_if_known);
15336     dst_node->SharedFlags = src_node->SharedFlags;
15337     dst_node->LocalFlags = src_node->LocalFlags;
15338     dst_node->LocalFlagsInWindows = ImGuiDockNodeFlags_None;
15339     dst_node->Pos = src_node->Pos;
15340     dst_node->Size = src_node->Size;
15341     dst_node->SizeRef = src_node->SizeRef;
15342     dst_node->SplitAxis = src_node->SplitAxis;
15343     dst_node->UpdateMergedFlags();
15344 
15345     out_node_remap_pairs->push_back(src_node->ID);
15346     out_node_remap_pairs->push_back(dst_node->ID);
15347 
15348     for (int child_n = 0; child_n < IM_ARRAYSIZE(src_node->ChildNodes); child_n++)
15349         if (src_node->ChildNodes[child_n])
15350         {
15351             dst_node->ChildNodes[child_n] = DockBuilderCopyNodeRec(src_node->ChildNodes[child_n], 0, out_node_remap_pairs);
15352             dst_node->ChildNodes[child_n]->ParentNode = dst_node;
15353         }
15354 
15355     IMGUI_DEBUG_LOG_DOCKING("Fork node %08X -> %08X (%d childs)\n", src_node->ID, dst_node->ID, dst_node->IsSplitNode() ? 2 : 0);
15356     return dst_node;
15357 }
15358 
DockBuilderCopyNode(ImGuiID src_node_id,ImGuiID dst_node_id,ImVector<ImGuiID> * out_node_remap_pairs)15359 void ImGui::DockBuilderCopyNode(ImGuiID src_node_id, ImGuiID dst_node_id, ImVector<ImGuiID>* out_node_remap_pairs)
15360 {
15361     ImGuiContext* ctx = GImGui;
15362     IM_ASSERT(src_node_id != 0);
15363     IM_ASSERT(dst_node_id != 0);
15364     IM_ASSERT(out_node_remap_pairs != NULL);
15365 
15366     DockBuilderRemoveNode(dst_node_id);
15367 
15368     ImGuiDockNode* src_node = DockContextFindNodeByID(ctx, src_node_id);
15369     IM_ASSERT(src_node != NULL);
15370 
15371     out_node_remap_pairs->clear();
15372     DockBuilderCopyNodeRec(src_node, dst_node_id, out_node_remap_pairs);
15373 
15374     IM_ASSERT((out_node_remap_pairs->Size % 2) == 0);
15375 }
15376 
DockBuilderCopyWindowSettings(const char * src_name,const char * dst_name)15377 void ImGui::DockBuilderCopyWindowSettings(const char* src_name, const char* dst_name)
15378 {
15379     ImGuiWindow* src_window = FindWindowByName(src_name);
15380     if (src_window == NULL)
15381         return;
15382     if (ImGuiWindow* dst_window = FindWindowByName(dst_name))
15383     {
15384         dst_window->Pos = src_window->Pos;
15385         dst_window->Size = src_window->Size;
15386         dst_window->SizeFull = src_window->SizeFull;
15387         dst_window->Collapsed = src_window->Collapsed;
15388     }
15389     else if (ImGuiWindowSettings* dst_settings = FindOrCreateWindowSettings(dst_name))
15390     {
15391         ImVec2ih window_pos_2ih = ImVec2ih(src_window->Pos);
15392         if (src_window->ViewportId != 0 && src_window->ViewportId != IMGUI_VIEWPORT_DEFAULT_ID)
15393         {
15394             dst_settings->ViewportPos = window_pos_2ih;
15395             dst_settings->ViewportId = src_window->ViewportId;
15396             dst_settings->Pos = ImVec2ih(0, 0);
15397         }
15398         else
15399         {
15400             dst_settings->Pos = window_pos_2ih;
15401         }
15402         dst_settings->Size = ImVec2ih(src_window->SizeFull);
15403         dst_settings->Collapsed = src_window->Collapsed;
15404     }
15405 }
15406 
15407 // 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)15408 void ImGui::DockBuilderCopyDockSpace(ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector<const char*>* in_window_remap_pairs)
15409 {
15410     IM_ASSERT(src_dockspace_id != 0);
15411     IM_ASSERT(dst_dockspace_id != 0);
15412     IM_ASSERT(in_window_remap_pairs != NULL);
15413     IM_ASSERT((in_window_remap_pairs->Size % 2) == 0);
15414 
15415     // Duplicate entire dock
15416     // 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,
15417     // whereas we could attempt to at least keep them together in a new, same floating node.
15418     ImVector<ImGuiID> node_remap_pairs;
15419     DockBuilderCopyNode(src_dockspace_id, dst_dockspace_id, &node_remap_pairs);
15420 
15421     // Attempt to transition all the upcoming windows associated to dst_dockspace_id into the newly created hierarchy of dock nodes
15422     // (The windows associated to src_dockspace_id are staying in place)
15423     ImVector<ImGuiID> src_windows;
15424     for (int remap_window_n = 0; remap_window_n < in_window_remap_pairs->Size; remap_window_n += 2)
15425     {
15426         const char* src_window_name = (*in_window_remap_pairs)[remap_window_n];
15427         const char* dst_window_name = (*in_window_remap_pairs)[remap_window_n + 1];
15428         ImGuiID src_window_id = ImHashStr(src_window_name);
15429         src_windows.push_back(src_window_id);
15430 
15431         // Search in the remapping tables
15432         ImGuiID src_dock_id = 0;
15433         if (ImGuiWindow* src_window = FindWindowByID(src_window_id))
15434             src_dock_id = src_window->DockId;
15435         else if (ImGuiWindowSettings* src_window_settings = FindWindowSettings(src_window_id))
15436             src_dock_id = src_window_settings->DockId;
15437         ImGuiID dst_dock_id = 0;
15438         for (int dock_remap_n = 0; dock_remap_n < node_remap_pairs.Size; dock_remap_n += 2)
15439             if (node_remap_pairs[dock_remap_n] == src_dock_id)
15440             {
15441                 dst_dock_id = node_remap_pairs[dock_remap_n + 1];
15442                 //node_remap_pairs[dock_remap_n] = node_remap_pairs[dock_remap_n + 1] = 0; // Clear
15443                 break;
15444             }
15445 
15446         if (dst_dock_id != 0)
15447         {
15448             // Docked windows gets redocked into the new node hierarchy.
15449             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);
15450             DockBuilderDockWindow(dst_window_name, dst_dock_id);
15451         }
15452         else
15453         {
15454             // Floating windows gets their settings transferred (regardless of whether the new window already exist or not)
15455             // When this is leading to a Copy and not a Move, we would get two overlapping floating windows. Could we possibly dock them together?
15456             IMGUI_DEBUG_LOG_DOCKING("Remap window settings '%s' -> '%s'\n", src_window_name, dst_window_name);
15457             DockBuilderCopyWindowSettings(src_window_name, dst_window_name);
15458         }
15459     }
15460 
15461     // 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")
15462     // Find those windows and move to them to the cloned dock node. This may be optional?
15463     for (int dock_remap_n = 0; dock_remap_n < node_remap_pairs.Size; dock_remap_n += 2)
15464         if (ImGuiID src_dock_id = node_remap_pairs[dock_remap_n])
15465         {
15466             ImGuiID dst_dock_id = node_remap_pairs[dock_remap_n + 1];
15467             ImGuiDockNode* node = DockBuilderGetNode(src_dock_id);
15468             for (int window_n = 0; window_n < node->Windows.Size; window_n++)
15469             {
15470                 ImGuiWindow* window = node->Windows[window_n];
15471                 if (src_windows.contains(window->ID))
15472                     continue;
15473 
15474                 // Docked windows gets redocked into the new node hierarchy.
15475                 IMGUI_DEBUG_LOG_DOCKING("Remap window '%s' %08X -> %08X\n", window->Name, src_dock_id, dst_dock_id);
15476                 DockBuilderDockWindow(window->Name, dst_dock_id);
15477             }
15478         }
15479 }
15480 
DockBuilderFinish(ImGuiID root_id)15481 void ImGui::DockBuilderFinish(ImGuiID root_id)
15482 {
15483     ImGuiContext* ctx = GImGui;
15484     //DockContextRebuild(ctx);
15485     DockContextBuildAddWindowsToNodes(ctx, root_id);
15486 }
15487 
15488 //-----------------------------------------------------------------------------
15489 // Docking: Begin/End Support Functions (called from Begin/End)
15490 //-----------------------------------------------------------------------------
15491 // - GetWindowAlwaysWantOwnTabBar()
15492 // - DockContextBindNodeToWindow()
15493 // - BeginDocked()
15494 // - BeginDockableDragDropSource()
15495 // - BeginDockableDragDropTarget()
15496 //-----------------------------------------------------------------------------
15497 
GetWindowAlwaysWantOwnTabBar(ImGuiWindow * window)15498 bool ImGui::GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window)
15499 {
15500     ImGuiContext& g = *GImGui;
15501     if (g.IO.ConfigDockingAlwaysTabBar || window->WindowClass.DockingAlwaysTabBar)
15502         if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking)) == 0)
15503             if (!window->IsFallbackWindow)    // We don't support AlwaysTabBar on the fallback/implicit window to avoid unused dock-node overhead/noise
15504                 return true;
15505     return false;
15506 }
15507 
DockContextBindNodeToWindow(ImGuiContext * ctx,ImGuiWindow * window)15508 static ImGuiDockNode* ImGui::DockContextBindNodeToWindow(ImGuiContext* ctx, ImGuiWindow* window)
15509 {
15510     ImGuiContext& g = *ctx;
15511     ImGuiDockNode* node = DockContextFindNodeByID(ctx, window->DockId);
15512     IM_ASSERT(window->DockNode == NULL);
15513 
15514     // We should not be docking into a split node (SetWindowDock should avoid this)
15515     if (node && node->IsSplitNode())
15516     {
15517         DockContextProcessUndockWindow(ctx, window);
15518         return NULL;
15519     }
15520 
15521     // Create node
15522     if (node == NULL)
15523     {
15524         node = DockContextAddNode(ctx, window->DockId);
15525         node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window;
15526         node->LastFrameAlive = g.FrameCount;
15527     }
15528 
15529     // If the node just turned visible and is part of a hierarchy, it doesn't have a Size assigned by DockNodeTreeUpdatePosSize() yet,
15530     // so we're forcing a Pos/Size update from the first ancestor that is already visible (often it will be the root node).
15531     // 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.
15532     // This is a little wonky because we don't normally update the Pos/Size of visible node mid-frame.
15533     if (!node->IsVisible)
15534     {
15535         ImGuiDockNode* ancestor_node = node;
15536         while (!ancestor_node->IsVisible)
15537         {
15538             ancestor_node->IsVisible = true;
15539             ancestor_node->MarkedForPosSizeWrite = true;
15540             if (ancestor_node->ParentNode)
15541                 ancestor_node = ancestor_node->ParentNode;
15542         }
15543         IM_ASSERT(ancestor_node->Size.x > 0.0f && ancestor_node->Size.y > 0.0f);
15544         DockNodeTreeUpdatePosSize(ancestor_node, ancestor_node->Pos, ancestor_node->Size, true);
15545     }
15546 
15547     // Add window to node
15548     DockNodeAddWindow(node, window, true);
15549     IM_ASSERT(node == window->DockNode);
15550     return node;
15551 }
15552 
BeginDocked(ImGuiWindow * window,bool * p_open)15553 void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open)
15554 {
15555     ImGuiContext* ctx = GImGui;
15556     ImGuiContext& g = *ctx;
15557 
15558     // Clear fields ahead so most early-out paths don't have to do it
15559     window->DockIsActive = window->DockNodeIsVisible = window->DockTabIsVisible = false;
15560 
15561     const bool auto_dock_node = GetWindowAlwaysWantOwnTabBar(window);
15562     if (auto_dock_node)
15563     {
15564         if (window->DockId == 0)
15565         {
15566             IM_ASSERT(window->DockNode == NULL);
15567             window->DockId = DockContextGenNodeID(ctx);
15568         }
15569     }
15570     else
15571     {
15572         // Calling SetNextWindowPos() undock windows by default (by setting PosUndock)
15573         bool want_undock = false;
15574         want_undock |= (window->Flags & ImGuiWindowFlags_NoDocking) != 0;
15575         want_undock |= (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) && (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) && g.NextWindowData.PosUndock;
15576         if (want_undock)
15577         {
15578             DockContextProcessUndockWindow(ctx, window);
15579             return;
15580         }
15581     }
15582 
15583     // Bind to our dock node
15584     ImGuiDockNode* node = window->DockNode;
15585     if (node != NULL)
15586         IM_ASSERT(window->DockId == node->ID);
15587     if (window->DockId != 0 && node == NULL)
15588     {
15589         node = DockContextBindNodeToWindow(ctx, window);
15590         if (node == NULL)
15591             return;
15592     }
15593 
15594 #if 0
15595     // Undock if the ImGuiDockNodeFlags_NoDockingInCentralNode got set
15596     if (node->IsCentralNode && (node->Flags & ImGuiDockNodeFlags_NoDockingInCentralNode))
15597     {
15598         DockContextProcessUndockWindow(ctx, window);
15599         return;
15600     }
15601 #endif
15602 
15603     // Undock if our dockspace node disappeared
15604     // Note how we are testing for LastFrameAlive and NOT LastFrameActive. A DockSpace node can be maintained alive while being inactive with ImGuiDockNodeFlags_KeepAliveOnly.
15605     if (node->LastFrameAlive < g.FrameCount)
15606     {
15607         // If the window has been orphaned, transition the docknode to an implicit node processed in DockContextNewFrameUpdateDocking()
15608         ImGuiDockNode* root_node = DockNodeGetRootNode(node);
15609         if (root_node->LastFrameAlive < g.FrameCount)
15610             DockContextProcessUndockWindow(ctx, window);
15611         else
15612             window->DockIsActive = true;
15613         return;
15614     }
15615 
15616     // Store style overrides
15617     for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
15618         window->DockStyle.Colors[color_n] = ColorConvertFloat4ToU32(g.Style.Colors[GWindowDockStyleColors[color_n]]);
15619 
15620     // Fast path return. It is common for windows to hold on a persistent DockId but be the only visible window,
15621     // and never create neither a host window neither a tab bar.
15622     // FIXME-DOCK: replace ->HostWindow NULL compare with something more explicit (~was initially intended as a first frame test)
15623     if (node->HostWindow == NULL)
15624     {
15625         if (node->State == ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing)
15626             window->DockIsActive = true;
15627         if (node->Windows.Size > 1)
15628             DockNodeHideWindowDuringHostWindowCreation(window);
15629         return;
15630     }
15631 
15632     // We can have zero-sized nodes (e.g. children of a small-size dockspace)
15633     IM_ASSERT(node->HostWindow);
15634     IM_ASSERT(node->IsLeafNode());
15635     IM_ASSERT(node->Size.x >= 0.0f && node->Size.y >= 0.0f);
15636     node->State = ImGuiDockNodeState_HostWindowVisible;
15637 
15638     // Undock if we are submitted earlier than the host window
15639     if (window->BeginOrderWithinContext < node->HostWindow->BeginOrderWithinContext)
15640     {
15641         DockContextProcessUndockWindow(ctx, window);
15642         return;
15643     }
15644 
15645     // Position/Size window
15646     SetNextWindowPos(node->Pos);
15647     SetNextWindowSize(node->Size);
15648     g.NextWindowData.PosUndock = false; // Cancel implicit undocking of SetNextWindowPos()
15649     window->DockIsActive = true;
15650     window->DockNodeIsVisible = true;
15651     window->DockTabIsVisible = false;
15652     if (node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly)
15653         return;
15654 
15655     // When the window is selected we mark it as visible.
15656     if (node->VisibleWindow == window)
15657         window->DockTabIsVisible = true;
15658 
15659     // Update window flag
15660     IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) == 0);
15661     window->Flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_NoResize;
15662     if (node->IsHiddenTabBar() || node->IsNoTabBar())
15663         window->Flags |= ImGuiWindowFlags_NoTitleBar;
15664     else
15665         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!
15666 
15667     // Save new dock order only if the window has been visible once already
15668     // This allows multiple windows to be created in the same frame and have their respective dock orders preserved.
15669     if (node->TabBar && window->WasActive)
15670         window->DockOrder = (short)DockNodeGetTabOrder(window);
15671 
15672     if ((node->WantCloseAll || node->WantCloseTabId == window->ID) && p_open != NULL)
15673         *p_open = false;
15674 
15675     // Update ChildId to allow returning from Child to Parent with Escape
15676     ImGuiWindow* parent_window = window->DockNode->HostWindow;
15677     window->ChildId = parent_window->GetID(window->Name);
15678 }
15679 
BeginDockableDragDropSource(ImGuiWindow * window)15680 void ImGui::BeginDockableDragDropSource(ImGuiWindow* window)
15681 {
15682     ImGuiContext& g = *GImGui;
15683     IM_ASSERT(g.ActiveId == window->MoveId);
15684     IM_ASSERT(g.MovingWindow == window);
15685 
15686     window->DC.LastItemId = window->MoveId;
15687     window = window->RootWindowDockTree;
15688     IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0);
15689     bool is_drag_docking = ImRect(0, 0, window->SizeFull.x, GetFrameHeight()).Contains(g.ActiveIdClickOffset); // FIXME-DOCKING: Need to make this stateful and explicit
15690     if (is_drag_docking && BeginDragDropSource(ImGuiDragDropFlags_SourceNoPreviewTooltip | ImGuiDragDropFlags_SourceNoHoldToOpenOthers | ImGuiDragDropFlags_SourceAutoExpirePayload))
15691     {
15692         SetDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, &window, sizeof(window));
15693         EndDragDropSource();
15694 
15695         // Store style overrides
15696         for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
15697             window->DockStyle.Colors[color_n] = ColorConvertFloat4ToU32(g.Style.Colors[GWindowDockStyleColors[color_n]]);
15698     }
15699 }
15700 
BeginDockableDragDropTarget(ImGuiWindow * window)15701 void ImGui::BeginDockableDragDropTarget(ImGuiWindow* window)
15702 {
15703     ImGuiContext* ctx = GImGui;
15704     ImGuiContext& g = *ctx;
15705 
15706     //IM_ASSERT(window->RootWindowDockTree == window); // May also be a DockSpace
15707     IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0);
15708     if (!g.DragDropActive)
15709         return;
15710     //GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
15711     if (!BeginDragDropTargetCustom(window->Rect(), window->ID))
15712         return;
15713 
15714     // Peek into the payload before calling AcceptDragDropPayload() so we can handle overlapping dock nodes with filtering
15715     // (this is a little unusual pattern, normally most code would call AcceptDragDropPayload directly)
15716     const ImGuiPayload* payload = &g.DragDropPayload;
15717     if (!payload->IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) || !DockNodeIsDropAllowed(window, *(ImGuiWindow**)payload->Data))
15718     {
15719         EndDragDropTarget();
15720         return;
15721     }
15722 
15723     ImGuiWindow* payload_window = *(ImGuiWindow**)payload->Data;
15724     if (AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect))
15725     {
15726         // Select target node
15727         // (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)
15728         bool dock_into_floating_window = false;
15729         ImGuiDockNode* node = NULL;
15730         if (window->DockNodeAsHost)
15731         {
15732             // Cannot assume that node will != NULL even though we passed the rectangle test: it depends on padding/spacing handled by DockNodeTreeFindVisibleNodeByPos().
15733             node = DockNodeTreeFindVisibleNodeByPos(window->DockNodeAsHost, g.IO.MousePos);
15734 
15735             // There is an edge case when docking into a dockspace which only has _inactive_ nodes (because none of the windows are active)
15736             // In this case we need to fallback into any leaf mode, possibly the central node.
15737             // FIXME-20181220: We should not have to test for IsLeafNode() here but we have another bug to fix first.
15738             if (node && node->IsDockSpace() && node->IsRootNode())
15739                 node = (node->CentralNode && node->IsLeafNode()) ? node->CentralNode : DockNodeTreeFindFallbackLeafNode(node);
15740         }
15741         else
15742         {
15743             if (window->DockNode)
15744                 node = window->DockNode;
15745             else
15746                 dock_into_floating_window = true; // Dock into a regular window
15747         }
15748 
15749         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()));
15750         const bool is_explicit_target = IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max);
15751 
15752         // Preview docking request and find out split direction/ratio
15753         //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.
15754         const bool do_preview = payload->IsPreview() || payload->IsDelivery();
15755         if (do_preview && (node != NULL || dock_into_floating_window))
15756         {
15757             ImGuiDockPreviewData split_inner;
15758             ImGuiDockPreviewData split_outer;
15759             ImGuiDockPreviewData* split_data = &split_inner;
15760             if (node && (node->ParentNode || node->IsCentralNode()))
15761                 if (ImGuiDockNode* root_node = DockNodeGetRootNode(node))
15762                 {
15763                     DockNodePreviewDockSetup(window, root_node, payload_window, &split_outer, is_explicit_target, true);
15764                     if (split_outer.IsSplitDirExplicit)
15765                         split_data = &split_outer;
15766                 }
15767             DockNodePreviewDockSetup(window, node, payload_window, &split_inner, is_explicit_target, false);
15768             if (split_data == &split_outer)
15769                 split_inner.IsDropAllowed = false;
15770 
15771             // Draw inner then outer, so that previewed tab (in inner data) will be behind the outer drop boxes
15772             DockNodePreviewDockRender(window, node, payload_window, &split_inner);
15773             DockNodePreviewDockRender(window, node, payload_window, &split_outer);
15774 
15775             // Queue docking request
15776             if (split_data->IsDropAllowed && payload->IsDelivery())
15777                 DockContextQueueDock(ctx, window, split_data->SplitNode, payload_window, split_data->SplitDir, split_data->SplitRatio, split_data == &split_outer);
15778         }
15779     }
15780     EndDragDropTarget();
15781 }
15782 
15783 //-----------------------------------------------------------------------------
15784 // Docking: Settings
15785 //-----------------------------------------------------------------------------
15786 // - DockSettingsRenameNodeReferences()
15787 // - DockSettingsRemoveNodeReferences()
15788 // - DockSettingsFindNodeSettings()
15789 // - DockSettingsHandler_ApplyAll()
15790 // - DockSettingsHandler_ReadOpen()
15791 // - DockSettingsHandler_ReadLine()
15792 // - DockSettingsHandler_DockNodeToSettings()
15793 // - DockSettingsHandler_WriteAll()
15794 //-----------------------------------------------------------------------------
15795 
DockSettingsRenameNodeReferences(ImGuiID old_node_id,ImGuiID new_node_id)15796 static void ImGui::DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id)
15797 {
15798     ImGuiContext& g = *GImGui;
15799     IMGUI_DEBUG_LOG_DOCKING("DockSettingsRenameNodeReferences: from 0x%08X -> to 0x%08X\n", old_node_id, new_node_id);
15800     for (int window_n = 0; window_n < g.Windows.Size; window_n++)
15801     {
15802         ImGuiWindow* window = g.Windows[window_n];
15803         if (window->DockId == old_node_id && window->DockNode == NULL)
15804             window->DockId = new_node_id;
15805     }
15806     //// FIXME-OPT: We could remove this loop by storing the index in the map
15807     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
15808         if (settings->DockId == old_node_id)
15809             settings->DockId = new_node_id;
15810 }
15811 
15812 // Remove references stored in ImGuiWindowSettings to the given ImGuiDockNodeSettings
DockSettingsRemoveNodeReferences(ImGuiID * node_ids,int node_ids_count)15813 static void ImGui::DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ids_count)
15814 {
15815     ImGuiContext& g = *GImGui;
15816     int found = 0;
15817     //// FIXME-OPT: We could remove this loop by storing the index in the map
15818     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
15819         for (int node_n = 0; node_n < node_ids_count; node_n++)
15820             if (settings->DockId == node_ids[node_n])
15821             {
15822                 settings->DockId = 0;
15823                 settings->DockOrder = -1;
15824                 if (++found < node_ids_count)
15825                     break;
15826                 return;
15827             }
15828 }
15829 
DockSettingsFindNodeSettings(ImGuiContext * ctx,ImGuiID id)15830 static ImGuiDockNodeSettings* ImGui::DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID id)
15831 {
15832     // FIXME-OPT
15833     ImGuiDockContext* dc  = &ctx->DockContext;
15834     for (int n = 0; n < dc->NodesSettings.Size; n++)
15835         if (dc->NodesSettings[n].ID == id)
15836             return &dc->NodesSettings[n];
15837     return NULL;
15838 }
15839 
15840 // Clear settings data
DockSettingsHandler_ClearAll(ImGuiContext * ctx,ImGuiSettingsHandler *)15841 static void ImGui::DockSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
15842 {
15843     ImGuiDockContext* dc  = &ctx->DockContext;
15844     dc->NodesSettings.clear();
15845     DockContextClearNodes(ctx, 0, true);
15846 }
15847 
15848 // Recreate nodes based on settings data
DockSettingsHandler_ApplyAll(ImGuiContext * ctx,ImGuiSettingsHandler *)15849 static void ImGui::DockSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
15850 {
15851     // Prune settings at boot time only
15852     ImGuiDockContext* dc  = &ctx->DockContext;
15853     if (ctx->Windows.Size == 0)
15854         DockContextPruneUnusedSettingsNodes(ctx);
15855     DockContextBuildNodesFromSettings(ctx, dc->NodesSettings.Data, dc->NodesSettings.Size);
15856     DockContextBuildAddWindowsToNodes(ctx, 0);
15857 }
15858 
DockSettingsHandler_ReadOpen(ImGuiContext *,ImGuiSettingsHandler *,const char * name)15859 static void* ImGui::DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
15860 {
15861     if (strcmp(name, "Data") != 0)
15862         return NULL;
15863     return (void*)1;
15864 }
15865 
DockSettingsHandler_ReadLine(ImGuiContext * ctx,ImGuiSettingsHandler *,void *,const char * line)15866 static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettingsHandler*, void*, const char* line)
15867 {
15868     char c = 0;
15869     int x = 0, y = 0;
15870     int r = 0;
15871 
15872     // Parsing, e.g.
15873     // " DockNode   ID=0x00000001 Pos=383,193 Size=201,322 Split=Y,0.506 "
15874     // "   DockNode ID=0x00000002 Parent=0x00000001 "
15875     // Important: this code expect currently fields in a fixed order.
15876     ImGuiDockNodeSettings node;
15877     line = ImStrSkipBlank(line);
15878     if      (strncmp(line, "DockNode", 8) == 0)  { line = ImStrSkipBlank(line + strlen("DockNode")); }
15879     else if (strncmp(line, "DockSpace", 9) == 0) { line = ImStrSkipBlank(line + strlen("DockSpace")); node.Flags |= ImGuiDockNodeFlags_DockSpace; }
15880     else return;
15881     if (sscanf(line, "ID=0x%08X%n",      &node.ID, &r) == 1)            { line += r; } else return;
15882     if (sscanf(line, " Parent=0x%08X%n", &node.ParentNodeId, &r) == 1)  { line += r; if (node.ParentNodeId == 0) return; }
15883     if (sscanf(line, " Window=0x%08X%n", &node.ParentWindowId, &r) ==1) { line += r; if (node.ParentWindowId == 0) return; }
15884     if (node.ParentNodeId == 0)
15885     {
15886         if (sscanf(line, " Pos=%i,%i%n",  &x, &y, &r) == 2)         { line += r; node.Pos = ImVec2ih((short)x, (short)y); } else return;
15887         if (sscanf(line, " Size=%i,%i%n", &x, &y, &r) == 2)         { line += r; node.Size = ImVec2ih((short)x, (short)y); } else return;
15888     }
15889     else
15890     {
15891         if (sscanf(line, " SizeRef=%i,%i%n", &x, &y, &r) == 2)      { line += r; node.SizeRef = ImVec2ih((short)x, (short)y); }
15892     }
15893     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; }
15894     if (sscanf(line, " NoResize=%d%n", &x, &r) == 1)                { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoResize; }
15895     if (sscanf(line, " CentralNode=%d%n", &x, &r) == 1)             { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_CentralNode; }
15896     if (sscanf(line, " NoTabBar=%d%n", &x, &r) == 1)                { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoTabBar; }
15897     if (sscanf(line, " HiddenTabBar=%d%n", &x, &r) == 1)            { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_HiddenTabBar; }
15898     if (sscanf(line, " NoWindowMenuButton=%d%n", &x, &r) == 1)      { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoWindowMenuButton; }
15899     if (sscanf(line, " NoCloseButton=%d%n", &x, &r) == 1)           { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoCloseButton; }
15900     if (sscanf(line, " Selected=0x%08X%n", &node.SelectedTabId,&r) == 1) { line += r; }
15901     if (node.ParentNodeId != 0)
15902         if (ImGuiDockNodeSettings* parent_settings = DockSettingsFindNodeSettings(ctx, node.ParentNodeId))
15903             node.Depth = parent_settings->Depth + 1;
15904     ctx->DockContext.NodesSettings.push_back(node);
15905 }
15906 
DockSettingsHandler_DockNodeToSettings(ImGuiDockContext * dc,ImGuiDockNode * node,int depth)15907 static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDockNode* node, int depth)
15908 {
15909     ImGuiDockNodeSettings node_settings;
15910     IM_ASSERT(depth < (1 << (sizeof(node_settings.Depth) << 3)));
15911     node_settings.ID = node->ID;
15912     node_settings.ParentNodeId = node->ParentNode ? node->ParentNode->ID : 0;
15913     node_settings.ParentWindowId = (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow) ? node->HostWindow->ParentWindow->ID : 0;
15914     node_settings.SelectedTabId = node->SelectedTabId;
15915     node_settings.SplitAxis = (signed char)(node->IsSplitNode() ? node->SplitAxis : ImGuiAxis_None);
15916     node_settings.Depth = (char)depth;
15917     node_settings.Flags = (node->LocalFlags & ImGuiDockNodeFlags_SavedFlagsMask_);
15918     node_settings.Pos = ImVec2ih(node->Pos);
15919     node_settings.Size = ImVec2ih(node->Size);
15920     node_settings.SizeRef = ImVec2ih(node->SizeRef);
15921     dc->NodesSettings.push_back(node_settings);
15922     if (node->ChildNodes[0])
15923         DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[0], depth + 1);
15924     if (node->ChildNodes[1])
15925         DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[1], depth + 1);
15926 }
15927 
DockSettingsHandler_WriteAll(ImGuiContext * ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)15928 static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
15929 {
15930     ImGuiContext& g = *ctx;
15931     ImGuiDockContext* dc = &ctx->DockContext;
15932     if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
15933         return;
15934 
15935     // Gather settings data
15936     // (unlike our windows settings, because nodes are always built we can do a full rewrite of the SettingsNode buffer)
15937     dc->NodesSettings.resize(0);
15938     dc->NodesSettings.reserve(dc->Nodes.Data.Size);
15939     for (int n = 0; n < dc->Nodes.Data.Size; n++)
15940         if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
15941             if (node->IsRootNode())
15942                 DockSettingsHandler_DockNodeToSettings(dc, node, 0);
15943 
15944     int max_depth = 0;
15945     for (int node_n = 0; node_n < dc->NodesSettings.Size; node_n++)
15946         max_depth = ImMax((int)dc->NodesSettings[node_n].Depth, max_depth);
15947 
15948     // Write to text buffer
15949     buf->appendf("[%s][Data]\n", handler->TypeName);
15950     for (int node_n = 0; node_n < dc->NodesSettings.Size; node_n++)
15951     {
15952         const int line_start_pos = buf->size(); (void)line_start_pos;
15953         const ImGuiDockNodeSettings* node_settings = &dc->NodesSettings[node_n];
15954         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
15955         buf->appendf(" ID=0x%08X", node_settings->ID);
15956         if (node_settings->ParentNodeId)
15957         {
15958             buf->appendf(" Parent=0x%08X SizeRef=%d,%d", node_settings->ParentNodeId, node_settings->SizeRef.x, node_settings->SizeRef.y);
15959         }
15960         else
15961         {
15962             if (node_settings->ParentWindowId)
15963                 buf->appendf(" Window=0x%08X", node_settings->ParentWindowId);
15964             buf->appendf(" Pos=%d,%d Size=%d,%d", node_settings->Pos.x, node_settings->Pos.y, node_settings->Size.x, node_settings->Size.y);
15965         }
15966         if (node_settings->SplitAxis != ImGuiAxis_None)
15967             buf->appendf(" Split=%c", (node_settings->SplitAxis == ImGuiAxis_X) ? 'X' : 'Y');
15968         if (node_settings->Flags & ImGuiDockNodeFlags_NoResize)
15969             buf->appendf(" NoResize=1");
15970         if (node_settings->Flags & ImGuiDockNodeFlags_CentralNode)
15971             buf->appendf(" CentralNode=1");
15972         if (node_settings->Flags & ImGuiDockNodeFlags_NoTabBar)
15973             buf->appendf(" NoTabBar=1");
15974         if (node_settings->Flags & ImGuiDockNodeFlags_HiddenTabBar)
15975             buf->appendf(" HiddenTabBar=1");
15976         if (node_settings->Flags & ImGuiDockNodeFlags_NoWindowMenuButton)
15977             buf->appendf(" NoWindowMenuButton=1");
15978         if (node_settings->Flags & ImGuiDockNodeFlags_NoCloseButton)
15979             buf->appendf(" NoCloseButton=1");
15980         if (node_settings->SelectedTabId)
15981             buf->appendf(" Selected=0x%08X", node_settings->SelectedTabId);
15982 
15983 #if IMGUI_DEBUG_INI_SETTINGS
15984         // [DEBUG] Include comments in the .ini file to ease debugging
15985         if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_settings->ID))
15986         {
15987             buf->appendf("%*s", ImMax(2, (line_start_pos + 92) - buf->size()), "");     // Align everything
15988             if (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow)
15989                 buf->appendf(" ; in '%s'", node->HostWindow->ParentWindow->Name);
15990             // Iterate settings so we can give info about windows that didn't exist during the session.
15991             int contains_window = 0;
15992             for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
15993                 if (settings->DockId == node_settings->ID)
15994                 {
15995                     if (contains_window++ == 0)
15996                         buf->appendf(" ; contains ");
15997                     buf->appendf("'%s' ", settings->GetName());
15998                 }
15999         }
16000 #endif
16001         buf->appendf("\n");
16002     }
16003     buf->appendf("\n");
16004 }
16005 
16006 
16007 //-----------------------------------------------------------------------------
16008 // [SECTION] PLATFORM DEPENDENT HELPERS
16009 //-----------------------------------------------------------------------------
16010 
16011 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
16012 
16013 #ifdef _MSC_VER
16014 #pragma comment(lib, "user32")
16015 #pragma comment(lib, "kernel32")
16016 #endif
16017 
16018 // Win32 clipboard implementation
16019 // We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown()
GetClipboardTextFn_DefaultImpl(void *)16020 static const char* GetClipboardTextFn_DefaultImpl(void*)
16021 {
16022     ImGuiContext& g = *GImGui;
16023     g.ClipboardHandlerData.clear();
16024     if (!::OpenClipboard(NULL))
16025         return NULL;
16026     HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
16027     if (wbuf_handle == NULL)
16028     {
16029         ::CloseClipboard();
16030         return NULL;
16031     }
16032     if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle))
16033     {
16034         int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL);
16035         g.ClipboardHandlerData.resize(buf_len);
16036         ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL);
16037     }
16038     ::GlobalUnlock(wbuf_handle);
16039     ::CloseClipboard();
16040     return g.ClipboardHandlerData.Data;
16041 }
16042 
SetClipboardTextFn_DefaultImpl(void *,const char * text)16043 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
16044 {
16045     if (!::OpenClipboard(NULL))
16046         return;
16047     const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);
16048     HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR));
16049     if (wbuf_handle == NULL)
16050     {
16051         ::CloseClipboard();
16052         return;
16053     }
16054     WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle);
16055     ::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length);
16056     ::GlobalUnlock(wbuf_handle);
16057     ::EmptyClipboard();
16058     if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
16059         ::GlobalFree(wbuf_handle);
16060     ::CloseClipboard();
16061 }
16062 
16063 #elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS)
16064 
16065 #include <Carbon/Carbon.h>  // Use old API to avoid need for separate .mm file
16066 static PasteboardRef main_clipboard = 0;
16067 
16068 // OSX clipboard implementation
16069 // If you enable this you will need to add '-framework ApplicationServices' to your linker command-line!
SetClipboardTextFn_DefaultImpl(void *,const char * text)16070 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
16071 {
16072     if (!main_clipboard)
16073         PasteboardCreate(kPasteboardClipboard, &main_clipboard);
16074     PasteboardClear(main_clipboard);
16075     CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text));
16076     if (cf_data)
16077     {
16078         PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0);
16079         CFRelease(cf_data);
16080     }
16081 }
16082 
GetClipboardTextFn_DefaultImpl(void *)16083 static const char* GetClipboardTextFn_DefaultImpl(void*)
16084 {
16085     if (!main_clipboard)
16086         PasteboardCreate(kPasteboardClipboard, &main_clipboard);
16087     PasteboardSynchronize(main_clipboard);
16088 
16089     ItemCount item_count = 0;
16090     PasteboardGetItemCount(main_clipboard, &item_count);
16091     for (ItemCount i = 0; i < item_count; i++)
16092     {
16093         PasteboardItemID item_id = 0;
16094         PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id);
16095         CFArrayRef flavor_type_array = 0;
16096         PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array);
16097         for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++)
16098         {
16099             CFDataRef cf_data;
16100             if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr)
16101             {
16102                 ImGuiContext& g = *GImGui;
16103                 g.ClipboardHandlerData.clear();
16104                 int length = (int)CFDataGetLength(cf_data);
16105                 g.ClipboardHandlerData.resize(length + 1);
16106                 CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data);
16107                 g.ClipboardHandlerData[length] = 0;
16108                 CFRelease(cf_data);
16109                 return g.ClipboardHandlerData.Data;
16110             }
16111         }
16112     }
16113     return NULL;
16114 }
16115 
16116 #else
16117 
16118 // Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers.
GetClipboardTextFn_DefaultImpl(void *)16119 static const char* GetClipboardTextFn_DefaultImpl(void*)
16120 {
16121     ImGuiContext& g = *GImGui;
16122     return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin();
16123 }
16124 
SetClipboardTextFn_DefaultImpl(void *,const char * text)16125 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
16126 {
16127     ImGuiContext& g = *GImGui;
16128     g.ClipboardHandlerData.clear();
16129     const char* text_end = text + strlen(text);
16130     g.ClipboardHandlerData.resize((int)(text_end - text) + 1);
16131     memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text));
16132     g.ClipboardHandlerData[(int)(text_end - text)] = 0;
16133 }
16134 
16135 #endif
16136 
16137 //-----------------------------------------------------------------------------
16138 // [SECTION] METRICS/DEBUGGER WINDOW
16139 //-----------------------------------------------------------------------------
16140 // - RenderViewportThumbnail() [Internal]
16141 // - RenderViewportsThumbnails() [Internal]
16142 // - MetricsHelpMarker() [Internal]
16143 // - ShowMetricsWindow()
16144 // - DebugNodeColumns() [Internal]
16145 // - DebugNodeDockNode() [Internal]
16146 // - DebugNodeDrawList() [Internal]
16147 // - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal]
16148 // - DebugNodeStorage() [Internal]
16149 // - DebugNodeTabBar() [Internal]
16150 // - DebugNodeViewport() [Internal]
16151 // - DebugNodeWindow() [Internal]
16152 // - DebugNodeWindowSettings() [Internal]
16153 // - DebugNodeWindowsList() [Internal]
16154 //-----------------------------------------------------------------------------
16155 
16156 #ifndef IMGUI_DISABLE_METRICS_WINDOW
16157 
DebugRenderViewportThumbnail(ImDrawList * draw_list,ImGuiViewportP * viewport,const ImRect & bb)16158 void ImGui::DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb)
16159 {
16160     ImGuiContext& g = *GImGui;
16161     ImGuiWindow* window = g.CurrentWindow;
16162 
16163     ImVec2 scale = bb.GetSize() / viewport->Size;
16164     ImVec2 off = bb.Min - viewport->Pos * scale;
16165     float alpha_mul = (viewport->Flags & ImGuiViewportFlags_Minimized) ? 0.30f : 1.00f;
16166     window->DrawList->AddRectFilled(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f));
16167     for (int i = 0; i != g.Windows.Size; i++)
16168     {
16169         ImGuiWindow* thumb_window = g.Windows[i];
16170         if (!thumb_window->WasActive || (thumb_window->Flags & ImGuiWindowFlags_ChildWindow))
16171             continue;
16172         if (thumb_window->Viewport != viewport)
16173             continue;
16174 
16175         ImRect thumb_r = thumb_window->Rect();
16176         ImRect title_r = thumb_window->TitleBarRect();
16177         thumb_r = ImRect(ImFloor(off + thumb_r.Min * scale), ImFloor(off +  thumb_r.Max * scale));
16178         title_r = ImRect(ImFloor(off + title_r.Min * scale), ImFloor(off +  ImVec2(title_r.Max.x, title_r.Min.y) * scale) + ImVec2(0,5)); // Exaggerate title bar height
16179         thumb_r.ClipWithFull(bb);
16180         title_r.ClipWithFull(bb);
16181         const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight);
16182         window->DrawList->AddRectFilled(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_WindowBg, alpha_mul));
16183         window->DrawList->AddRectFilled(title_r.Min, title_r.Max, GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg, alpha_mul));
16184         window->DrawList->AddRect(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_Border, alpha_mul));
16185         window->DrawList->AddText(g.Font, g.FontSize * 1.0f, title_r.Min, GetColorU32(ImGuiCol_Text, alpha_mul), thumb_window->Name, FindRenderedTextEnd(thumb_window->Name));
16186     }
16187     draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul));
16188 }
16189 
RenderViewportsThumbnails()16190 static void RenderViewportsThumbnails()
16191 {
16192     ImGuiContext& g = *GImGui;
16193     ImGuiWindow* window = g.CurrentWindow;
16194 
16195     // 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.
16196     float SCALE = 1.0f / 8.0f;
16197     ImRect bb_full(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
16198     for (int n = 0; n < g.Viewports.Size; n++)
16199         bb_full.Add(g.Viewports[n]->GetMainRect());
16200     ImVec2 p = window->DC.CursorPos;
16201     ImVec2 off = p - bb_full.Min * SCALE;
16202     for (int n = 0; n < g.Viewports.Size; n++)
16203     {
16204         ImGuiViewportP* viewport = g.Viewports[n];
16205         ImRect viewport_draw_bb(off + (viewport->Pos) * SCALE, off + (viewport->Pos + viewport->Size) * SCALE);
16206         ImGui::DebugRenderViewportThumbnail(window->DrawList, viewport, viewport_draw_bb);
16207     }
16208     ImGui::Dummy(bb_full.GetSize() * SCALE);
16209 }
16210 
ViewportComparerByFrontMostStampCount(const void * lhs,const void * rhs)16211 static int IMGUI_CDECL ViewportComparerByFrontMostStampCount(const void* lhs, const void* rhs)
16212 {
16213     const ImGuiViewportP* a = *(const ImGuiViewportP* const *)lhs;
16214     const ImGuiViewportP* b = *(const ImGuiViewportP* const *)rhs;
16215     return b->LastFrontMostStampCount - a->LastFrontMostStampCount;
16216 }
16217 
16218 // Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds.
MetricsHelpMarker(const char * desc)16219 static void MetricsHelpMarker(const char* desc)
16220 {
16221     ImGui::TextDisabled("(?)");
16222     if (ImGui::IsItemHovered())
16223     {
16224         ImGui::BeginTooltip();
16225         ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
16226         ImGui::TextUnformatted(desc);
16227         ImGui::PopTextWrapPos();
16228         ImGui::EndTooltip();
16229     }
16230 }
16231 
16232 #ifndef IMGUI_DISABLE_DEMO_WINDOWS
16233 namespace ImGui { void ShowFontAtlas(ImFontAtlas* atlas); }
16234 #endif
16235 
ShowMetricsWindow(bool * p_open)16236 void ImGui::ShowMetricsWindow(bool* p_open)
16237 {
16238     if (!Begin("Dear ImGui Metrics/Debugger", p_open))
16239     {
16240         End();
16241         return;
16242     }
16243 
16244     ImGuiContext& g = *GImGui;
16245     ImGuiIO& io = g.IO;
16246     ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
16247 
16248     // Basic info
16249     Text("Dear ImGui %s", GetVersion());
16250     Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
16251     Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
16252     Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
16253     Text("%d active allocations", io.MetricsActiveAllocations);
16254     //SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; }
16255 
16256     Separator();
16257 
16258     // Debugging enums
16259     enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentIdeal, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type
16260     const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentIdeal", "ContentRegionRect" };
16261     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
16262     const char* trt_rects_names[TRT_Count] = { "OuterRect", "InnerRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsWorkRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentFrozen", "ColumnsContentUnfrozen" };
16263     if (cfg->ShowWindowsRectsType < 0)
16264         cfg->ShowWindowsRectsType = WRT_WorkRect;
16265     if (cfg->ShowTablesRectsType < 0)
16266         cfg->ShowTablesRectsType = TRT_WorkRect;
16267 
16268     struct Funcs
16269     {
16270         static ImRect GetTableRect(ImGuiTable* table, int rect_type, int n)
16271         {
16272             if (rect_type == TRT_OuterRect)                     { return table->OuterRect; }
16273             else if (rect_type == TRT_InnerRect)                { return table->InnerRect; }
16274             else if (rect_type == TRT_WorkRect)                 { return table->WorkRect; }
16275             else if (rect_type == TRT_HostClipRect)             { return table->HostClipRect; }
16276             else if (rect_type == TRT_InnerClipRect)            { return table->InnerClipRect; }
16277             else if (rect_type == TRT_BackgroundClipRect)       { return table->BgClipRect; }
16278             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); }
16279             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); }
16280             else if (rect_type == TRT_ColumnsClipRect)          { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; }
16281             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
16282             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); }
16283             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); }
16284             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); }
16285             IM_ASSERT(0);
16286             return ImRect();
16287         }
16288 
16289         static ImRect GetWindowRect(ImGuiWindow* window, int rect_type)
16290         {
16291             if (rect_type == WRT_OuterRect)                 { return window->Rect(); }
16292             else if (rect_type == WRT_OuterRectClipped)     { return window->OuterRectClipped; }
16293             else if (rect_type == WRT_InnerRect)            { return window->InnerRect; }
16294             else if (rect_type == WRT_InnerClipRect)        { return window->InnerClipRect; }
16295             else if (rect_type == WRT_WorkRect)             { return window->WorkRect; }
16296             else if (rect_type == WRT_Content)       { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
16297             else if (rect_type == WRT_ContentIdeal)         { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSizeIdeal); }
16298             else if (rect_type == WRT_ContentRegionRect)    { return window->ContentRegionRect; }
16299             IM_ASSERT(0);
16300             return ImRect();
16301         }
16302     };
16303 
16304     // Tools
16305     if (TreeNode("Tools"))
16306     {
16307         // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
16308         if (Button("Item Picker.."))
16309             DebugStartItemPicker();
16310         SameLine();
16311         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.");
16312 
16313         Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder);
16314         Checkbox("Show windows rectangles", &cfg->ShowWindowsRects);
16315         SameLine();
16316         SetNextItemWidth(GetFontSize() * 12);
16317         cfg->ShowWindowsRects |= Combo("##show_windows_rect_type", &cfg->ShowWindowsRectsType, wrt_rects_names, WRT_Count, WRT_Count);
16318         if (cfg->ShowWindowsRects && g.NavWindow != NULL)
16319         {
16320             BulletText("'%s':", g.NavWindow->Name);
16321             Indent();
16322             for (int rect_n = 0; rect_n < WRT_Count; rect_n++)
16323             {
16324                 ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n);
16325                 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]);
16326             }
16327             Unindent();
16328         }
16329         Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh);
16330         Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes);
16331 
16332         Checkbox("Show tables rectangles", &cfg->ShowTablesRects);
16333         SameLine();
16334         SetNextItemWidth(GetFontSize() * 12);
16335         cfg->ShowTablesRects |= Combo("##show_table_rects_type", &cfg->ShowTablesRectsType, trt_rects_names, TRT_Count, TRT_Count);
16336         if (cfg->ShowTablesRects && g.NavWindow != NULL)
16337         {
16338             for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)
16339             {
16340                 ImGuiTable* table = g.Tables.TryGetMapData(table_n);
16341                 if (table == NULL || table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow))
16342                     continue;
16343 
16344                 BulletText("Table 0x%08X (%d columns, in '%s')", table->ID, table->ColumnsCount, table->OuterWindow->Name);
16345                 if (IsItemHovered())
16346                     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);
16347                 Indent();
16348                 char buf[128];
16349                 for (int rect_n = 0; rect_n < TRT_Count; rect_n++)
16350                 {
16351                     if (rect_n >= TRT_ColumnsRect)
16352                     {
16353                         if (rect_n != TRT_ColumnsRect && rect_n != TRT_ColumnsClipRect)
16354                             continue;
16355                         for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
16356                         {
16357                             ImRect r = Funcs::GetTableRect(table, rect_n, column_n);
16358                             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]);
16359                             Selectable(buf);
16360                             if (IsItemHovered())
16361                                 GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
16362                         }
16363                     }
16364                     else
16365                     {
16366                         ImRect r = Funcs::GetTableRect(table, rect_n, -1);
16367                         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]);
16368                         Selectable(buf);
16369                         if (IsItemHovered())
16370                             GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
16371                     }
16372                 }
16373                 Unindent();
16374             }
16375         }
16376 
16377         TreePop();
16378     }
16379 
16380     // Windows
16381     DebugNodeWindowsList(&g.Windows, "Windows");
16382     //DebugNodeWindowsList(&g.WindowsFocusOrder, "WindowsFocusOrder");
16383 
16384     // DrawLists
16385     int drawlist_count = 0;
16386     for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++)
16387         drawlist_count += g.Viewports[viewport_i]->DrawDataBuilder.GetDrawListCount();
16388     if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count))
16389     {
16390         for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++)
16391         {
16392             ImGuiViewportP* viewport = g.Viewports[viewport_i];
16393             bool viewport_has_drawlist = false;
16394             for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++)
16395                 for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++)
16396                 {
16397                     if (!viewport_has_drawlist)
16398                         Text("Active DrawLists in Viewport #%d, ID: 0x%08X", viewport->Idx, viewport->ID);
16399                     viewport_has_drawlist = true;
16400                     DebugNodeDrawList(NULL, viewport, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList");
16401                 }
16402         }
16403         TreePop();
16404     }
16405 
16406     // Viewports
16407     if (TreeNode("Viewports", "Viewports (%d)", g.Viewports.Size))
16408     {
16409         Indent(GetTreeNodeToLabelSpacing());
16410         RenderViewportsThumbnails();
16411         Unindent(GetTreeNodeToLabelSpacing());
16412 
16413         bool open = TreeNode("Monitors", "Monitors (%d)", g.PlatformIO.Monitors.Size);
16414         SameLine();
16415         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.");
16416         if (open)
16417         {
16418             for (int i = 0; i < g.PlatformIO.Monitors.Size; i++)
16419             {
16420                 const ImGuiPlatformMonitor& mon = g.PlatformIO.Monitors[i];
16421                 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)",
16422                     i, mon.DpiScale * 100.0f,
16423                     mon.MainPos.x, mon.MainPos.y, mon.MainPos.x + mon.MainSize.x, mon.MainPos.y + mon.MainSize.y, mon.MainSize.x, mon.MainSize.y,
16424                     mon.WorkPos.x, mon.WorkPos.y, mon.WorkPos.x + mon.WorkSize.x, mon.WorkPos.y + mon.WorkSize.y, mon.WorkSize.x, mon.WorkSize.y);
16425             }
16426             TreePop();
16427         }
16428 
16429         if (TreeNode("Inferred order (front-to-back)"))
16430         {
16431             static ImVector<ImGuiViewportP*> viewports;
16432             viewports.resize(g.Viewports.Size);
16433             memcpy(viewports.Data, g.Viewports.Data, g.Viewports.size_in_bytes());
16434             if (viewports.Size > 1)
16435                 ImQsort(viewports.Data, viewports.Size, sizeof(ImGuiViewport*), ViewportComparerByFrontMostStampCount);
16436             for (int i = 0; i < viewports.Size; i++)
16437                 BulletText("Viewport #%d, ID: 0x%08X, FrontMostStampCount = %08d, Window: \"%s\"", viewports[i]->Idx, viewports[i]->ID, viewports[i]->LastFrontMostStampCount, viewports[i]->Window ? viewports[i]->Window->Name : "N/A");
16438             TreePop();
16439         }
16440 
16441         for (int i = 0; i < g.Viewports.Size; i++)
16442             DebugNodeViewport(g.Viewports[i]);
16443         TreePop();
16444     }
16445 
16446     // Details for Popups
16447     if (TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
16448     {
16449         for (int i = 0; i < g.OpenPopupStack.Size; i++)
16450         {
16451             ImGuiWindow* window = g.OpenPopupStack[i].Window;
16452             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" : "");
16453         }
16454         TreePop();
16455     }
16456 
16457     // Details for TabBars
16458     if (TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetAliveCount()))
16459     {
16460         for (int n = 0; n < g.TabBars.GetMapSize(); n++)
16461             if (ImGuiTabBar* tab_bar = g.TabBars.TryGetMapData(n))
16462             {
16463                 PushID(tab_bar);
16464                 DebugNodeTabBar(tab_bar, "TabBar");
16465                 PopID();
16466             }
16467         TreePop();
16468     }
16469 
16470     // Details for Tables
16471     if (TreeNode("Tables", "Tables (%d)", g.Tables.GetAliveCount()))
16472     {
16473         for (int n = 0; n < g.Tables.GetMapSize(); n++)
16474             if (ImGuiTable* table = g.Tables.TryGetMapData(n))
16475                 DebugNodeTable(table);
16476         TreePop();
16477     }
16478 
16479     // Details for Fonts
16480 #ifndef IMGUI_DISABLE_DEMO_WINDOWS
16481     ImFontAtlas* atlas = g.IO.Fonts;
16482     if (TreeNode("Fonts", "Fonts (%d)", atlas->Fonts.Size))
16483     {
16484         ShowFontAtlas(atlas);
16485         TreePop();
16486     }
16487 #endif
16488 
16489     // Details for Docking
16490 #ifdef IMGUI_HAS_DOCK
16491     if (TreeNode("Docking"))
16492     {
16493         static bool root_nodes_only = true;
16494         ImGuiDockContext* dc = &g.DockContext;
16495         Checkbox("List root nodes", &root_nodes_only);
16496         Checkbox("Ctrl shows window dock info", &cfg->ShowDockingNodes);
16497         if (SmallButton("Clear nodes")) { DockContextClearNodes(&g, 0, true); }
16498         SameLine();
16499         if (SmallButton("Rebuild all")) { dc->WantFullRebuild = true; }
16500         for (int n = 0; n < dc->Nodes.Data.Size; n++)
16501             if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
16502                 if (!root_nodes_only || node->IsRootNode())
16503                     DebugNodeDockNode(node, "Node");
16504         TreePop();
16505     }
16506 #endif // #ifdef IMGUI_HAS_DOCK
16507 
16508     // Settings
16509     if (TreeNode("Settings"))
16510     {
16511         if (SmallButton("Clear"))
16512             ClearIniSettings();
16513         SameLine();
16514         if (SmallButton("Save to memory"))
16515             SaveIniSettingsToMemory();
16516         SameLine();
16517         if (SmallButton("Save to disk"))
16518             SaveIniSettingsToDisk(g.IO.IniFilename);
16519         SameLine();
16520         if (g.IO.IniFilename)
16521             Text("\"%s\"", g.IO.IniFilename);
16522         else
16523             TextUnformatted("<NULL>");
16524         Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer);
16525         if (TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size))
16526         {
16527             for (int n = 0; n < g.SettingsHandlers.Size; n++)
16528                 BulletText("%s", g.SettingsHandlers[n].TypeName);
16529             TreePop();
16530         }
16531         if (TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size()))
16532         {
16533             for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
16534                 DebugNodeWindowSettings(settings);
16535             TreePop();
16536         }
16537 
16538         if (TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size()))
16539         {
16540             for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))
16541                 DebugNodeTableSettings(settings);
16542             TreePop();
16543         }
16544 
16545 #ifdef IMGUI_HAS_DOCK
16546         if (TreeNode("SettingsDocking", "Settings packed data: Docking"))
16547         {
16548             ImGuiDockContext* dc = &g.DockContext;
16549             Text("In SettingsWindows:");
16550             for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
16551                 if (settings->DockId != 0)
16552                     BulletText("Window '%s' -> DockId %08X", settings->GetName(), settings->DockId);
16553             Text("In SettingsNodes:");
16554             for (int n = 0; n < dc->NodesSettings.Size; n++)
16555             {
16556                 ImGuiDockNodeSettings* settings = &dc->NodesSettings[n];
16557                 const char* selected_tab_name = NULL;
16558                 if (settings->SelectedTabId)
16559                 {
16560                     if (ImGuiWindow* window = FindWindowByID(settings->SelectedTabId))
16561                         selected_tab_name = window->Name;
16562                     else if (ImGuiWindowSettings* window_settings = FindWindowSettings(settings->SelectedTabId))
16563                         selected_tab_name = window_settings->GetName();
16564                 }
16565                 BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings->ID, settings->ParentNodeId, settings->SelectedTabId, selected_tab_name ? selected_tab_name : settings->SelectedTabId ? "N/A" : "");
16566             }
16567             TreePop();
16568         }
16569 #endif // #ifdef IMGUI_HAS_DOCK
16570 
16571         if (TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size()))
16572         {
16573             InputTextMultiline("##Ini", (char*)(void*)g.SettingsIniData.c_str(), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, GetTextLineHeight() * 20), ImGuiInputTextFlags_ReadOnly);
16574             TreePop();
16575         }
16576         TreePop();
16577     }
16578 
16579     // Misc Details
16580     if (TreeNode("Internal state"))
16581     {
16582         const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad", "Nav", "Clipboard" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
16583 
16584         Text("WINDOWING");
16585         Indent();
16586         Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
16587         Text("HoveredWindow->Root: '%s'", g.HoveredWindow ? g.HoveredWindow->RootWindowDockTree->Name : "NULL");
16588         Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL");
16589         Text("HoveredDockNode: 0x%08X", g.HoveredDockNode ? g.HoveredDockNode->ID : 0);
16590         Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
16591         Text("MouseViewport: 0x%08X (UserHovered 0x%08X, LastHovered 0x%08X)", g.MouseViewport->ID, g.IO.MouseHoveredViewport, g.MouseLastHoveredViewport ? g.MouseLastHoveredViewport->ID : 0);
16592         Unindent();
16593 
16594         Text("ITEMS");
16595         Indent();
16596         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]);
16597         Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
16598         Text("ActiveIdUsing: Wheel: %d, NavDirMask: %X, NavInputMask: %X, KeyInputMask: %llX", g.ActiveIdUsingMouseWheel, g.ActiveIdUsingNavDirMask, g.ActiveIdUsingNavInputMask, g.ActiveIdUsingKeyInputMask);
16599         Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame
16600         Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
16601         Unindent();
16602 
16603         Text("NAV,FOCUS");
16604         Indent();
16605         Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
16606         Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
16607         Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
16608         Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
16609         Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
16610         Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
16611         Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId);
16612         Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
16613         Unindent();
16614 
16615         TreePop();
16616     }
16617 
16618     // Overlay: Display windows Rectangles and Begin Order
16619     if (cfg->ShowWindowsRects || cfg->ShowWindowsBeginOrder)
16620     {
16621         for (int n = 0; n < g.Windows.Size; n++)
16622         {
16623             ImGuiWindow* window = g.Windows[n];
16624             if (!window->WasActive)
16625                 continue;
16626             ImDrawList* draw_list = GetForegroundDrawList(window);
16627             if (cfg->ShowWindowsRects)
16628             {
16629                 ImRect r = Funcs::GetWindowRect(window, cfg->ShowWindowsRectsType);
16630                 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
16631             }
16632             if (cfg->ShowWindowsBeginOrder && !(window->Flags & ImGuiWindowFlags_ChildWindow))
16633             {
16634                 char buf[32];
16635                 ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
16636                 float font_size = GetFontSize();
16637                 draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
16638                 draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf);
16639             }
16640         }
16641     }
16642 
16643     // Overlay: Display Tables Rectangles
16644     if (cfg->ShowTablesRects)
16645     {
16646         for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)
16647         {
16648             ImGuiTable* table = g.Tables.TryGetMapData(table_n);
16649             if (table == NULL || table->LastFrameActive < g.FrameCount - 1)
16650                 continue;
16651             ImDrawList* draw_list = GetForegroundDrawList(table->OuterWindow);
16652             if (cfg->ShowTablesRectsType >= TRT_ColumnsRect)
16653             {
16654                 for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
16655                 {
16656                     ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, column_n);
16657                     ImU32 col = (table->HoveredColumnBody == column_n) ? IM_COL32(255, 255, 128, 255) : IM_COL32(255, 0, 128, 255);
16658                     float thickness = (table->HoveredColumnBody == column_n) ? 3.0f : 1.0f;
16659                     draw_list->AddRect(r.Min, r.Max, col, 0.0f, 0, thickness);
16660                 }
16661             }
16662             else
16663             {
16664                 ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, -1);
16665                 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
16666             }
16667         }
16668     }
16669 
16670 #ifdef IMGUI_HAS_DOCK
16671     // Overlay: Display Docking info
16672     if (cfg->ShowDockingNodes && g.IO.KeyCtrl && g.HoveredDockNode)
16673     {
16674         char buf[64] = "";
16675         char* p = buf;
16676         ImGuiDockNode* node = g.HoveredDockNode;
16677         ImDrawList* overlay_draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport());
16678         p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "DockId: %X%s\n", node->ID, node->IsCentralNode() ? " *CentralNode*" : "");
16679         p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "WindowClass: %08X\n", node->WindowClass.ClassId);
16680         p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "Size: (%.0f, %.0f)\n", node->Size.x, node->Size.y);
16681         p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "SizeRef: (%.0f, %.0f)\n", node->SizeRef.x, node->SizeRef.y);
16682         int depth = DockNodeGetDepth(node);
16683         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));
16684         ImVec2 pos = node->Pos + ImVec2(3, 3) * (float)depth;
16685         overlay_draw_list->AddRectFilled(pos - ImVec2(1, 1), pos + CalcTextSize(buf) + ImVec2(1, 1), IM_COL32(200, 100, 100, 255));
16686         overlay_draw_list->AddText(NULL, 0.0f, pos, IM_COL32(255, 255, 255, 255), buf);
16687     }
16688 #endif // #ifdef IMGUI_HAS_DOCK
16689 
16690     End();
16691 }
16692 
16693 // [DEBUG] List fonts in a font atlas and display its texture
ShowFontAtlas(ImFontAtlas * atlas)16694 void ImGui::ShowFontAtlas(ImFontAtlas* atlas)
16695 {
16696     for (int i = 0; i < atlas->Fonts.Size; i++)
16697     {
16698         ImFont* font = atlas->Fonts[i];
16699         PushID(font);
16700         DebugNodeFont(font);
16701         PopID();
16702     }
16703     if (TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight))
16704     {
16705         ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
16706         ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f);
16707         Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), tint_col, border_col);
16708         TreePop();
16709     }
16710 }
16711 
16712 // [DEBUG] Display contents of Columns
DebugNodeColumns(ImGuiOldColumns * columns)16713 void ImGui::DebugNodeColumns(ImGuiOldColumns* columns)
16714 {
16715     if (!TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
16716         return;
16717     BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX);
16718     for (int column_n = 0; column_n < columns->Columns.Size; column_n++)
16719         BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, GetColumnOffsetFromNorm(columns, columns->Columns[column_n].OffsetNorm));
16720     TreePop();
16721 }
16722 
DebugNodeDockNodeFlags(ImGuiDockNodeFlags * p_flags,const char * label,bool enabled)16723 static void DebugNodeDockNodeFlags(ImGuiDockNodeFlags* p_flags, const char* label, bool enabled)
16724 {
16725     using namespace ImGui;
16726     PushID(label);
16727     PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));
16728     Text("%s:", label);
16729     if (!enabled)
16730         PushDisabled();
16731     CheckboxFlags("NoSplit", p_flags, ImGuiDockNodeFlags_NoSplit);
16732     CheckboxFlags("NoResize", p_flags, ImGuiDockNodeFlags_NoResize);
16733     CheckboxFlags("NoResizeX", p_flags, ImGuiDockNodeFlags_NoResizeX);
16734     CheckboxFlags("NoResizeY",p_flags, ImGuiDockNodeFlags_NoResizeY);
16735     CheckboxFlags("NoTabBar", p_flags, ImGuiDockNodeFlags_NoTabBar);
16736     CheckboxFlags("HiddenTabBar", p_flags, ImGuiDockNodeFlags_HiddenTabBar);
16737     CheckboxFlags("NoWindowMenuButton", p_flags, ImGuiDockNodeFlags_NoWindowMenuButton);
16738     CheckboxFlags("NoCloseButton", p_flags, ImGuiDockNodeFlags_NoCloseButton);
16739     CheckboxFlags("NoDocking", p_flags, ImGuiDockNodeFlags_NoDocking);
16740     CheckboxFlags("NoDockingSplitMe", p_flags, ImGuiDockNodeFlags_NoDockingSplitMe);
16741     CheckboxFlags("NoDockingSplitOther", p_flags, ImGuiDockNodeFlags_NoDockingSplitOther);
16742     CheckboxFlags("NoDockingOverMe", p_flags, ImGuiDockNodeFlags_NoDockingOverMe);
16743     CheckboxFlags("NoDockingOverOther", p_flags, ImGuiDockNodeFlags_NoDockingOverOther);
16744     CheckboxFlags("NoDockingOverEmpty", p_flags, ImGuiDockNodeFlags_NoDockingOverEmpty);
16745     if (!enabled)
16746         PopDisabled();
16747     PopStyleVar();
16748     PopID();
16749 }
16750 
16751 // [DEBUG] Display contents of ImDockNode
DebugNodeDockNode(ImGuiDockNode * node,const char * label)16752 void ImGui::DebugNodeDockNode(ImGuiDockNode* node, const char* label)
16753 {
16754     ImGuiContext& g = *GImGui;
16755     const bool is_alive = (g.FrameCount - node->LastFrameAlive < 2);    // Submitted with ImGuiDockNodeFlags_KeepAliveOnly
16756     const bool is_active = (g.FrameCount - node->LastFrameActive < 2);  // Submitted
16757     if (!is_alive) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
16758     bool open;
16759     if (node->Windows.Size > 0)
16760         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");
16761     else
16762         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");
16763     if (!is_alive) { PopStyleColor(); }
16764     if (is_active && IsItemHovered())
16765         if (ImGuiWindow* window = node->HostWindow ? node->HostWindow : node->VisibleWindow)
16766             GetForegroundDrawList(window)->AddRect(node->Pos, node->Pos + node->Size, IM_COL32(255, 255, 0, 255));
16767     if (open)
16768     {
16769         IM_ASSERT(node->ChildNodes[0] == NULL || node->ChildNodes[0]->ParentNode == node);
16770         IM_ASSERT(node->ChildNodes[1] == NULL || node->ChildNodes[1]->ParentNode == node);
16771         BulletText("Pos (%.0f,%.0f), Size (%.0f, %.0f) Ref (%.0f, %.0f)",
16772             node->Pos.x, node->Pos.y, node->Size.x, node->Size.y, node->SizeRef.x, node->SizeRef.y);
16773         DebugNodeWindow(node->HostWindow, "HostWindow");
16774         DebugNodeWindow(node->VisibleWindow, "VisibleWindow");
16775         BulletText("SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X", node->SelectedTabId, node->LastFocusedNodeId);
16776         BulletText("Misc:%s%s%s%s%s",
16777             node->IsDockSpace() ? " IsDockSpace" : "",
16778             node->IsCentralNode() ? " IsCentralNode" : "",
16779             is_alive ? " IsAlive" : "", is_active ? " IsActive" : "",
16780             node->WantLockSizeOnce ? " WantLockSizeOnce" : "");
16781         if (TreeNode("flags", "Flags Merged: 0x%04X, Local: 0x%04X, InWindows: 0x%04X, Shared: 0x%04X", node->MergedFlags, node->LocalFlags, node->LocalFlagsInWindows, node->SharedFlags))
16782         {
16783             if (BeginTable("flags", 4))
16784             {
16785                 TableNextColumn(); DebugNodeDockNodeFlags(&node->MergedFlags, "MergedFlags", false);
16786                 TableNextColumn(); DebugNodeDockNodeFlags(&node->LocalFlags, "LocalFlags", true);
16787                 TableNextColumn(); DebugNodeDockNodeFlags(&node->LocalFlagsInWindows, "LocalFlagsInWindows", false);
16788                 TableNextColumn(); DebugNodeDockNodeFlags(&node->SharedFlags, "SharedFlags", true);
16789                 EndTable();
16790             }
16791             TreePop();
16792         }
16793         if (node->ParentNode)
16794             DebugNodeDockNode(node->ParentNode, "ParentNode");
16795         if (node->ChildNodes[0])
16796             DebugNodeDockNode(node->ChildNodes[0], "Child[0]");
16797         if (node->ChildNodes[1])
16798             DebugNodeDockNode(node->ChildNodes[1], "Child[1]");
16799         if (node->TabBar)
16800             DebugNodeTabBar(node->TabBar, "TabBar");
16801         TreePop();
16802     }
16803 }
16804 
16805 // [DEBUG] Display contents of ImDrawList
16806 // 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)16807 void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label)
16808 {
16809     ImGuiContext& g = *GImGui;
16810     ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
16811     int cmd_count = draw_list->CmdBuffer.Size;
16812     if (cmd_count > 0 && draw_list->CmdBuffer.back().ElemCount == 0 && draw_list->CmdBuffer.back().UserCallback == NULL)
16813         cmd_count--;
16814     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);
16815     if (draw_list == GetWindowDrawList())
16816     {
16817         SameLine();
16818         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)
16819         if (node_open)
16820             TreePop();
16821         return;
16822     }
16823 
16824     ImDrawList* fg_draw_list = viewport ? GetForegroundDrawList(viewport) : NULL; // Render additional visuals into the top-most draw list
16825     if (window && fg_draw_list && IsItemHovered())
16826         fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
16827     if (!node_open)
16828         return;
16829 
16830     if (window && !window->WasActive)
16831         TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!");
16832 
16833     for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.Data; pcmd < draw_list->CmdBuffer.Data + cmd_count; pcmd++)
16834     {
16835         if (pcmd->UserCallback)
16836         {
16837             BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
16838             continue;
16839         }
16840 
16841         char buf[300];
16842         ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d tris, Tex 0x%p, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
16843             pcmd->ElemCount / 3, (void*)(intptr_t)pcmd->TextureId,
16844             pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
16845         bool pcmd_node_open = TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf);
16846         if (IsItemHovered() && (cfg->ShowDrawCmdMesh || cfg->ShowDrawCmdBoundingBoxes) && fg_draw_list)
16847             DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, cfg->ShowDrawCmdMesh, cfg->ShowDrawCmdBoundingBoxes);
16848         if (!pcmd_node_open)
16849             continue;
16850 
16851         // Calculate approximate coverage area (touched pixel count)
16852         // This will be in pixels squared as long there's no post-scaling happening to the renderer output.
16853         const ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
16854         const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + pcmd->VtxOffset;
16855         float total_area = 0.0f;
16856         for (unsigned int idx_n = pcmd->IdxOffset; idx_n < pcmd->IdxOffset + pcmd->ElemCount; )
16857         {
16858             ImVec2 triangle[3];
16859             for (int n = 0; n < 3; n++, idx_n++)
16860                 triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos;
16861             total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]);
16862         }
16863 
16864         // Display vertex information summary. Hover to get all triangles drawn in wire-frame
16865         ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area);
16866         Selectable(buf);
16867         if (IsItemHovered() && fg_draw_list)
16868             DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, true, false);
16869 
16870         // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
16871         ImGuiListClipper clipper;
16872         clipper.Begin(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
16873         while (clipper.Step())
16874             for (int prim = clipper.DisplayStart, idx_i = pcmd->IdxOffset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++)
16875             {
16876                 char* buf_p = buf, * buf_end = buf + IM_ARRAYSIZE(buf);
16877                 ImVec2 triangle[3];
16878                 for (int n = 0; n < 3; n++, idx_i++)
16879                 {
16880                     const ImDrawVert& v = vtx_buffer[idx_buffer ? idx_buffer[idx_i] : idx_i];
16881                     triangle[n] = v.pos;
16882                     buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n",
16883                         (n == 0) ? "Vert:" : "     ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
16884                 }
16885 
16886                 Selectable(buf, false);
16887                 if (fg_draw_list && IsItemHovered())
16888                 {
16889                     ImDrawListFlags backup_flags = fg_draw_list->Flags;
16890                     fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
16891                     fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f);
16892                     fg_draw_list->Flags = backup_flags;
16893                 }
16894             }
16895         TreePop();
16896     }
16897     TreePop();
16898 }
16899 
16900 // [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)16901 void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb)
16902 {
16903     IM_ASSERT(show_mesh || show_aabb);
16904     ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
16905     ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset;
16906 
16907     // Draw wire-frame version of all triangles
16908     ImRect clip_rect = draw_cmd->ClipRect;
16909     ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
16910     ImDrawListFlags backup_flags = out_draw_list->Flags;
16911     out_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
16912     for (unsigned int idx_n = draw_cmd->IdxOffset; idx_n < draw_cmd->IdxOffset + draw_cmd->ElemCount; )
16913     {
16914         ImVec2 triangle[3];
16915         for (int n = 0; n < 3; n++, idx_n++)
16916             vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos));
16917         if (show_mesh)
16918             out_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f); // In yellow: mesh triangles
16919     }
16920     // Draw bounding boxes
16921     if (show_aabb)
16922     {
16923         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
16924         out_draw_list->AddRect(ImFloor(vtxs_rect.Min), ImFloor(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles
16925     }
16926     out_draw_list->Flags = backup_flags;
16927 }
16928 
16929 // [DEBUG] Display details for a single font, called by ShowStyleEditor().
DebugNodeFont(ImFont * font)16930 void ImGui::DebugNodeFont(ImFont* font)
16931 {
16932     bool opened = TreeNode(font, "Font: \"%s\"\n%.2f px, %d glyphs, %d file(s)",
16933         font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size, font->ConfigDataCount);
16934     SameLine();
16935     if (SmallButton("Set as default"))
16936         GetIO().FontDefault = font;
16937     if (!opened)
16938         return;
16939 
16940     // Display preview text
16941     PushFont(font);
16942     Text("The quick brown fox jumps over the lazy dog");
16943     PopFont();
16944 
16945     // Display details
16946     SetNextItemWidth(GetFontSize() * 8);
16947     DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f");
16948     SameLine(); MetricsHelpMarker(
16949         "Note than the default embedded font is NOT meant to be scaled.\n\n"
16950         "Font are currently rendered into bitmaps at a given size at the time of building the atlas. "
16951         "You may oversample them to get some flexibility with scaling. "
16952         "You can also render at multiple sizes and select which one to use at runtime.\n\n"
16953         "(Glimmer of hope: the atlas system will be rewritten in the future to make scaling more flexible.)");
16954     Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent);
16955     char c_str[5];
16956     Text("Fallback character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->FallbackChar), font->FallbackChar);
16957     Text("Ellipsis character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->EllipsisChar), font->EllipsisChar);
16958     const int surface_sqrt = (int)ImSqrt((float)font->MetricsTotalSurface);
16959     Text("Texture Area: about %d px ~%dx%d px", font->MetricsTotalSurface, surface_sqrt, surface_sqrt);
16960     for (int config_i = 0; config_i < font->ConfigDataCount; config_i++)
16961         if (font->ConfigData)
16962             if (const ImFontConfig* cfg = &font->ConfigData[config_i])
16963                 BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d, Offset: (%.1f,%.1f)",
16964                     config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH, cfg->GlyphOffset.x, cfg->GlyphOffset.y);
16965 
16966     // Display all glyphs of the fonts in separate pages of 256 characters
16967     if (TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size))
16968     {
16969         ImDrawList* draw_list = GetWindowDrawList();
16970         const ImU32 glyph_col = GetColorU32(ImGuiCol_Text);
16971         const float cell_size = font->FontSize * 1;
16972         const float cell_spacing = GetStyle().ItemSpacing.y;
16973         for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256)
16974         {
16975             // Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k)
16976             // This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT
16977             // is large // (if ImWchar==ImWchar32 we will do at least about 272 queries here)
16978             if (!(base & 4095) && font->IsGlyphRangeUnused(base, base + 4095))
16979             {
16980                 base += 4096 - 256;
16981                 continue;
16982             }
16983 
16984             int count = 0;
16985             for (unsigned int n = 0; n < 256; n++)
16986                 if (font->FindGlyphNoFallback((ImWchar)(base + n)))
16987                     count++;
16988             if (count <= 0)
16989                 continue;
16990             if (!TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph"))
16991                 continue;
16992 
16993             // Draw a 16x16 grid of glyphs
16994             ImVec2 base_pos = GetCursorScreenPos();
16995             for (unsigned int n = 0; n < 256; n++)
16996             {
16997                 // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions
16998                 // available here and thus cannot easily generate a zero-terminated UTF-8 encoded string.
16999                 ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing));
17000                 ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size);
17001                 const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n));
17002                 draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50));
17003                 if (glyph)
17004                     font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n));
17005                 if (glyph && IsMouseHoveringRect(cell_p1, cell_p2))
17006                 {
17007                     BeginTooltip();
17008                     Text("Codepoint: U+%04X", base + n);
17009                     Separator();
17010                     Text("Visible: %d", glyph->Visible);
17011                     Text("AdvanceX: %.1f", glyph->AdvanceX);
17012                     Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1);
17013                     Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1);
17014                     EndTooltip();
17015                 }
17016             }
17017             Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16));
17018             TreePop();
17019         }
17020         TreePop();
17021     }
17022     TreePop();
17023 }
17024 
17025 // [DEBUG] Display contents of ImGuiStorage
DebugNodeStorage(ImGuiStorage * storage,const char * label)17026 void ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label)
17027 {
17028     if (!TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes()))
17029         return;
17030     for (int n = 0; n < storage->Data.Size; n++)
17031     {
17032         const ImGuiStorage::ImGuiStoragePair& p = storage->Data[n];
17033         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.
17034     }
17035     TreePop();
17036 }
17037 
17038 // [DEBUG] Display contents of ImGuiTabBar
DebugNodeTabBar(ImGuiTabBar * tab_bar,const char * label)17039 void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label)
17040 {
17041     // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
17042     char buf[256];
17043     char* p = buf;
17044     const char* buf_end = buf + IM_ARRAYSIZE(buf);
17045     const bool is_active = (tab_bar->PrevFrameVisible >= GetFrameCount() - 2);
17046     p += ImFormatString(p, buf_end - p, "%s 0x%08X (%d tabs)%s", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*");
17047     p += ImFormatString(p, buf_end - p, "  { ");
17048     for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++)
17049     {
17050         ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
17051         p += ImFormatString(p, buf_end - p, "%s'%s'",
17052             tab_n > 0 ? ", " : "", (tab->Window || tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???");
17053     }
17054     p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } ");
17055     if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
17056     bool open = TreeNode(label, "%s", buf);
17057     if (!is_active) { PopStyleColor(); }
17058     if (is_active && IsItemHovered())
17059     {
17060         ImDrawList* draw_list = GetForegroundDrawList();
17061         draw_list->AddRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, IM_COL32(255, 255, 0, 255));
17062         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));
17063         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));
17064     }
17065     if (open)
17066     {
17067         for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
17068         {
17069             const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
17070             PushID(tab);
17071             if (SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } SameLine(0, 2);
17072             if (SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } SameLine();
17073             Text("%02d%c Tab 0x%08X '%s' Offset: %.1f, Width: %.1f/%.1f",
17074                 tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->Window || tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???", tab->Offset, tab->Width, tab->ContentWidth);
17075             PopID();
17076         }
17077         TreePop();
17078     }
17079 }
17080 
DebugNodeViewport(ImGuiViewportP * viewport)17081 void ImGui::DebugNodeViewport(ImGuiViewportP* viewport)
17082 {
17083     SetNextItemOpen(true, ImGuiCond_Once);
17084     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"))
17085     {
17086         ImGuiWindowFlags flags = viewport->Flags;
17087         BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Offset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f\nMonitor: %d, DpiScale: %.0f%%",
17088             viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y,
17089             viewport->WorkOffsetMin.x, viewport->WorkOffsetMin.y, viewport->WorkOffsetMax.x, viewport->WorkOffsetMax.y,
17090             viewport->PlatformMonitor, viewport->DpiScale * 100.0f);
17091         if (viewport->Idx > 0) { SameLine(); if (SmallButton("Reset Pos")) { viewport->Pos = ImVec2(200, 200); viewport->UpdateWorkRect(); if (viewport->Window) viewport->Window->Pos = viewport->Pos; } }
17092         BulletText("Flags: 0x%04X =%s%s%s%s%s%s%s%s%s%s%s%s", viewport->Flags,
17093             //(flags & ImGuiViewportFlags_IsPlatformWindow) ? " IsPlatformWindow" : "", // Omitting because it is the standard
17094             (flags & ImGuiViewportFlags_IsPlatformMonitor) ? " IsPlatformMonitor" : "",
17095             (flags & ImGuiViewportFlags_OwnedByApp) ? " OwnedByApp" : "",
17096             (flags & ImGuiViewportFlags_NoDecoration) ? " NoDecoration" : "",
17097             (flags & ImGuiViewportFlags_NoTaskBarIcon) ? " NoTaskBarIcon" : "",
17098             (flags & ImGuiViewportFlags_NoFocusOnAppearing) ? " NoFocusOnAppearing" : "",
17099             (flags & ImGuiViewportFlags_NoFocusOnClick) ? " NoFocusOnClick" : "",
17100             (flags & ImGuiViewportFlags_NoInputs) ? " NoInputs" : "",
17101             (flags & ImGuiViewportFlags_NoRendererClear) ? " NoRendererClear" : "",
17102             (flags & ImGuiViewportFlags_TopMost) ? " TopMost" : "",
17103             (flags & ImGuiViewportFlags_Minimized) ? " Minimized" : "",
17104             (flags & ImGuiViewportFlags_NoAutoMerge) ? " NoAutoMerge" : "",
17105             (flags & ImGuiViewportFlags_CanHostOtherWindows) ? " CanHostOtherWindows" : "");
17106         for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++)
17107             for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++)
17108                 DebugNodeDrawList(NULL, viewport, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList");
17109         TreePop();
17110     }
17111 }
17112 
DebugNodeWindow(ImGuiWindow * window,const char * label)17113 void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)
17114 {
17115     if (window == NULL)
17116     {
17117         BulletText("%s: NULL", label);
17118         return;
17119     }
17120 
17121     ImGuiContext& g = *GImGui;
17122     const bool is_active = window->WasActive;
17123     ImGuiTreeNodeFlags tree_node_flags = (window == g.NavWindow) ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None;
17124     if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
17125     const bool open = TreeNodeEx(label, tree_node_flags, "%s '%s'%s", label, window->Name, is_active ? "" : " *Inactive*");
17126     if (!is_active) { PopStyleColor(); }
17127     if (IsItemHovered() && is_active)
17128         GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
17129     if (!open)
17130         return;
17131 
17132     if (window->MemoryCompacted)
17133         TextDisabled("Note: some memory buffers have been compacted/freed.");
17134 
17135     ImGuiWindowFlags flags = window->Flags;
17136     DebugNodeDrawList(window, window->Viewport, window->DrawList, "DrawList");
17137     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);
17138     BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
17139         (flags & ImGuiWindowFlags_ChildWindow)  ? "Child " : "",      (flags & ImGuiWindowFlags_Tooltip)     ? "Tooltip "   : "",  (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
17140         (flags & ImGuiWindowFlags_Modal)        ? "Modal " : "",      (flags & ImGuiWindowFlags_ChildMenu)   ? "ChildMenu " : "",  (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
17141         (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
17142     BulletText("WindowClassId: 0x%08X", window->WindowClass.ClassId);
17143     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" : "");
17144     BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
17145     BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems);
17146     for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)
17147     {
17148         ImRect r = window->NavRectRel[layer];
17149         if (r.Min.x >= r.Max.y && r.Min.y >= r.Max.y)
17150         {
17151             BulletText("NavLastIds[%d]: 0x%08X", layer, window->NavLastIds[layer]);
17152             continue;
17153         }
17154         BulletText("NavLastIds[%d]: 0x%08X at +(%.1f,%.1f)(%.1f,%.1f)", layer, window->NavLastIds[layer], r.Min.x, r.Min.y, r.Max.x, r.Max.y);
17155         if (IsItemHovered())
17156             GetForegroundDrawList(window)->AddRect(r.Min + window->Pos, r.Max + window->Pos, IM_COL32(255, 255, 0, 255));
17157     }
17158     BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
17159 
17160     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);
17161     BulletText("ViewportMonitor: %d", window->Viewport ? window->Viewport->PlatformMonitor : -1);
17162     BulletText("DockId: 0x%04X, DockOrder: %d, Act: %d, Vis: %d", window->DockId, window->DockOrder, window->DockIsActive, window->DockTabIsVisible);
17163     if (window->DockNode || window->DockNodeAsHost)
17164         DebugNodeDockNode(window->DockNodeAsHost ? window->DockNodeAsHost : window->DockNode, window->DockNodeAsHost ? "DockNodeAsHost" : "DockNode");
17165 
17166     if (window->RootWindow != window)       { DebugNodeWindow(window->RootWindow, "RootWindow"); }
17167     if (window->RootWindowDockTree != window->RootWindow) { DebugNodeWindow(window->RootWindowDockTree, "RootWindowDockTree"); }
17168     if (window->ParentWindow != NULL)       { DebugNodeWindow(window->ParentWindow, "ParentWindow"); }
17169     if (window->DC.ChildWindows.Size > 0)   { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); }
17170     if (window->ColumnsStorage.Size > 0 && TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
17171     {
17172         for (int n = 0; n < window->ColumnsStorage.Size; n++)
17173             DebugNodeColumns(&window->ColumnsStorage[n]);
17174         TreePop();
17175     }
17176     DebugNodeStorage(&window->StateStorage, "Storage");
17177     TreePop();
17178 }
17179 
DebugNodeWindowSettings(ImGuiWindowSettings * settings)17180 void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings* settings)
17181 {
17182     Text("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d",
17183         settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed);
17184 }
17185 
DebugNodeWindowsList(ImVector<ImGuiWindow * > * windows,const char * label)17186 void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label)
17187 {
17188     if (!TreeNode(label, "%s (%d)", label, windows->Size))
17189         return;
17190     Text("(In front-to-back order:)");
17191     for (int i = windows->Size - 1; i >= 0; i--) // Iterate front to back
17192     {
17193         PushID((*windows)[i]);
17194         DebugNodeWindow((*windows)[i], "Window");
17195         PopID();
17196     }
17197     TreePop();
17198 }
17199 
17200 #else
17201 
ShowMetricsWindow(bool *)17202 void ImGui::ShowMetricsWindow(bool*) {}
ShowFontAtlas(ImFontAtlas *)17203 void ImGui::ShowFontAtlas(ImFontAtlas*) {}
DebugNodeColumns(ImGuiOldColumns *)17204 void ImGui::DebugNodeColumns(ImGuiOldColumns*) {}
DebugNodeDrawList(ImGuiWindow *,ImGuiViewportP *,const ImDrawList *,const char *)17205 void ImGui::DebugNodeDrawList(ImGuiWindow*, ImGuiViewportP*, const ImDrawList*, const char*) {}
DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList *,const ImDrawList *,const ImDrawCmd *,bool,bool)17206 void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList*, const ImDrawList*, const ImDrawCmd*, bool, bool) {}
DebugNodeFont(ImFont *)17207 void ImGui::DebugNodeFont(ImFont*) {}
DebugNodeStorage(ImGuiStorage *,const char *)17208 void ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {}
DebugNodeTabBar(ImGuiTabBar *,const char *)17209 void ImGui::DebugNodeTabBar(ImGuiTabBar*, const char*) {}
DebugNodeWindow(ImGuiWindow *,const char *)17210 void ImGui::DebugNodeWindow(ImGuiWindow*, const char*) {}
DebugNodeWindowSettings(ImGuiWindowSettings *)17211 void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {}
DebugNodeWindowsList(ImVector<ImGuiWindow * > *,const char *)17212 void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>*, const char*) {}
DebugNodeViewport(ImGuiViewportP *)17213 void ImGui::DebugNodeViewport(ImGuiViewportP*) {}
17214 
17215 #endif
17216 
17217 //-----------------------------------------------------------------------------
17218 
17219 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
17220 // 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.
17221 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
17222 #include "imgui_user.inl"
17223 #endif
17224 
17225 //-----------------------------------------------------------------------------
17226 
17227 #endif // #ifndef IMGUI_DISABLE
17228