1 // dear imgui, v1.79 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/3075 (please post your screenshots/video there!)
15 // - Glossary              https://github.com/ocornut/imgui/wiki/Glossary
16 // - Wiki                  https://github.com/ocornut/imgui/wiki
17 // - Issues & support      https://github.com/ocornut/imgui/issues
18 
19 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
20 // See LICENSE.txt for copyright and licensing details (standard MIT License).
21 // This library is free but needs your support to sustain development and maintenance.
22 // Businesses: you can support continued development via invoiced technical support, maintenance and sponsoring contracts. Please reach out to "contact AT dearimgui.org".
23 // Individuals: you can support continued development via donations. See docs/README or web page.
24 
25 // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
26 // Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
27 // modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
28 // come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
29 // to a better solution or official support for them.
30 
31 /*
32 
33 Index of this file:
34 
35 DOCUMENTATION
36 
37 - MISSION STATEMENT
38 - END-USER GUIDE
39 - PROGRAMMER GUIDE
40   - READ FIRST
41   - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
42   - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
43   - HOW A SIMPLE APPLICATION MAY LOOK LIKE
44   - HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
45   - USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
46 - API BREAKING CHANGES (read me when you update!)
47 - FREQUENTLY ASKED QUESTIONS (FAQ)
48   - Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer)
49 
50 CODE
51 (search for "[SECTION]" in the code to find them)
52 
53 // [SECTION] INCLUDES
54 // [SECTION] FORWARD DECLARATIONS
55 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
56 // [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
57 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
58 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
59 // [SECTION] MISC HELPERS/UTILITIES (File functions)
60 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
61 // [SECTION] MISC HELPERS/UTILITIES (Color functions)
62 // [SECTION] ImGuiStorage
63 // [SECTION] ImGuiTextFilter
64 // [SECTION] ImGuiTextBuffer
65 // [SECTION] ImGuiListClipper
66 // [SECTION] STYLING
67 // [SECTION] RENDER HELPERS
68 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
69 // [SECTION] ERROR CHECKING
70 // [SECTION] LAYOUT
71 // [SECTION] SCROLLING
72 // [SECTION] TOOLTIPS
73 // [SECTION] POPUPS
74 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
75 // [SECTION] DRAG AND DROP
76 // [SECTION] LOGGING/CAPTURING
77 // [SECTION] SETTINGS
78 // [SECTION] VIEWPORTS, PLATFORM WINDOWS
79 // [SECTION] DOCKING
80 // [SECTION] PLATFORM DEPENDENT HELPERS
81 // [SECTION] METRICS/DEBUG WINDOW
82 
83 */
84 
85 //-----------------------------------------------------------------------------
86 // DOCUMENTATION
87 //-----------------------------------------------------------------------------
88 
89 /*
90 
91  MISSION STATEMENT
92  =================
93 
94  - Easy to use to create code-driven and data-driven tools.
95  - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
96  - Easy to hack and improve.
97  - Minimize screen real-estate usage.
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 (NB- we do allocate when "growing" content e.g. creating a window,.
102    opening a tree node for the first time, etc. but a typical frame should not allocate anything).
103 
104  Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
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://goo.gl/9LgVZW
131 
132 
133  PROGRAMMER GUIDE
134  ================
135 
136  READ FIRST
137  ----------
138  - Remember to read the FAQ (https://www.dearimgui.org/faq)
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
140    or destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, less 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 the FAQ.
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 own 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 are 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 made modification to your copy of imconfig.h)
162  - Or maintain your own branch where you have imconfig.h modified.
163  - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
164    If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
165    from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
166    likely be a comment about it. Please report any issue to the GitHub page!
167  - Try to keep your copy of dear imgui reasonably up to date.
168 
169 
170  GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
171  ---------------------------------------------------------------
172  - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
173  - In the majority of cases you should be able to use unmodified back-ends files available in the examples/ folder.
174  - Add the Dear ImGui source files + selected back-end source files to your projects or using your preferred build system.
175    It is recommended you build and statically link the .cpp files as part of your project and NOT as shared library (DLL).
176  - 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.
177  - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
178  - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
179    Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
180    phases of your own application. All rendering information are stored into command-lists that you will retrieve after calling ImGui::Render().
181  - Refer to the bindings and demo applications in the examples/ folder for instruction on how to setup your code.
182  - 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.
183 
184 
185  HOW A SIMPLE APPLICATION MAY LOOK LIKE
186  --------------------------------------
187  EXHIBIT 1: USING THE EXAMPLE BINDINGS (= imgui_impl_XXX.cpp files from the examples/ folder).
188  The sub-folders in examples/ contains examples applications following this structure.
189 
190      // Application init: create a dear imgui context, setup some options, load fonts
191      ImGui::CreateContext();
192      ImGuiIO& io = ImGui::GetIO();
193      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
194      // TODO: Fill optional fields of the io structure later.
195      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
196 
197      // Initialize helper Platform and Renderer bindings (here we are using imgui_impl_win32.cpp and imgui_impl_dx11.cpp)
198      ImGui_ImplWin32_Init(hwnd);
199      ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
200 
201      // Application main loop
202      while (true)
203      {
204          // Feed inputs to dear imgui, start new frame
205          ImGui_ImplDX11_NewFrame();
206          ImGui_ImplWin32_NewFrame();
207          ImGui::NewFrame();
208 
209          // Any application code here
210          ImGui::Text("Hello, world!");
211 
212          // Render dear imgui into screen
213          ImGui::Render();
214          ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
215          g_pSwapChain->Present(1, 0);
216      }
217 
218      // Shutdown
219      ImGui_ImplDX11_Shutdown();
220      ImGui_ImplWin32_Shutdown();
221      ImGui::DestroyContext();
222 
223  EXHIBIT 2: IMPLEMENTING CUSTOM BINDING / CUSTOM ENGINE
224 
225      // Application init: create a dear imgui context, setup some options, load fonts
226      ImGui::CreateContext();
227      ImGuiIO& io = ImGui::GetIO();
228      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
229      // TODO: Fill optional fields of the io structure later.
230      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
231 
232      // Build and load the texture atlas into a texture
233      // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
234      int width, height;
235      unsigned char* pixels = NULL;
236      io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
237 
238      // At this point you've got the texture data and you need to upload that your your graphic system:
239      // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
240      // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID.
241      MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
242      io.Fonts->TexID = (void*)texture;
243 
244      // Application main loop
245      while (true)
246      {
247         // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
248         // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform bindings)
249         io.DeltaTime = 1.0f/60.0f;              // set the time elapsed since the previous frame (in seconds)
250         io.DisplaySize.x = 1920.0f;             // set the current display width
251         io.DisplaySize.y = 1280.0f;             // set the current display height here
252         io.MousePos = my_mouse_pos;             // set the mouse position
253         io.MouseDown[0] = my_mouse_buttons[0];  // set the mouse button states
254         io.MouseDown[1] = my_mouse_buttons[1];
255 
256         // Call NewFrame(), after this point you can use ImGui::* functions anytime
257         // (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use Dear ImGui everywhere)
258         ImGui::NewFrame();
259 
260         // Most of your application code here
261         ImGui::Text("Hello, world!");
262         MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
263         MyGameRender(); // may use any Dear ImGui functions as well!
264 
265         // Render dear imgui, swap buffers
266         // (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)
267         ImGui::EndFrame();
268         ImGui::Render();
269         ImDrawData* draw_data = ImGui::GetDrawData();
270         MyImGuiRenderFunction(draw_data);
271         SwapBuffers();
272      }
273 
274      // Shutdown
275      ImGui::DestroyContext();
276 
277  To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest your application,
278  you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
279  Please read the FAQ and example applications for details about this!
280 
281 
282  HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
283  ---------------------------------------------
284  The bindings in impl_impl_XXX.cpp files contains many working implementations of a rendering function.
285 
286     void void MyImGuiRenderFunction(ImDrawData* draw_data)
287     {
288        // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
289        // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
290        // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
291        // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
292        for (int n = 0; n < draw_data->CmdListsCount; n++)
293        {
294           const ImDrawList* cmd_list = draw_data->CmdLists[n];
295           const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;  // vertex buffer generated by Dear ImGui
296           const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;   // index buffer generated by Dear ImGui
297           for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
298           {
299              const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
300              if (pcmd->UserCallback)
301              {
302                  pcmd->UserCallback(cmd_list, pcmd);
303              }
304              else
305              {
306                  // The texture for the draw call is specified by pcmd->TextureId.
307                  // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization.
308                  MyEngineBindTexture((MyTexture*)pcmd->TextureId);
309 
310                  // We are using scissoring to clip some objects. All low-level graphics API should supports it.
311                  // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
312                  //   (some elements visible outside their bounds) but you can fix that once everything else works!
313                  // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize)
314                  //   In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize.
315                  //   However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github),
316                  //   always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
317                  // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
318                  ImVec2 pos = draw_data->DisplayPos;
319                  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));
320 
321                  // Render 'pcmd->ElemCount/3' indexed triangles.
322                  // 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.
323                  MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
324              }
325              idx_buffer += pcmd->ElemCount;
326           }
327        }
328     }
329 
330 
331  USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
332  ------------------------------------------
333  - The gamepad/keyboard navigation is fairly functional and keeps being improved.
334  - Gamepad support is particularly useful to use Dear ImGui on a console system (e.g. PS4, Switch, XB1) without a mouse!
335  - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787
336  - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
337  - Keyboard:
338     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
339       NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
340     - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
341       will be set. For more advanced uses, you may want to read from:
342        - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
343        - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
344        - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions.
345       Please reach out if you think the game vs navigation input sharing could be improved.
346  - Gamepad:
347     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
348     - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
349       Note that io.NavInputs[] is cleared by EndFrame().
350     - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
351          0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
352     - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
353       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.).
354     - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://goo.gl/9LgVZW.
355     - 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
356       to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
357  - Mouse:
358     - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
359     - 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.
360     - 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.
361       Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
362       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.
363       When that happens your back-end NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the binding in examples/ do that.
364       (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, imgui will misbehave as it will see your mouse as moving back and forth!)
365       (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
366        to set a boolean to ignore your other external mouse positions until the external source is moved again.)
367 
368 
369  API BREAKING CHANGES
370  ====================
371 
372  Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
373  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.
374  When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
375  You can read releases logs https://github.com/ocornut/imgui/releases for more details.
376 
377  (Docking/Viewport Branch)
378  - 2020/XX/XX (1.XX) - when multi-viewports are enabled, all positions will be in your natural OS coordinates space. It means that:
379                         - reference to hard-coded positions such as in SetNextWindowPos(ImVec2(0,0)) are probably not what you want anymore.
380                           you may use GetMainViewport()->Pos to offset hard-coded positions, e.g. SetNextWindowPos(GetMainViewport()->Pos)
381                         - likewise io.MousePos and GetMousePos() will use OS coordinates.
382                           If you query mouse positions to interact with non-imgui coordinates you will need to offset them, e.g. subtract GetWindowViewport()->Pos.
383  - 2020/XX/XX (1.XX) - Moved IME support functions from io.ImeSetInputScreenPosFn, io.ImeWindowHandle to the PlatformIO api.
384 
385 
386  - 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().
387                        replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags).
388                        worked out a backward-compatibility scheme so hopefully most C++ codebase should not be affected. in short, when calling those functions:
389                        - if you omitted the 'power' parameter (likely!), you are not affected.
390                        - 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.
391                        - 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.
392                        see https://github.com/ocornut/imgui/issues/3361 for all details.
393                        kept inline redirection functions (will obsolete) apart for: DragFloatRange2(), VSliderFloat(), VSliderScalar(). For those three the 'float power=1.0f' version were removed directly as they were most unlikely ever used.
394                      - 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.
395  - 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.
396  - 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete).
397  - 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017.
398  - 2020/04/23 (1.77) - removed unnecessary ID (first arg) of ImFontAtlas::AddCustomRectRegular().
399  - 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more.
400  - 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.
401  - 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.
402  - 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017):
403                        - ShowTestWindow()                    -> use ShowDemoWindow()
404                        - IsRootWindowFocused()               -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow)
405                        - IsRootWindowOrAnyChildFocused()     -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
406                        - SetNextWindowContentWidth(w)        -> use SetNextWindowContentSize(ImVec2(w, 0.0f)
407                        - GetItemsLineHeightWithSpacing()     -> use GetFrameHeightWithSpacing()
408                        - ImGuiCol_ChildWindowBg              -> use ImGuiCol_ChildBg
409                        - ImGuiStyleVar_ChildWindowRounding   -> use ImGuiStyleVar_ChildRounding
410                        - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap
411                        - IMGUI_DISABLE_TEST_WINDOWS          -> use IMGUI_DISABLE_DEMO_WINDOWS
412  - 2019/12/08 (1.75) - obsoleted calling ImDrawList::PrimReserve() with a negative count (which was the vaguely documented and rarely if ever used). Instead we added an explicit PrimUnreserve() API.
413  - 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).
414  - 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.
415  - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency.
416  - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency.
417  - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017):
418                        - Begin() [old 5 args version]        -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed
419                        - IsRootWindowOrAnyChildHovered()     -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows)
420                        - AlignFirstTextHeightToWidgets()     -> use AlignTextToFramePadding()
421                        - SetNextWindowPosCenter()            -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f)
422                        - ImFont::Glyph                       -> use ImFontGlyph
423  - 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.
424                        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.
425                        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).
426                        If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you.
427  - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete).
428  - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete).
429  - 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.
430  - 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
431                        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.
432                        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.
433                        Please reach out if you are affected.
434  - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete).
435  - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c).
436  - 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.
437  - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).
438  - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).
439  - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).
440  - 2019/02/14 (1.68) - made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). If for some reason your time step calculation gives you a zero value, replace it with an arbitrary small value!
441  - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
442  - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
443  - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).
444  - 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.
445  - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
446  - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
447  - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
448  - 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.
449                        If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
450  - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
451  - 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.
452                        NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
453                        Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
454  - 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).
455  - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
456  - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
457  - 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.
458  - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
459  - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
460  - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
461  - 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.).
462                        old bindings will still work as is, however prefer using the separated bindings as they will be updated to support multi-viewports.
463                        when adopting new bindings follow the main.cpp code of your preferred examples/ folder to know which functions to call.
464                        in particular, note that old bindings called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function.
465  - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
466  - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
467  - 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.
468                        If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
469                        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.
470                        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.
471  - 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",
472                        consistent with other functions. Kept redirection functions (will obsolete).
473  - 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.
474  - 2018/03/20 (1.60) - renamed io.WantMoveMouse to io.WantSetMousePos for consistency and ease of understanding (was added in 1.52, _not_ used by core and only honored by some binding ahead of merging the Nav branch).
475  - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
476  - 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.
477  - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
478  - 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.
479  - 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.
480  - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
481                        - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
482                        - removed Shutdown() function, as DestroyContext() serve this purpose.
483                        - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
484                        - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
485                        - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
486  - 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.
487  - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
488  - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
489  - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
490  - 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.
491  - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
492  - 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
493  - 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.
494  - 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.
495  - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
496  - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
497                      - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
498  - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
499  - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
500  - 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.
501  - 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.
502                        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.
503  - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
504  - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
505  - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
506  - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
507  - 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.
508  - 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.
509  - 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.
510                        removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
511                          IsItemHoveredRect()        --> IsItemHovered(ImGuiHoveredFlags_RectOnly)
512                          IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
513                          IsMouseHoveringWindow()    --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior]
514  - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
515  - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
516  - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).
517  - 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).
518  - 2017/08/25 (1.52) - io.MousePos needs to be set to ImVec2(-FLT_MAX,-FLT_MAX) when mouse is unavailable/missing. Previously ImVec2(-1,-1) was enough but we now accept negative mouse coordinates. In your binding if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)".
519  - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
520                      - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
521                      - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
522  - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
523  - 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.
524  - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type.
525  - 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.
526  - 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).
527  - 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).
528  - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
529  - 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.
530                      - 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.
531                      - 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))'
532  - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
533  - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
534  - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
535  - 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().
536  - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
537  - 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.
538  - 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.
539  - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
540                        If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you, otherwise if <1.0f you need tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
541                        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:
542                        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); }
543                        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.
544  - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
545  - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
546  - 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).
547  - 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.
548  - 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).
549  - 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)
550  - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
551  - 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.
552  - 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.
553  - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
554  - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
555  - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
556                        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.
557                        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!
558  - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
559  - 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.
560  - 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
561  - 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.
562                        you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
563  - 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.
564                        this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
565                      - if you are using a vanilla copy of one of the imgui_impl_XXXX.cpp provided in the example, you just need to update your copy and you can ignore the rest.
566                      - the signature of the io.RenderDrawListsFn handler has changed!
567                        old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
568                        new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
569                          parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
570                          ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
571                          ImDrawCmd:  'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
572                      - 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.
573                      - 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!
574                      - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
575  - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
576  - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
577  - 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.
578  - 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
579  - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely be used. Sorry!
580  - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
581  - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
582  - 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.
583  - 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.
584  - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
585  - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
586  - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
587  - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
588  - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
589  - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
590  - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
591  - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
592  - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
593  - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
594  - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
595  - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
596  - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
597  - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
598  - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
599  - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
600  - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
601  - 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
602                        - old:  const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..];
603                        - new:  unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->TexId = YourTexIdentifier;
604                        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.
605  - 2015/01/11 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
606  - 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
607  - 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
608  - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
609  - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
610  - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
611  - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
612  - 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)
613  - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
614  - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
615  - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
616  - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
617  - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
618  - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
619 
620 
621  FREQUENTLY ASKED QUESTIONS (FAQ)
622  ================================
623 
624  Read all answers online:
625    https://www.dearimgui.org/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url)
626  Read all answers locally (with a text editor or ideally a Markdown viewer):
627    docs/FAQ.md
628  Some answers are copied down here to facilitate searching in code.
629 
630  Q&A: Basics
631  ===========
632 
633  Q: Where is the documentation?
634  A: This library is poorly documented at the moment and expects of the user to be acquainted with C/C++.
635     - Run the examples/ and explore them.
636     - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function.
637     - The demo covers most features of Dear ImGui, so you can read the code and see its output.
638     - See documentation and comments at the top of imgui.cpp + effectively imgui.h.
639     - Dozens of standalone example applications using e.g. OpenGL/DirectX are provided in the
640       examples/ folder to explain how to integrate Dear ImGui with your own engine/application.
641     - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links.
642     - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful.
643     - Your programming IDE is your friend, find the type or function declaration to find comments
644       associated to it.
645 
646  Q: What is this library called?
647  Q: Which version should I get?
648  >> This library is called "Dear ImGui", please don't call it "ImGui" :)
649  >> See https://www.dearimgui.org/faq for details.
650 
651  Q&A: Integration
652  ================
653 
654  Q: How to get started?
655  A: Read 'PROGRAMMER GUIDE' above. Read examples/README.txt.
656 
657  Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or to my application?
658  A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
659  >> See https://www.dearimgui.org/faq for fully detailed answer. You really want to read this.
660 
661  Q. How can I enable keyboard controls?
662  Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display)
663  Q: I integrated Dear ImGui in my engine and little squares are showing instead of text..
664  Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
665  Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries..
666  >> See https://www.dearimgui.org/faq
667 
668  Q&A: Usage
669  ----------
670 
671  Q: Why is my widget not reacting when I click on it?
672  Q: How can I have widgets with an empty label?
673  Q: How can I have multiple widgets with the same label?
674  Q: How can I display an image? What is ImTextureID, how does it works?
675  Q: How can I use my own math types instead of ImVec2/ImVec4?
676  Q: How can I interact with standard C++ types (such as std::string and std::vector)?
677  Q: How can I display custom shapes? (using low-level ImDrawList API)
678  >> See https://www.dearimgui.org/faq
679 
680  Q&A: Fonts, Text
681  ================
682 
683  Q: How should I handle DPI in my application?
684  Q: How can I load a different font than the default?
685  Q: How can I easily use icons in my application?
686  Q: How can I load multiple fonts?
687  Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
688  >> See https://www.dearimgui.org/faq and https://github.com/ocornut/imgui/edit/master/docs/FONTS.md
689 
690  Q&A: Concerns
691  =============
692 
693  Q: Who uses Dear ImGui?
694  Q: Can you create elaborate/serious tools with Dear ImGui?
695  Q: Can you reskin the look of Dear ImGui?
696  Q: Why using C++ (as opposed to C)?
697  >> See https://www.dearimgui.org/faq
698 
699  Q&A: Community
700  ==============
701 
702  Q: How can I help?
703  A: - Businesses: please reach out to "contact AT dearimgui.org" if you work in a place using Dear ImGui!
704       We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts.
705       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.
706     - Individuals: you can support continued development via PayPal donations. See README.
707     - If you are experienced with Dear ImGui and C++, look at the github issues, look at the Wiki, read docs/TODO.txt
708       and see how you want to help and can help!
709     - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
710       You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/3075). Visuals are ideal as they inspire other programmers.
711       But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions.
712     - 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).
713 
714 */
715 
716 //-------------------------------------------------------------------------
717 // [SECTION] INCLUDES
718 //-------------------------------------------------------------------------
719 
720 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
721 #define _CRT_SECURE_NO_WARNINGS
722 #endif
723 
724 #include "imgui.h"
725 #ifndef IMGUI_DISABLE
726 
727 #ifndef IMGUI_DEFINE_MATH_OPERATORS
728 #define IMGUI_DEFINE_MATH_OPERATORS
729 #endif
730 #include "imgui_internal.h"
731 
732 // System includes
733 #include <ctype.h>      // toupper
734 #include <stdio.h>      // vsnprintf, sscanf, printf
735 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
736 #include <stddef.h>     // intptr_t
737 #else
738 #include <stdint.h>     // intptr_t
739 #endif
740 
741 // [Windows] OS specific includes (optional)
742 #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)
743 #define IMGUI_DISABLE_WIN32_FUNCTIONS
744 #endif
745 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
746 #ifndef WIN32_LEAN_AND_MEAN
747 #define WIN32_LEAN_AND_MEAN
748 #endif
749 #ifndef NOMINMAX
750 #define NOMINMAX
751 #endif
752 #ifndef __MINGW32__
753 #include <Windows.h>        // _wfopen, OpenClipboard
754 #else
755 #include <windows.h>
756 #endif
757 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) // UWP doesn't have all Win32 functions
758 #define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
759 #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
760 #endif
761 #endif
762 
763 // [Apple] OS specific includes
764 #if defined(__APPLE__)
765 #include <TargetConditionals.h>
766 #endif
767 
768 // Visual Studio warnings
769 #ifdef _MSC_VER
770 #pragma warning (disable: 4127)             // condition expression is constant
771 #pragma warning (disable: 4996)             // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
772 #if defined(_MSC_VER) && _MSC_VER >= 1922   // MSVC 2019 16.2 or later
773 #pragma warning (disable: 5054)             // operator '|': deprecated between enumerations of different types
774 #endif
775 #endif
776 
777 // Clang/GCC warnings with -Weverything
778 #if defined(__clang__)
779 #if __has_warning("-Wunknown-warning-option")
780 #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!
781 #endif
782 #pragma clang diagnostic ignored "-Wunknown-pragmas"                // warning: unknown warning group 'xxx'
783 #pragma clang diagnostic ignored "-Wold-style-cast"                 // warning: use of old-style cast                            // yes, they are more terse.
784 #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.
785 #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.
786 #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.
787 #pragma clang diagnostic ignored "-Wglobal-constructors"            // warning: declaration requires a global destructor         // similar to above, not sure what the exact difference is.
788 #pragma clang diagnostic ignored "-Wsign-conversion"                // warning: implicit conversion changes signedness
789 #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.
790 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast"       // warning: cast to 'void *' from smaller integer type 'int'
791 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"  // warning: zero as null pointer constant                    // some standard header variations use #define NULL 0
792 #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.
793 #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"  // warning: implicit conversion from 'xxx' to 'float' may lose precision
794 #elif defined(__GNUC__)
795 // We disable -Wpragmas because GCC doesn't provide an has_warning equivalent and some forks/patches may not following the warning/version association.
796 #pragma GCC diagnostic ignored "-Wpragmas"                  // warning: unknown option after '#pragma GCC diagnostic' kind
797 #pragma GCC diagnostic ignored "-Wunused-function"          // warning: 'xxxx' defined but not used
798 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"      // warning: cast to pointer from integer of different size
799 #pragma GCC diagnostic ignored "-Wformat"                   // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
800 #pragma GCC diagnostic ignored "-Wdouble-promotion"         // warning: implicit conversion from 'float' to 'double' when passing argument to function
801 #pragma GCC diagnostic ignored "-Wconversion"               // warning: conversion to 'xxxx' from 'xxxx' may alter its value
802 #pragma GCC diagnostic ignored "-Wformat-nonliteral"        // warning: format not a string literal, format string not checked
803 #pragma GCC diagnostic ignored "-Wstrict-overflow"          // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
804 #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
805 #endif
806 
807 // Debug options
808 #define IMGUI_DEBUG_NAV_SCORING     0   // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL
809 #define IMGUI_DEBUG_NAV_RECTS       0   // Display the reference navigation rectangle for each window
810 #define IMGUI_DEBUG_INI_SETTINGS    0   // Save additional comments in .ini file (particularly helps for Docking, but makes saving slower)
811 
812 // 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.
813 static const float NAV_WINDOWING_HIGHLIGHT_DELAY            = 0.20f;    // Time before the highlight and screen dimming starts fading in
814 static const float NAV_WINDOWING_LIST_APPEAR_DELAY          = 0.15f;    // Time before the window list starts to appear
815 
816 // Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by back-end)
817 static const float WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS = 4.0f;     // Extend outside and inside windows. Affect FindHoveredWindow().
818 static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f;    // Reduce visual noise by only highlighting the border after a certain time.
819 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.
820 
821 // Docking
822 static const float DOCKING_TRANSPARENT_PAYLOAD_ALPHA        = 0.50f;    // For use with io.ConfigDockingTransparentPayload. Apply to Viewport _or_ WindowBg in host viewport.
823 static const float DOCKING_SPLITTER_SIZE                    = 2.0f;
824 
825 //-------------------------------------------------------------------------
826 // [SECTION] FORWARD DECLARATIONS
827 //-------------------------------------------------------------------------
828 
829 static void             SetCurrentWindow(ImGuiWindow* window);
830 static void             FindHoveredWindow();
831 static ImGuiWindow*     CreateNewWindow(const char* name, ImGuiWindowFlags flags);
832 static ImVec2           CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);
833 
834 static void             AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
835 static void             AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
836 
837 // Settings
838 static void             WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
839 static void*            WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
840 static void             WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
841 static void             WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);
842 static void             WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf);
843 
844 // Platform Dependents default implementation for IO functions
845 static const char*      GetClipboardTextFn_DefaultImpl(void* user_data);
846 static void             SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
847 
848 namespace ImGui
849 {
850 // Navigation
851 static void             NavUpdate();
852 static void             NavUpdateWindowing();
853 static void             NavUpdateWindowingOverlay();
854 static void             NavUpdateMoveResult();
855 static float            NavUpdatePageUpPageDown();
856 static inline void      NavUpdateAnyRequestFlag();
857 static void             NavEndFrame();
858 static bool             NavScoreItem(ImGuiNavMoveResult* result, ImRect cand);
859 static void             NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id);
860 static ImVec2           NavCalcPreferredRefPos();
861 static void             NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
862 static ImGuiWindow*     NavRestoreLastChildNavWindow(ImGuiWindow* window);
863 static int              FindWindowFocusIndex(ImGuiWindow* window);
864 
865 // Error Checking
866 static void             ErrorCheckNewFrameSanityChecks();
867 static void             ErrorCheckEndFrameSanityChecks();
868 static void             ErrorCheckBeginEndCompareStacksSize(ImGuiWindow* window, bool write);
869 
870 // Misc
871 static void             UpdateSettings();
872 static void             UpdateMouseInputs();
873 static void             UpdateMouseWheel();
874 static void             UpdateTabFocus();
875 static void             UpdateDebugToolItemPicker();
876 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);
877 static void             RenderWindowOuterBorders(ImGuiWindow* window);
878 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);
879 static void             RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
880 static void             EndFrameDrawDimmedBackgrounds();
881 
882 // Viewports
883 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.
884 static ImGuiViewportP*  AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& platform_pos, const ImVec2& size, ImGuiViewportFlags flags);
885 static void             UpdateViewportsNewFrame();
886 static void             UpdateViewportsEndFrame();
887 static void             UpdateSelectWindowViewport(ImGuiWindow* window);
888 static bool             UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport);
889 static bool             UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window);
890 static void             SetCurrentViewport(ImGuiWindow* window, ImGuiViewportP* viewport);
891 static bool             GetWindowAlwaysWantOwnViewport(ImGuiWindow* window);
892 static int              FindPlatformMonitorForPos(const ImVec2& pos);
893 static int              FindPlatformMonitorForRect(const ImRect& r);
894 static void             UpdateViewportPlatformMonitor(ImGuiViewportP* viewport);
895 
896 }
897 
898 //-----------------------------------------------------------------------------
899 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
900 //-----------------------------------------------------------------------------
901 
902 // Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
903 // ImGui::CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext().
904 // 1) Important: globals are not shared across DLL boundaries! If you use DLLs or any form of hot-reloading: you will need to call
905 //    SetCurrentContext() (with the pointer you got from CreateContext) from each unique static/DLL boundary, and after each hot-reloading.
906 //    In your debugger, add GImGui to your watch window and notice how its value changes depending on which location you are currently stepping into.
907 // 2) Important: Dear ImGui functions are not thread-safe because of this pointer.
908 //    If you want thread-safety to allow N threads to access N different contexts, you can:
909 //    - Change this variable to use thread local storage so each thread can refer to a different context, in imconfig.h:
910 //          struct ImGuiContext;
911 //          extern thread_local ImGuiContext* MyImGuiTLS;
912 //          #define GImGui MyImGuiTLS
913 //      And then define MyImGuiTLS in one of your cpp file. Note that thread_local is a C++11 keyword, earlier C++ uses compiler-specific keyword.
914 //    - Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
915 //    - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from different namespace.
916 #ifndef GImGui
917 ImGuiContext*   GImGui = NULL;
918 #endif
919 
920 // Memory Allocator functions. Use SetAllocatorFunctions() to change them.
921 // If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file.
922 // Otherwise, you probably don't want to modify them mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction.
923 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
MallocWrapper(size_t size,void * user_data)924 static void*   MallocWrapper(size_t size, void* user_data)    { IM_UNUSED(user_data); return malloc(size); }
FreeWrapper(void * ptr,void * user_data)925 static void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); free(ptr); }
926 #else
MallocWrapper(size_t size,void * user_data)927 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)928 static void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }
929 #endif
930 
931 static void*  (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper;
932 static void   (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper;
933 static void*    GImAllocatorUserData = NULL;
934 
935 //-----------------------------------------------------------------------------
936 // [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
937 //-----------------------------------------------------------------------------
938 
ImGuiStyle()939 ImGuiStyle::ImGuiStyle()
940 {
941     Alpha                   = 1.0f;             // Global alpha applies to everything in ImGui
942     WindowPadding           = ImVec2(8,8);      // Padding within a window
943     WindowRounding          = 7.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.
944     WindowBorderSize        = 1.0f;             // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
945     WindowMinSize           = ImVec2(32,32);    // Minimum window size
946     WindowTitleAlign        = ImVec2(0.0f,0.5f);// Alignment for title bar text
947     WindowMenuButtonPosition= ImGuiDir_Left;    // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.
948     ChildRounding           = 0.0f;             // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
949     ChildBorderSize         = 1.0f;             // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
950     PopupRounding           = 0.0f;             // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
951     PopupBorderSize         = 1.0f;             // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
952     FramePadding            = ImVec2(4,3);      // Padding within a framed rectangle (used by most widgets)
953     FrameRounding           = 0.0f;             // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
954     FrameBorderSize         = 0.0f;             // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
955     ItemSpacing             = ImVec2(8,4);      // Horizontal and vertical spacing between widgets/lines
956     ItemInnerSpacing        = ImVec2(4,4);      // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
957     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!
958     IndentSpacing           = 21.0f;            // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
959     ColumnsMinSpacing       = 6.0f;             // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
960     ScrollbarSize           = 14.0f;            // Width of the vertical scrollbar, Height of the horizontal scrollbar
961     ScrollbarRounding       = 9.0f;             // Radius of grab corners rounding for scrollbar
962     GrabMinSize             = 10.0f;            // Minimum width/height of a grab box for slider/scrollbar
963     GrabRounding            = 0.0f;             // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
964     LogSliderDeadzone       = 4.0f;             // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
965     TabRounding             = 4.0f;             // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
966     TabBorderSize           = 0.0f;             // Thickness of border around tabs.
967     TabMinWidthForUnselectedCloseButton = 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.
968     ColorButtonPosition     = ImGuiDir_Right;   // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
969     ButtonTextAlign         = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
970     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.
971     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.
972     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.
973     MouseCursorScale        = 1.0f;             // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
974     AntiAliasedLines        = true;             // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU.
975     AntiAliasedLinesUseTex  = true;             // Enable anti-aliased lines/borders using textures where possible. Require back-end to render with bilinear filtering.
976     AntiAliasedFill         = true;             // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).
977     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.
978     CircleSegmentMaxError   = 1.60f;            // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
979 
980     // Default theme
981     ImGui::StyleColorsDark(this);
982 }
983 
984 // 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.
985 // 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)986 void ImGuiStyle::ScaleAllSizes(float scale_factor)
987 {
988     WindowPadding = ImFloor(WindowPadding * scale_factor);
989     WindowRounding = ImFloor(WindowRounding * scale_factor);
990     WindowMinSize = ImFloor(WindowMinSize * scale_factor);
991     ChildRounding = ImFloor(ChildRounding * scale_factor);
992     PopupRounding = ImFloor(PopupRounding * scale_factor);
993     FramePadding = ImFloor(FramePadding * scale_factor);
994     FrameRounding = ImFloor(FrameRounding * scale_factor);
995     ItemSpacing = ImFloor(ItemSpacing * scale_factor);
996     ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
997     TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
998     IndentSpacing = ImFloor(IndentSpacing * scale_factor);
999     ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
1000     ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
1001     ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
1002     GrabMinSize = ImFloor(GrabMinSize * scale_factor);
1003     GrabRounding = ImFloor(GrabRounding * scale_factor);
1004     LogSliderDeadzone = ImFloor(LogSliderDeadzone * scale_factor);
1005     TabRounding = ImFloor(TabRounding * scale_factor);
1006     if (TabMinWidthForUnselectedCloseButton != FLT_MAX)
1007         TabMinWidthForUnselectedCloseButton = ImFloor(TabMinWidthForUnselectedCloseButton * scale_factor);
1008     DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
1009     DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
1010     MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
1011 }
1012 
ImGuiIO()1013 ImGuiIO::ImGuiIO()
1014 {
1015     // Most fields are initialized with zero
1016     memset(this, 0, sizeof(*this));
1017     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.
1018 
1019     // Settings
1020     ConfigFlags = ImGuiConfigFlags_None;
1021     BackendFlags = ImGuiBackendFlags_None;
1022     DisplaySize = ImVec2(-1.0f, -1.0f);
1023     DeltaTime = 1.0f / 60.0f;
1024     IniSavingRate = 5.0f;
1025     IniFilename = "imgui.ini";
1026     LogFilename = "imgui_log.txt";
1027     MouseDoubleClickTime = 0.30f;
1028     MouseDoubleClickMaxDist = 6.0f;
1029     for (int i = 0; i < ImGuiKey_COUNT; i++)
1030         KeyMap[i] = -1;
1031     KeyRepeatDelay = 0.275f;
1032     KeyRepeatRate = 0.050f;
1033     UserData = NULL;
1034 
1035     Fonts = NULL;
1036     FontGlobalScale = 1.0f;
1037     FontDefault = NULL;
1038     FontAllowUserScaling = false;
1039     DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1040 
1041     // Docking options (when ImGuiConfigFlags_DockingEnable is set)
1042     ConfigDockingNoSplit = false;
1043     ConfigDockingWithShift = false;
1044     ConfigDockingAlwaysTabBar = false;
1045     ConfigDockingTransparentPayload = false;
1046 
1047     // Viewport options (when ImGuiConfigFlags_ViewportsEnable is set)
1048     ConfigViewportsNoAutoMerge = false;
1049     ConfigViewportsNoTaskBarIcon = false;
1050     ConfigViewportsNoDecoration = true;
1051     ConfigViewportsNoDefaultParent = false;
1052 
1053     // Miscellaneous options
1054     MouseDrawCursor = false;
1055 #ifdef __APPLE__
1056     ConfigMacOSXBehaviors = true;  // Set Mac OS X style defaults based on __APPLE__ compile time flag
1057 #else
1058     ConfigMacOSXBehaviors = false;
1059 #endif
1060     ConfigInputTextCursorBlink = true;
1061     ConfigWindowsResizeFromEdges = true;
1062     ConfigWindowsMoveFromTitleBarOnly = false;
1063     ConfigWindowsMemoryCompactTimer = 60.0f;
1064 
1065     // Platform Functions
1066     BackendPlatformName = BackendRendererName = NULL;
1067     BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1068     GetClipboardTextFn = GetClipboardTextFn_DefaultImpl;   // Platform dependent default implementations
1069     SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
1070     ClipboardUserData = NULL;
1071 
1072 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1073     RenderDrawListsFn = NULL;
1074 #endif
1075 
1076     // Input (NB: we already have memset zero the entire structure!)
1077     MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1078     MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1079     MouseDragThreshold = 6.0f;
1080     for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1081     for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i]  = KeysDownDurationPrev[i] = -1.0f;
1082     for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
1083 }
1084 
1085 // Pass in translated ASCII characters for text input.
1086 // - with glfw you can get those from the callback set in glfwSetCharCallback()
1087 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(unsigned int c)1088 void ImGuiIO::AddInputCharacter(unsigned int c)
1089 {
1090     if (c != 0)
1091         InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID);
1092 }
1093 
1094 // UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so
1095 // we should save the high surrogate.
AddInputCharacterUTF16(ImWchar16 c)1096 void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c)
1097 {
1098     if (c == 0 && InputQueueSurrogate == 0)
1099         return;
1100 
1101     if ((c & 0xFC00) == 0xD800) // High surrogate, must save
1102     {
1103         if (InputQueueSurrogate != 0)
1104             InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID);
1105         InputQueueSurrogate = c;
1106         return;
1107     }
1108 
1109     ImWchar cp = c;
1110     if (InputQueueSurrogate != 0)
1111     {
1112         if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate
1113             InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID);
1114         else if (IM_UNICODE_CODEPOINT_MAX == (0xFFFF)) // Codepoint will not fit in ImWchar (extra parenthesis around 0xFFFF somehow fixes -Wunreachable-code with Clang)
1115             cp = IM_UNICODE_CODEPOINT_INVALID;
1116         else
1117             cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000);
1118         InputQueueSurrogate = 0;
1119     }
1120     InputQueueCharacters.push_back(cp);
1121 }
1122 
AddInputCharactersUTF8(const char * utf8_chars)1123 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1124 {
1125     while (*utf8_chars != 0)
1126     {
1127         unsigned int c = 0;
1128         utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
1129         if (c != 0)
1130             InputQueueCharacters.push_back((ImWchar)c);
1131     }
1132 }
1133 
ClearInputCharacters()1134 void ImGuiIO::ClearInputCharacters()
1135 {
1136     InputQueueCharacters.resize(0);
1137 }
1138 
1139 //-----------------------------------------------------------------------------
1140 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
1141 //-----------------------------------------------------------------------------
1142 
ImBezierClosestPoint(const ImVec2 & p1,const ImVec2 & p2,const ImVec2 & p3,const ImVec2 & p4,const ImVec2 & p,int num_segments)1143 ImVec2 ImBezierClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments)
1144 {
1145     IM_ASSERT(num_segments > 0); // Use ImBezierClosestPointCasteljau()
1146     ImVec2 p_last = p1;
1147     ImVec2 p_closest;
1148     float p_closest_dist2 = FLT_MAX;
1149     float t_step = 1.0f / (float)num_segments;
1150     for (int i_step = 1; i_step <= num_segments; i_step++)
1151     {
1152         ImVec2 p_current = ImBezierCalc(p1, p2, p3, p4, t_step * i_step);
1153         ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1154         float dist2 = ImLengthSqr(p - p_line);
1155         if (dist2 < p_closest_dist2)
1156         {
1157             p_closest = p_line;
1158             p_closest_dist2 = dist2;
1159         }
1160         p_last = p_current;
1161     }
1162     return p_closest;
1163 }
1164 
1165 // Closely mimics PathBezierToCasteljau() in imgui_draw.cpp
BezierClosestPointCasteljauStep(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)1166 static void BezierClosestPointCasteljauStep(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)
1167 {
1168     float dx = x4 - x1;
1169     float dy = y4 - y1;
1170     float d2 = ((x2 - x4) * dy - (y2 - y4) * dx);
1171     float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
1172     d2 = (d2 >= 0) ? d2 : -d2;
1173     d3 = (d3 >= 0) ? d3 : -d3;
1174     if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))
1175     {
1176         ImVec2 p_current(x4, y4);
1177         ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1178         float dist2 = ImLengthSqr(p - p_line);
1179         if (dist2 < p_closest_dist2)
1180         {
1181             p_closest = p_line;
1182             p_closest_dist2 = dist2;
1183         }
1184         p_last = p_current;
1185     }
1186     else if (level < 10)
1187     {
1188         float x12 = (x1 + x2)*0.5f,       y12 = (y1 + y2)*0.5f;
1189         float x23 = (x2 + x3)*0.5f,       y23 = (y2 + y3)*0.5f;
1190         float x34 = (x3 + x4)*0.5f,       y34 = (y3 + y4)*0.5f;
1191         float x123 = (x12 + x23)*0.5f,    y123 = (y12 + y23)*0.5f;
1192         float x234 = (x23 + x34)*0.5f,    y234 = (y23 + y34)*0.5f;
1193         float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f;
1194         BezierClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);
1195         BezierClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);
1196     }
1197 }
1198 
1199 // tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol
1200 // Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically.
ImBezierClosestPointCasteljau(const ImVec2 & p1,const ImVec2 & p2,const ImVec2 & p3,const ImVec2 & p4,const ImVec2 & p,float tess_tol)1201 ImVec2 ImBezierClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol)
1202 {
1203     IM_ASSERT(tess_tol > 0.0f);
1204     ImVec2 p_last = p1;
1205     ImVec2 p_closest;
1206     float p_closest_dist2 = FLT_MAX;
1207     BezierClosestPointCasteljauStep(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);
1208     return p_closest;
1209 }
1210 
ImLineClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & p)1211 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1212 {
1213     ImVec2 ap = p - a;
1214     ImVec2 ab_dir = b - a;
1215     float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1216     if (dot < 0.0f)
1217         return a;
1218     float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1219     if (dot > ab_len_sqr)
1220         return b;
1221     return a + ab_dir * dot / ab_len_sqr;
1222 }
1223 
ImTriangleContainsPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1224 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1225 {
1226     bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1227     bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1228     bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1229     return ((b1 == b2) && (b2 == b3));
1230 }
1231 
ImTriangleBarycentricCoords(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p,float & out_u,float & out_v,float & out_w)1232 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1233 {
1234     ImVec2 v0 = b - a;
1235     ImVec2 v1 = c - a;
1236     ImVec2 v2 = p - a;
1237     const float denom = v0.x * v1.y - v1.x * v0.y;
1238     out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1239     out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1240     out_u = 1.0f - out_v - out_w;
1241 }
1242 
ImTriangleClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1243 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1244 {
1245     ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1246     ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1247     ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1248     float dist2_ab = ImLengthSqr(p - proj_ab);
1249     float dist2_bc = ImLengthSqr(p - proj_bc);
1250     float dist2_ca = ImLengthSqr(p - proj_ca);
1251     float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1252     if (m == dist2_ab)
1253         return proj_ab;
1254     if (m == dist2_bc)
1255         return proj_bc;
1256     return proj_ca;
1257 }
1258 
1259 //-----------------------------------------------------------------------------
1260 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
1261 //-----------------------------------------------------------------------------
1262 
1263 // 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)1264 int ImStricmp(const char* str1, const char* str2)
1265 {
1266     int d;
1267     while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
1268     return d;
1269 }
1270 
ImStrnicmp(const char * str1,const char * str2,size_t count)1271 int ImStrnicmp(const char* str1, const char* str2, size_t count)
1272 {
1273     int d = 0;
1274     while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
1275     return d;
1276 }
1277 
ImStrncpy(char * dst,const char * src,size_t count)1278 void ImStrncpy(char* dst, const char* src, size_t count)
1279 {
1280     if (count < 1)
1281         return;
1282     if (count > 1)
1283         strncpy(dst, src, count - 1);
1284     dst[count - 1] = 0;
1285 }
1286 
ImStrdup(const char * str)1287 char* ImStrdup(const char* str)
1288 {
1289     size_t len = strlen(str);
1290     void* buf = IM_ALLOC(len + 1);
1291     return (char*)memcpy(buf, (const void*)str, len + 1);
1292 }
1293 
ImStrdupcpy(char * dst,size_t * p_dst_size,const char * src)1294 char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
1295 {
1296     size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1;
1297     size_t src_size = strlen(src) + 1;
1298     if (dst_buf_size < src_size)
1299     {
1300         IM_FREE(dst);
1301         dst = (char*)IM_ALLOC(src_size);
1302         if (p_dst_size)
1303             *p_dst_size = src_size;
1304     }
1305     return (char*)memcpy(dst, (const void*)src, src_size);
1306 }
1307 
ImStrchrRange(const char * str,const char * str_end,char c)1308 const char* ImStrchrRange(const char* str, const char* str_end, char c)
1309 {
1310     const char* p = (const char*)memchr(str, (int)c, str_end - str);
1311     return p;
1312 }
1313 
ImStrlenW(const ImWchar * str)1314 int ImStrlenW(const ImWchar* str)
1315 {
1316     //return (int)wcslen((const wchar_t*)str);  // FIXME-OPT: Could use this when wchar_t are 16-bit
1317     int n = 0;
1318     while (*str++) n++;
1319     return n;
1320 }
1321 
1322 // Find end-of-line. Return pointer will point to either first \n, either str_end.
ImStreolRange(const char * str,const char * str_end)1323 const char* ImStreolRange(const char* str, const char* str_end)
1324 {
1325     const char* p = (const char*)memchr(str, '\n', str_end - str);
1326     return p ? p : str_end;
1327 }
1328 
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)1329 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
1330 {
1331     while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1332         buf_mid_line--;
1333     return buf_mid_line;
1334 }
1335 
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)1336 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1337 {
1338     if (!needle_end)
1339         needle_end = needle + strlen(needle);
1340 
1341     const char un0 = (char)toupper(*needle);
1342     while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1343     {
1344         if (toupper(*haystack) == un0)
1345         {
1346             const char* b = needle + 1;
1347             for (const char* a = haystack + 1; b < needle_end; a++, b++)
1348                 if (toupper(*a) != toupper(*b))
1349                     break;
1350             if (b == needle_end)
1351                 return haystack;
1352         }
1353         haystack++;
1354     }
1355     return NULL;
1356 }
1357 
1358 // 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)1359 void ImStrTrimBlanks(char* buf)
1360 {
1361     char* p = buf;
1362     while (p[0] == ' ' || p[0] == '\t')     // Leading blanks
1363         p++;
1364     char* p_start = p;
1365     while (*p != 0)                         // Find end of string
1366         p++;
1367     while (p > p_start && (p[-1] == ' ' || p[-1] == '\t'))  // Trailing blanks
1368         p--;
1369     if (p_start != buf)                     // Copy memory if we had leading blanks
1370         memmove(buf, p_start, p - p_start);
1371     buf[p - p_start] = 0;                   // Zero terminate
1372 }
1373 
ImStrSkipBlank(const char * str)1374 const char* ImStrSkipBlank(const char* str)
1375 {
1376     while (str[0] == ' ' || str[0] == '\t')
1377         str++;
1378     return str;
1379 }
1380 
1381 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1382 // 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.
1383 // B) When buf==NULL vsnprintf() will return the output size.
1384 #ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1385 
1386 // We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h)
1387 // You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1388 // and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are
1389 // designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.)
1390 #ifdef IMGUI_USE_STB_SPRINTF
1391 #define STB_SPRINTF_IMPLEMENTATION
1392 #include "stb_sprintf.h"
1393 #endif
1394 
1395 #if defined(_MSC_VER) && !defined(vsnprintf)
1396 #define vsnprintf _vsnprintf
1397 #endif
1398 
ImFormatString(char * buf,size_t buf_size,const char * fmt,...)1399 int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1400 {
1401     va_list args;
1402     va_start(args, fmt);
1403 #ifdef IMGUI_USE_STB_SPRINTF
1404     int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1405 #else
1406     int w = vsnprintf(buf, buf_size, fmt, args);
1407 #endif
1408     va_end(args);
1409     if (buf == NULL)
1410         return w;
1411     if (w == -1 || w >= (int)buf_size)
1412         w = (int)buf_size - 1;
1413     buf[w] = 0;
1414     return w;
1415 }
1416 
ImFormatStringV(char * buf,size_t buf_size,const char * fmt,va_list args)1417 int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1418 {
1419 #ifdef IMGUI_USE_STB_SPRINTF
1420     int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1421 #else
1422     int w = vsnprintf(buf, buf_size, fmt, args);
1423 #endif
1424     if (buf == NULL)
1425         return w;
1426     if (w == -1 || w >= (int)buf_size)
1427         w = (int)buf_size - 1;
1428     buf[w] = 0;
1429     return w;
1430 }
1431 #endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1432 
1433 // CRC32 needs a 1KB lookup table (not cache friendly)
1434 // Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
1435 // - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
1436 static const ImU32 GCrc32LookupTable[256] =
1437 {
1438     0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
1439     0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
1440     0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
1441     0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
1442     0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
1443     0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
1444     0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
1445     0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
1446     0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
1447     0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
1448     0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
1449     0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
1450     0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
1451     0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
1452     0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
1453     0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
1454 };
1455 
1456 // Known size hash
1457 // It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
1458 // 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)1459 ImU32 ImHashData(const void* data_p, size_t data_size, ImU32 seed)
1460 {
1461     ImU32 crc = ~seed;
1462     const unsigned char* data = (const unsigned char*)data_p;
1463     const ImU32* crc32_lut = GCrc32LookupTable;
1464     while (data_size-- != 0)
1465         crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
1466     return ~crc;
1467 }
1468 
1469 // Zero-terminated string hash, with support for ### to reset back to seed value
1470 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1471 // Because this syntax is rarely used we are optimizing for the common case.
1472 // - If we reach ### in the string we discard the hash so far and reset to the seed.
1473 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
1474 // 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)1475 ImU32 ImHashStr(const char* data_p, size_t data_size, ImU32 seed)
1476 {
1477     seed = ~seed;
1478     ImU32 crc = seed;
1479     const unsigned char* data = (const unsigned char*)data_p;
1480     const ImU32* crc32_lut = GCrc32LookupTable;
1481     if (data_size != 0)
1482     {
1483         while (data_size-- != 0)
1484         {
1485             unsigned char c = *data++;
1486             if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')
1487                 crc = seed;
1488             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1489         }
1490     }
1491     else
1492     {
1493         while (unsigned char c = *data++)
1494         {
1495             if (c == '#' && data[0] == '#' && data[1] == '#')
1496                 crc = seed;
1497             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1498         }
1499     }
1500     return ~crc;
1501 }
1502 
1503 //-----------------------------------------------------------------------------
1504 // [SECTION] MISC HELPERS/UTILITIES (File functions)
1505 //-----------------------------------------------------------------------------
1506 
1507 // Default file functions
1508 #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
1509 
ImFileOpen(const char * filename,const char * mode)1510 ImFileHandle ImFileOpen(const char* filename, const char* mode)
1511 {
1512 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__)
1513     // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.
1514     // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32!
1515     const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
1516     const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
1517     ImVector<ImWchar> buf;
1518     buf.resize(filename_wsize + mode_wsize);
1519     ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, (wchar_t*)&buf[0], filename_wsize);
1520     ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, (wchar_t*)&buf[filename_wsize], mode_wsize);
1521     return ::_wfopen((const wchar_t*)&buf[0], (const wchar_t*)&buf[filename_wsize]);
1522 #else
1523     return fopen(filename, mode);
1524 #endif
1525 }
1526 
1527 // 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)1528 bool    ImFileClose(ImFileHandle f)     { return fclose(f) == 0; }
ImFileGetSize(ImFileHandle f)1529 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)1530 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)1531 ImU64   ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f)    { return fwrite(data, (size_t)sz, (size_t)count, f); }
1532 #endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
1533 
1534 // Helper: Load file content into memory
1535 // Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree()
1536 // 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)1537 void*   ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes)
1538 {
1539     IM_ASSERT(filename && mode);
1540     if (out_file_size)
1541         *out_file_size = 0;
1542 
1543     ImFileHandle f;
1544     if ((f = ImFileOpen(filename, mode)) == NULL)
1545         return NULL;
1546 
1547     size_t file_size = (size_t)ImFileGetSize(f);
1548     if (file_size == (size_t)-1)
1549     {
1550         ImFileClose(f);
1551         return NULL;
1552     }
1553 
1554     void* file_data = IM_ALLOC(file_size + padding_bytes);
1555     if (file_data == NULL)
1556     {
1557         ImFileClose(f);
1558         return NULL;
1559     }
1560     if (ImFileRead(file_data, 1, file_size, f) != file_size)
1561     {
1562         ImFileClose(f);
1563         IM_FREE(file_data);
1564         return NULL;
1565     }
1566     if (padding_bytes > 0)
1567         memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
1568 
1569     ImFileClose(f);
1570     if (out_file_size)
1571         *out_file_size = file_size;
1572 
1573     return file_data;
1574 }
1575 
1576 //-----------------------------------------------------------------------------
1577 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
1578 //-----------------------------------------------------------------------------
1579 
1580 // Convert UTF-8 to 32-bit character, process single character input.
1581 // Based on stb_from_utf8() from github.com/nothings/stb/
1582 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)1583 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1584 {
1585     unsigned int c = (unsigned int)-1;
1586     const unsigned char* str = (const unsigned char*)in_text;
1587     if (!(*str & 0x80))
1588     {
1589         c = (unsigned int)(*str++);
1590         *out_char = c;
1591         return 1;
1592     }
1593     if ((*str & 0xe0) == 0xc0)
1594     {
1595         *out_char = IM_UNICODE_CODEPOINT_INVALID; // will be invalid but not end of string
1596         if (in_text_end && in_text_end - (const char*)str < 2) return 1;
1597         if (*str < 0xc2) return 2;
1598         c = (unsigned int)((*str++ & 0x1f) << 6);
1599         if ((*str & 0xc0) != 0x80) return 2;
1600         c += (*str++ & 0x3f);
1601         *out_char = c;
1602         return 2;
1603     }
1604     if ((*str & 0xf0) == 0xe0)
1605     {
1606         *out_char = IM_UNICODE_CODEPOINT_INVALID; // will be invalid but not end of string
1607         if (in_text_end && in_text_end - (const char*)str < 3) return 1;
1608         if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3;
1609         if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below
1610         c = (unsigned int)((*str++ & 0x0f) << 12);
1611         if ((*str & 0xc0) != 0x80) return 3;
1612         c += (unsigned int)((*str++ & 0x3f) << 6);
1613         if ((*str & 0xc0) != 0x80) return 3;
1614         c += (*str++ & 0x3f);
1615         *out_char = c;
1616         return 3;
1617     }
1618     if ((*str & 0xf8) == 0xf0)
1619     {
1620         *out_char = IM_UNICODE_CODEPOINT_INVALID; // will be invalid but not end of string
1621         if (in_text_end && in_text_end - (const char*)str < 4) return 1;
1622         if (*str > 0xf4) return 4;
1623         if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4;
1624         if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below
1625         c = (unsigned int)((*str++ & 0x07) << 18);
1626         if ((*str & 0xc0) != 0x80) return 4;
1627         c += (unsigned int)((*str++ & 0x3f) << 12);
1628         if ((*str & 0xc0) != 0x80) return 4;
1629         c += (unsigned int)((*str++ & 0x3f) << 6);
1630         if ((*str & 0xc0) != 0x80) return 4;
1631         c += (*str++ & 0x3f);
1632         // utf-8 encodings of values used in surrogate pairs are invalid
1633         if ((c & 0xFFFFF800) == 0xD800) return 4;
1634         // If codepoint does not fit in ImWchar, use replacement character U+FFFD instead
1635         if (c > IM_UNICODE_CODEPOINT_MAX) c = IM_UNICODE_CODEPOINT_INVALID;
1636         *out_char = c;
1637         return 4;
1638     }
1639     *out_char = 0;
1640     return 0;
1641 }
1642 
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1643 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1644 {
1645     ImWchar* buf_out = buf;
1646     ImWchar* buf_end = buf + buf_size;
1647     while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
1648     {
1649         unsigned int c;
1650         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1651         if (c == 0)
1652             break;
1653         *buf_out++ = (ImWchar)c;
1654     }
1655     *buf_out = 0;
1656     if (in_text_remaining)
1657         *in_text_remaining = in_text;
1658     return (int)(buf_out - buf);
1659 }
1660 
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1661 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1662 {
1663     int char_count = 0;
1664     while ((!in_text_end || in_text < in_text_end) && *in_text)
1665     {
1666         unsigned int c;
1667         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1668         if (c == 0)
1669             break;
1670         char_count++;
1671     }
1672     return char_count;
1673 }
1674 
1675 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8(char * buf,int buf_size,unsigned int c)1676 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1677 {
1678     if (c < 0x80)
1679     {
1680         buf[0] = (char)c;
1681         return 1;
1682     }
1683     if (c < 0x800)
1684     {
1685         if (buf_size < 2) return 0;
1686         buf[0] = (char)(0xc0 + (c >> 6));
1687         buf[1] = (char)(0x80 + (c & 0x3f));
1688         return 2;
1689     }
1690     if (c < 0x10000)
1691     {
1692         if (buf_size < 3) return 0;
1693         buf[0] = (char)(0xe0 + (c >> 12));
1694         buf[1] = (char)(0x80 + ((c >> 6) & 0x3f));
1695         buf[2] = (char)(0x80 + ((c ) & 0x3f));
1696         return 3;
1697     }
1698     if (c <= 0x10FFFF)
1699     {
1700         if (buf_size < 4) return 0;
1701         buf[0] = (char)(0xf0 + (c >> 18));
1702         buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1703         buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1704         buf[3] = (char)(0x80 + ((c ) & 0x3f));
1705         return 4;
1706     }
1707     // Invalid code point, the max unicode is 0x10FFFF
1708     return 0;
1709 }
1710 
1711 // Not optimal but we very rarely use this function.
ImTextCountUtf8BytesFromChar(const char * in_text,const char * in_text_end)1712 int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
1713 {
1714     unsigned int unused = 0;
1715     return ImTextCharFromUtf8(&unused, in_text, in_text_end);
1716 }
1717 
ImTextCountUtf8BytesFromChar(unsigned int c)1718 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1719 {
1720     if (c < 0x80) return 1;
1721     if (c < 0x800) return 2;
1722     if (c < 0x10000) return 3;
1723     if (c <= 0x10FFFF) return 4;
1724     return 3;
1725 }
1726 
ImTextStrToUtf8(char * buf,int buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1727 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1728 {
1729     char* buf_out = buf;
1730     const char* buf_end = buf + buf_size;
1731     while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
1732     {
1733         unsigned int c = (unsigned int)(*in_text++);
1734         if (c < 0x80)
1735             *buf_out++ = (char)c;
1736         else
1737             buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end - buf_out - 1), c);
1738     }
1739     *buf_out = 0;
1740     return (int)(buf_out - buf);
1741 }
1742 
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1743 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1744 {
1745     int bytes_count = 0;
1746     while ((!in_text_end || in_text < in_text_end) && *in_text)
1747     {
1748         unsigned int c = (unsigned int)(*in_text++);
1749         if (c < 0x80)
1750             bytes_count++;
1751         else
1752             bytes_count += ImTextCountUtf8BytesFromChar(c);
1753     }
1754     return bytes_count;
1755 }
1756 
1757 //-----------------------------------------------------------------------------
1758 // [SECTION] MISC HELPERS/UTILITIES (Color functions)
1759 // Note: The Convert functions are early design which are not consistent with other API.
1760 //-----------------------------------------------------------------------------
1761 
ImAlphaBlendColors(ImU32 col_a,ImU32 col_b)1762 IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b)
1763 {
1764     float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;
1765     int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);
1766     int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);
1767     int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);
1768     return IM_COL32(r, g, b, 0xFF);
1769 }
1770 
ColorConvertU32ToFloat4(ImU32 in)1771 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1772 {
1773     float s = 1.0f / 255.0f;
1774     return ImVec4(
1775         ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1776         ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1777         ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1778         ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1779 }
1780 
ColorConvertFloat4ToU32(const ImVec4 & in)1781 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1782 {
1783     ImU32 out;
1784     out  = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1785     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1786     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1787     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1788     return out;
1789 }
1790 
1791 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1792 // 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)1793 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1794 {
1795     float K = 0.f;
1796     if (g < b)
1797     {
1798         ImSwap(g, b);
1799         K = -1.f;
1800     }
1801     if (r < g)
1802     {
1803         ImSwap(r, g);
1804         K = -2.f / 6.f - K;
1805     }
1806 
1807     const float chroma = r - (g < b ? g : b);
1808     out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
1809     out_s = chroma / (r + 1e-20f);
1810     out_v = r;
1811 }
1812 
1813 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1814 // 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)1815 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1816 {
1817     if (s == 0.0f)
1818     {
1819         // gray
1820         out_r = out_g = out_b = v;
1821         return;
1822     }
1823 
1824     h = ImFmod(h, 1.0f) / (60.0f / 360.0f);
1825     int   i = (int)h;
1826     float f = h - (float)i;
1827     float p = v * (1.0f - s);
1828     float q = v * (1.0f - s * f);
1829     float t = v * (1.0f - s * (1.0f - f));
1830 
1831     switch (i)
1832     {
1833     case 0: out_r = v; out_g = t; out_b = p; break;
1834     case 1: out_r = q; out_g = v; out_b = p; break;
1835     case 2: out_r = p; out_g = v; out_b = t; break;
1836     case 3: out_r = p; out_g = q; out_b = v; break;
1837     case 4: out_r = t; out_g = p; out_b = v; break;
1838     case 5: default: out_r = v; out_g = p; out_b = q; break;
1839     }
1840 }
1841 
1842 //-----------------------------------------------------------------------------
1843 // [SECTION] ImGuiStorage
1844 // Helper: Key->value storage
1845 //-----------------------------------------------------------------------------
1846 
1847 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair> & data,ImGuiID key)1848 static ImGuiStorage::ImGuiStoragePair* LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair>& data, ImGuiID key)
1849 {
1850     ImGuiStorage::ImGuiStoragePair* first = data.Data;
1851     ImGuiStorage::ImGuiStoragePair* last = data.Data + data.Size;
1852     size_t count = (size_t)(last - first);
1853     while (count > 0)
1854     {
1855         size_t count2 = count >> 1;
1856         ImGuiStorage::ImGuiStoragePair* mid = first + count2;
1857         if (mid->key < key)
1858         {
1859             first = ++mid;
1860             count -= count2 + 1;
1861         }
1862         else
1863         {
1864             count = count2;
1865         }
1866     }
1867     return first;
1868 }
1869 
1870 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
BuildSortByKey()1871 void ImGuiStorage::BuildSortByKey()
1872 {
1873     struct StaticFunc
1874     {
1875         static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)
1876         {
1877             // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1878             if (((const ImGuiStoragePair*)lhs)->key > ((const ImGuiStoragePair*)rhs)->key) return +1;
1879             if (((const ImGuiStoragePair*)lhs)->key < ((const ImGuiStoragePair*)rhs)->key) return -1;
1880             return 0;
1881         }
1882     };
1883     if (Data.Size > 1)
1884         ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairCompareByID);
1885 }
1886 
GetInt(ImGuiID key,int default_val) const1887 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1888 {
1889     ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1890     if (it == Data.end() || it->key != key)
1891         return default_val;
1892     return it->val_i;
1893 }
1894 
GetBool(ImGuiID key,bool default_val) const1895 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1896 {
1897     return GetInt(key, default_val ? 1 : 0) != 0;
1898 }
1899 
GetFloat(ImGuiID key,float default_val) const1900 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1901 {
1902     ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1903     if (it == Data.end() || it->key != key)
1904         return default_val;
1905     return it->val_f;
1906 }
1907 
GetVoidPtr(ImGuiID key) const1908 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1909 {
1910     ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1911     if (it == Data.end() || it->key != key)
1912         return NULL;
1913     return it->val_p;
1914 }
1915 
1916 // 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)1917 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1918 {
1919     ImGuiStoragePair* it = LowerBound(Data, key);
1920     if (it == Data.end() || it->key != key)
1921         it = Data.insert(it, ImGuiStoragePair(key, default_val));
1922     return &it->val_i;
1923 }
1924 
GetBoolRef(ImGuiID key,bool default_val)1925 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1926 {
1927     return (bool*)GetIntRef(key, default_val ? 1 : 0);
1928 }
1929 
GetFloatRef(ImGuiID key,float default_val)1930 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1931 {
1932     ImGuiStoragePair* it = LowerBound(Data, key);
1933     if (it == Data.end() || it->key != key)
1934         it = Data.insert(it, ImGuiStoragePair(key, default_val));
1935     return &it->val_f;
1936 }
1937 
GetVoidPtrRef(ImGuiID key,void * default_val)1938 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1939 {
1940     ImGuiStoragePair* it = LowerBound(Data, key);
1941     if (it == Data.end() || it->key != key)
1942         it = Data.insert(it, ImGuiStoragePair(key, default_val));
1943     return &it->val_p;
1944 }
1945 
1946 // 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)1947 void ImGuiStorage::SetInt(ImGuiID key, int val)
1948 {
1949     ImGuiStoragePair* it = LowerBound(Data, key);
1950     if (it == Data.end() || it->key != key)
1951     {
1952         Data.insert(it, ImGuiStoragePair(key, val));
1953         return;
1954     }
1955     it->val_i = val;
1956 }
1957 
SetBool(ImGuiID key,bool val)1958 void ImGuiStorage::SetBool(ImGuiID key, bool val)
1959 {
1960     SetInt(key, val ? 1 : 0);
1961 }
1962 
SetFloat(ImGuiID key,float val)1963 void ImGuiStorage::SetFloat(ImGuiID key, float val)
1964 {
1965     ImGuiStoragePair* it = LowerBound(Data, key);
1966     if (it == Data.end() || it->key != key)
1967     {
1968         Data.insert(it, ImGuiStoragePair(key, val));
1969         return;
1970     }
1971     it->val_f = val;
1972 }
1973 
SetVoidPtr(ImGuiID key,void * val)1974 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1975 {
1976     ImGuiStoragePair* it = LowerBound(Data, key);
1977     if (it == Data.end() || it->key != key)
1978     {
1979         Data.insert(it, ImGuiStoragePair(key, val));
1980         return;
1981     }
1982     it->val_p = val;
1983 }
1984 
SetAllInt(int v)1985 void ImGuiStorage::SetAllInt(int v)
1986 {
1987     for (int i = 0; i < Data.Size; i++)
1988         Data[i].val_i = v;
1989 }
1990 
1991 //-----------------------------------------------------------------------------
1992 // [SECTION] ImGuiTextFilter
1993 //-----------------------------------------------------------------------------
1994 
1995 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)1996 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
1997 {
1998     if (default_filter)
1999     {
2000         ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
2001         Build();
2002     }
2003     else
2004     {
2005         InputBuf[0] = 0;
2006         CountGrep = 0;
2007     }
2008 }
2009 
Draw(const char * label,float width)2010 bool ImGuiTextFilter::Draw(const char* label, float width)
2011 {
2012     if (width != 0.0f)
2013         ImGui::SetNextItemWidth(width);
2014     bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
2015     if (value_changed)
2016         Build();
2017     return value_changed;
2018 }
2019 
split(char separator,ImVector<ImGuiTextRange> * out) const2020 void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const
2021 {
2022     out->resize(0);
2023     const char* wb = b;
2024     const char* we = wb;
2025     while (we < e)
2026     {
2027         if (*we == separator)
2028         {
2029             out->push_back(ImGuiTextRange(wb, we));
2030             wb = we + 1;
2031         }
2032         we++;
2033     }
2034     if (wb != we)
2035         out->push_back(ImGuiTextRange(wb, we));
2036 }
2037 
Build()2038 void ImGuiTextFilter::Build()
2039 {
2040     Filters.resize(0);
2041     ImGuiTextRange input_range(InputBuf, InputBuf + strlen(InputBuf));
2042     input_range.split(',', &Filters);
2043 
2044     CountGrep = 0;
2045     for (int i = 0; i != Filters.Size; i++)
2046     {
2047         ImGuiTextRange& f = Filters[i];
2048         while (f.b < f.e && ImCharIsBlankA(f.b[0]))
2049             f.b++;
2050         while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
2051             f.e--;
2052         if (f.empty())
2053             continue;
2054         if (Filters[i].b[0] != '-')
2055             CountGrep += 1;
2056     }
2057 }
2058 
PassFilter(const char * text,const char * text_end) const2059 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
2060 {
2061     if (Filters.empty())
2062         return true;
2063 
2064     if (text == NULL)
2065         text = "";
2066 
2067     for (int i = 0; i != Filters.Size; i++)
2068     {
2069         const ImGuiTextRange& f = Filters[i];
2070         if (f.empty())
2071             continue;
2072         if (f.b[0] == '-')
2073         {
2074             // Subtract
2075             if (ImStristr(text, text_end, f.b + 1, f.e) != NULL)
2076                 return false;
2077         }
2078         else
2079         {
2080             // Grep
2081             if (ImStristr(text, text_end, f.b, f.e) != NULL)
2082                 return true;
2083         }
2084     }
2085 
2086     // Implicit * grep
2087     if (CountGrep == 0)
2088         return true;
2089 
2090     return false;
2091 }
2092 
2093 //-----------------------------------------------------------------------------
2094 // [SECTION] ImGuiTextBuffer
2095 //-----------------------------------------------------------------------------
2096 
2097 // On some platform vsnprintf() takes va_list by reference and modifies it.
2098 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
2099 #ifndef va_copy
2100 #if defined(__GNUC__) || defined(__clang__)
2101 #define va_copy(dest, src) __builtin_va_copy(dest, src)
2102 #else
2103 #define va_copy(dest, src) (dest = src)
2104 #endif
2105 #endif
2106 
2107 char ImGuiTextBuffer::EmptyString[1] = { 0 };
2108 
append(const char * str,const char * str_end)2109 void ImGuiTextBuffer::append(const char* str, const char* str_end)
2110 {
2111     int len = str_end ? (int)(str_end - str) : (int)strlen(str);
2112 
2113     // Add zero-terminator the first time
2114     const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2115     const int needed_sz = write_off + len;
2116     if (write_off + len >= Buf.Capacity)
2117     {
2118         int new_capacity = Buf.Capacity * 2;
2119         Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2120     }
2121 
2122     Buf.resize(needed_sz);
2123     memcpy(&Buf[write_off - 1], str, (size_t)len);
2124     Buf[write_off - 1 + len] = 0;
2125 }
2126 
appendf(const char * fmt,...)2127 void ImGuiTextBuffer::appendf(const char* fmt, ...)
2128 {
2129     va_list args;
2130     va_start(args, fmt);
2131     appendfv(fmt, args);
2132     va_end(args);
2133 }
2134 
2135 // Helper: Text buffer for logging/accumulating text
appendfv(const char * fmt,va_list args)2136 void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
2137 {
2138     va_list args_copy;
2139     va_copy(args_copy, args);
2140 
2141     int len = ImFormatStringV(NULL, 0, fmt, args);         // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
2142     if (len <= 0)
2143     {
2144         va_end(args_copy);
2145         return;
2146     }
2147 
2148     // Add zero-terminator the first time
2149     const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2150     const int needed_sz = write_off + len;
2151     if (write_off + len >= Buf.Capacity)
2152     {
2153         int new_capacity = Buf.Capacity * 2;
2154         Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2155     }
2156 
2157     Buf.resize(needed_sz);
2158     ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
2159     va_end(args_copy);
2160 }
2161 
2162 //-----------------------------------------------------------------------------
2163 // [SECTION] ImGuiListClipper
2164 // This is currently not as flexible/powerful as it should be and really confusing/spaghetti, mostly because we changed
2165 // the API mid-way through development and support two ways to using the clipper, needs some rework (see TODO)
2166 //-----------------------------------------------------------------------------
2167 
2168 // Helper to calculate coarse clipping of large list of evenly sized items.
2169 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
2170 // 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)2171 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
2172 {
2173     ImGuiContext& g = *GImGui;
2174     ImGuiWindow* window = g.CurrentWindow;
2175     if (g.LogEnabled)
2176     {
2177         // If logging is active, do not perform any clipping
2178         *out_items_display_start = 0;
2179         *out_items_display_end = items_count;
2180         return;
2181     }
2182     if (window->SkipItems)
2183     {
2184         *out_items_display_start = *out_items_display_end = 0;
2185         return;
2186     }
2187 
2188     // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect
2189     ImRect unclipped_rect = window->ClipRect;
2190     if (g.NavMoveRequest)
2191         unclipped_rect.Add(g.NavScoringRect);
2192     if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId)
2193         unclipped_rect.Add(ImRect(window->Pos + window->NavRectRel[0].Min, window->Pos + window->NavRectRel[0].Max));
2194 
2195     const ImVec2 pos = window->DC.CursorPos;
2196     int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
2197     int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
2198 
2199     // When performing a navigation request, ensure we have one item extra in the direction we are moving to
2200     if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up)
2201         start--;
2202     if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down)
2203         end++;
2204 
2205     start = ImClamp(start, 0, items_count);
2206     end = ImClamp(end + 1, start, items_count);
2207     *out_items_display_start = start;
2208     *out_items_display_end = end;
2209 }
2210 
SetCursorPosYAndSetupForPrevLine(float pos_y,float line_height)2211 static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height)
2212 {
2213     // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
2214     // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
2215     // The clipper should probably have a 4th step to display the last item in a regular manner.
2216     ImGuiContext& g = *GImGui;
2217     ImGuiWindow* window = g.CurrentWindow;
2218     window->DC.CursorPos.y = pos_y;
2219     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y);
2220     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.
2221     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.
2222     if (ImGuiColumns* columns = window->DC.CurrentColumns)
2223         columns->LineMinY = window->DC.CursorPos.y;                         // Setting this so that cell Y position are set properly
2224 }
2225 
2226 // Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
2227 // Use case B: Begin() called from constructor with items_height>0
2228 // FIXME-LEGACY: Ideally we should remove the Begin/End functions but they are part of the legacy API we still support. This is why some of the code in Step() calling Begin() and reassign some fields, spaghetti style.
Begin(int count,float items_height)2229 void ImGuiListClipper::Begin(int count, float items_height)
2230 {
2231     ImGuiContext& g = *GImGui;
2232     ImGuiWindow* window = g.CurrentWindow;
2233 
2234     StartPosY = window->DC.CursorPos.y;
2235     ItemsHeight = items_height;
2236     ItemsCount = count;
2237     StepNo = 0;
2238     DisplayEnd = DisplayStart = -1;
2239     if (ItemsHeight > 0.0f)
2240     {
2241         ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display
2242         if (DisplayStart > 0)
2243             SetCursorPosYAndSetupForPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor
2244         StepNo = 2;
2245     }
2246 }
2247 
End()2248 void ImGuiListClipper::End()
2249 {
2250     if (ItemsCount < 0)
2251         return;
2252     // 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.
2253     if (ItemsCount < INT_MAX)
2254         SetCursorPosYAndSetupForPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor
2255     ItemsCount = -1;
2256     StepNo = 3;
2257 }
2258 
Step()2259 bool ImGuiListClipper::Step()
2260 {
2261     ImGuiContext& g = *GImGui;
2262     ImGuiWindow* window = g.CurrentWindow;
2263 
2264     if (ItemsCount == 0 || window->SkipItems)
2265     {
2266         ItemsCount = -1;
2267         return false;
2268     }
2269     if (StepNo == 0) // Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height.
2270     {
2271         DisplayStart = 0;
2272         DisplayEnd = 1;
2273         StartPosY = window->DC.CursorPos.y;
2274         StepNo = 1;
2275         return true;
2276     }
2277     if (StepNo == 1) // Step 1: the clipper infer height from first element, calculate the actual range of elements to display, and position the cursor before the first element.
2278     {
2279         if (ItemsCount == 1) { ItemsCount = -1; return false; }
2280         float items_height = window->DC.CursorPos.y - StartPosY;
2281         IM_ASSERT(items_height > 0.0f);   // If this triggers, it means Item 0 hasn't moved the cursor vertically
2282         Begin(ItemsCount - 1, items_height);
2283         DisplayStart++;
2284         DisplayEnd++;
2285         StepNo = 3;
2286         return true;
2287     }
2288     if (StepNo == 2) // Step 2: empty step only required if an explicit items_height was passed to constructor or Begin() and user still call Step(). Does nothing and switch to Step 3.
2289     {
2290         IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0);
2291         StepNo = 3;
2292         return true;
2293     }
2294     if (StepNo == 3) // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), advance the cursor to the end of the list and then returns 'false' to end the loop.
2295         End();
2296     return false;
2297 }
2298 
2299 //-----------------------------------------------------------------------------
2300 // [SECTION] STYLING
2301 //-----------------------------------------------------------------------------
2302 
GetStyle()2303 ImGuiStyle& ImGui::GetStyle()
2304 {
2305     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
2306     return GImGui->Style;
2307 }
2308 
GetColorU32(ImGuiCol idx,float alpha_mul)2309 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
2310 {
2311     ImGuiStyle& style = GImGui->Style;
2312     ImVec4 c = style.Colors[idx];
2313     c.w *= style.Alpha * alpha_mul;
2314     return ColorConvertFloat4ToU32(c);
2315 }
2316 
GetColorU32(const ImVec4 & col)2317 ImU32 ImGui::GetColorU32(const ImVec4& col)
2318 {
2319     ImGuiStyle& style = GImGui->Style;
2320     ImVec4 c = col;
2321     c.w *= style.Alpha;
2322     return ColorConvertFloat4ToU32(c);
2323 }
2324 
GetStyleColorVec4(ImGuiCol idx)2325 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
2326 {
2327     ImGuiStyle& style = GImGui->Style;
2328     return style.Colors[idx];
2329 }
2330 
GetColorU32(ImU32 col)2331 ImU32 ImGui::GetColorU32(ImU32 col)
2332 {
2333     ImGuiStyle& style = GImGui->Style;
2334     if (style.Alpha >= 1.0f)
2335         return col;
2336     ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
2337     a = (ImU32)(a * style.Alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
2338     return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
2339 }
2340 
2341 // 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)2342 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
2343 {
2344     ImGuiContext& g = *GImGui;
2345     ImGuiColorMod backup;
2346     backup.Col = idx;
2347     backup.BackupValue = g.Style.Colors[idx];
2348     g.ColorModifiers.push_back(backup);
2349     g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
2350 }
2351 
PushStyleColor(ImGuiCol idx,const ImVec4 & col)2352 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
2353 {
2354     ImGuiContext& g = *GImGui;
2355     ImGuiColorMod backup;
2356     backup.Col = idx;
2357     backup.BackupValue = g.Style.Colors[idx];
2358     g.ColorModifiers.push_back(backup);
2359     g.Style.Colors[idx] = col;
2360 }
2361 
PopStyleColor(int count)2362 void ImGui::PopStyleColor(int count)
2363 {
2364     ImGuiContext& g = *GImGui;
2365     while (count > 0)
2366     {
2367         ImGuiColorMod& backup = g.ColorModifiers.back();
2368         g.Style.Colors[backup.Col] = backup.BackupValue;
2369         g.ColorModifiers.pop_back();
2370         count--;
2371     }
2372 }
2373 
2374 struct ImGuiStyleVarInfo
2375 {
2376     ImGuiDataType   Type;
2377     ImU32           Count;
2378     ImU32           Offset;
GetVarPtrImGuiStyleVarInfo2379     void*           GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
2380 };
2381 
2382 static const ImGuiStyleVarInfo GStyleVarInfo[] =
2383 {
2384     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) },               // ImGuiStyleVar_Alpha
2385     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) },       // ImGuiStyleVar_WindowPadding
2386     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) },      // ImGuiStyleVar_WindowRounding
2387     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) },    // ImGuiStyleVar_WindowBorderSize
2388     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) },       // ImGuiStyleVar_WindowMinSize
2389     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) },    // ImGuiStyleVar_WindowTitleAlign
2390     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) },       // ImGuiStyleVar_ChildRounding
2391     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) },     // ImGuiStyleVar_ChildBorderSize
2392     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) },       // ImGuiStyleVar_PopupRounding
2393     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) },     // ImGuiStyleVar_PopupBorderSize
2394     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) },        // ImGuiStyleVar_FramePadding
2395     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) },       // ImGuiStyleVar_FrameRounding
2396     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) },     // ImGuiStyleVar_FrameBorderSize
2397     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) },         // ImGuiStyleVar_ItemSpacing
2398     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) },    // ImGuiStyleVar_ItemInnerSpacing
2399     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) },       // ImGuiStyleVar_IndentSpacing
2400     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) },       // ImGuiStyleVar_ScrollbarSize
2401     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) },   // ImGuiStyleVar_ScrollbarRounding
2402     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) },         // ImGuiStyleVar_GrabMinSize
2403     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) },        // ImGuiStyleVar_GrabRounding
2404     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) },         // ImGuiStyleVar_TabRounding
2405     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) },     // ImGuiStyleVar_ButtonTextAlign
2406     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
2407 };
2408 
GetStyleVarInfo(ImGuiStyleVar idx)2409 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
2410 {
2411     IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
2412     IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
2413     return &GStyleVarInfo[idx];
2414 }
2415 
PushStyleVar(ImGuiStyleVar idx,float val)2416 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
2417 {
2418     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
2419     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
2420     {
2421         ImGuiContext& g = *GImGui;
2422         float* pvar = (float*)var_info->GetVarPtr(&g.Style);
2423         g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
2424         *pvar = val;
2425         return;
2426     }
2427     IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!");
2428 }
2429 
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)2430 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
2431 {
2432     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
2433     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
2434     {
2435         ImGuiContext& g = *GImGui;
2436         ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
2437         g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
2438         *pvar = val;
2439         return;
2440     }
2441     IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!");
2442 }
2443 
PopStyleVar(int count)2444 void ImGui::PopStyleVar(int count)
2445 {
2446     ImGuiContext& g = *GImGui;
2447     while (count > 0)
2448     {
2449         // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
2450         ImGuiStyleMod& backup = g.StyleModifiers.back();
2451         const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
2452         void* data = info->GetVarPtr(&g.Style);
2453         if (info->Type == ImGuiDataType_Float && info->Count == 1)      { ((float*)data)[0] = backup.BackupFloat[0]; }
2454         else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
2455         g.StyleModifiers.pop_back();
2456         count--;
2457     }
2458 }
2459 
GetStyleColorName(ImGuiCol idx)2460 const char* ImGui::GetStyleColorName(ImGuiCol idx)
2461 {
2462     // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
2463     switch (idx)
2464     {
2465     case ImGuiCol_Text: return "Text";
2466     case ImGuiCol_TextDisabled: return "TextDisabled";
2467     case ImGuiCol_WindowBg: return "WindowBg";
2468     case ImGuiCol_ChildBg: return "ChildBg";
2469     case ImGuiCol_PopupBg: return "PopupBg";
2470     case ImGuiCol_Border: return "Border";
2471     case ImGuiCol_BorderShadow: return "BorderShadow";
2472     case ImGuiCol_FrameBg: return "FrameBg";
2473     case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
2474     case ImGuiCol_FrameBgActive: return "FrameBgActive";
2475     case ImGuiCol_TitleBg: return "TitleBg";
2476     case ImGuiCol_TitleBgActive: return "TitleBgActive";
2477     case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
2478     case ImGuiCol_MenuBarBg: return "MenuBarBg";
2479     case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
2480     case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
2481     case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
2482     case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
2483     case ImGuiCol_CheckMark: return "CheckMark";
2484     case ImGuiCol_SliderGrab: return "SliderGrab";
2485     case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
2486     case ImGuiCol_Button: return "Button";
2487     case ImGuiCol_ButtonHovered: return "ButtonHovered";
2488     case ImGuiCol_ButtonActive: return "ButtonActive";
2489     case ImGuiCol_Header: return "Header";
2490     case ImGuiCol_HeaderHovered: return "HeaderHovered";
2491     case ImGuiCol_HeaderActive: return "HeaderActive";
2492     case ImGuiCol_Separator: return "Separator";
2493     case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
2494     case ImGuiCol_SeparatorActive: return "SeparatorActive";
2495     case ImGuiCol_ResizeGrip: return "ResizeGrip";
2496     case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
2497     case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
2498     case ImGuiCol_Tab: return "Tab";
2499     case ImGuiCol_TabHovered: return "TabHovered";
2500     case ImGuiCol_TabActive: return "TabActive";
2501     case ImGuiCol_TabUnfocused: return "TabUnfocused";
2502     case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive";
2503     case ImGuiCol_DockingPreview: return "DockingPreview";
2504     case ImGuiCol_DockingEmptyBg: return "DockingEmptyBg";
2505     case ImGuiCol_PlotLines: return "PlotLines";
2506     case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
2507     case ImGuiCol_PlotHistogram: return "PlotHistogram";
2508     case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
2509     case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
2510     case ImGuiCol_DragDropTarget: return "DragDropTarget";
2511     case ImGuiCol_NavHighlight: return "NavHighlight";
2512     case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
2513     case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
2514     case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
2515     }
2516     IM_ASSERT(0);
2517     return "Unknown";
2518 }
2519 
2520 
2521 //-----------------------------------------------------------------------------
2522 // [SECTION] RENDER HELPERS
2523 // Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change,
2524 // we need a nicer separation between low-level functions and high-level functions relying on the ImGui context.
2525 // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context.
2526 //-----------------------------------------------------------------------------
2527 
FindRenderedTextEnd(const char * text,const char * text_end)2528 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2529 {
2530     const char* text_display_end = text;
2531     if (!text_end)
2532         text_end = (const char*)-1;
2533 
2534     while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2535         text_display_end++;
2536     return text_display_end;
2537 }
2538 
2539 // Internal ImGui functions to render text
2540 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)2541 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2542 {
2543     ImGuiContext& g = *GImGui;
2544     ImGuiWindow* window = g.CurrentWindow;
2545 
2546     // Hide anything after a '##' string
2547     const char* text_display_end;
2548     if (hide_text_after_hash)
2549     {
2550         text_display_end = FindRenderedTextEnd(text, text_end);
2551     }
2552     else
2553     {
2554         if (!text_end)
2555             text_end = text + strlen(text); // FIXME-OPT
2556         text_display_end = text_end;
2557     }
2558 
2559     if (text != text_display_end)
2560     {
2561         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2562         if (g.LogEnabled)
2563             LogRenderedText(&pos, text, text_display_end);
2564     }
2565 }
2566 
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)2567 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2568 {
2569     ImGuiContext& g = *GImGui;
2570     ImGuiWindow* window = g.CurrentWindow;
2571 
2572     if (!text_end)
2573         text_end = text + strlen(text); // FIXME-OPT
2574 
2575     if (text != text_end)
2576     {
2577         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2578         if (g.LogEnabled)
2579             LogRenderedText(&pos, text, text_end);
2580     }
2581 }
2582 
2583 // Default clip_rect uses (pos_min,pos_max)
2584 // 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)2585 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)
2586 {
2587     // Perform CPU side clipping for single clipped element to avoid using scissor state
2588     ImVec2 pos = pos_min;
2589     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
2590 
2591     const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
2592     const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
2593     bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2594     if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
2595         need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2596 
2597     // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
2598     if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
2599     if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
2600 
2601     // Render
2602     if (need_clipping)
2603     {
2604         ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2605         draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2606     }
2607     else
2608     {
2609         draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2610     }
2611 }
2612 
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)2613 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)
2614 {
2615     // Hide anything after a '##' string
2616     const char* text_display_end = FindRenderedTextEnd(text, text_end);
2617     const int text_len = (int)(text_display_end - text);
2618     if (text_len == 0)
2619         return;
2620 
2621     ImGuiContext& g = *GImGui;
2622     ImGuiWindow* window = g.CurrentWindow;
2623     RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
2624     if (g.LogEnabled)
2625         LogRenderedText(&pos_min, text, text_display_end);
2626 }
2627 
2628 
2629 // Another overly complex function until we reorganize everything into a nice all-in-one helper.
2630 // 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.
2631 // 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)2632 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)
2633 {
2634     ImGuiContext& g = *GImGui;
2635     if (text_end_full == NULL)
2636         text_end_full = FindRenderedTextEnd(text);
2637     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);
2638 
2639     //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));
2640     //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));
2641     //draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255));
2642     // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
2643     if (text_size.x > pos_max.x - pos_min.x)
2644     {
2645         // Hello wo...
2646         // |       |   |
2647         // min   max   ellipsis_max
2648         //          <-> this is generally some padding value
2649 
2650         const ImFont* font = draw_list->_Data->Font;
2651         const float font_size = draw_list->_Data->FontSize;
2652         const char* text_end_ellipsis = NULL;
2653 
2654         ImWchar ellipsis_char = font->EllipsisChar;
2655         int ellipsis_char_count = 1;
2656         if (ellipsis_char == (ImWchar)-1)
2657         {
2658             ellipsis_char = (ImWchar)'.';
2659             ellipsis_char_count = 3;
2660         }
2661         const ImFontGlyph* glyph = font->FindGlyph(ellipsis_char);
2662 
2663         float ellipsis_glyph_width = glyph->X1;                 // Width of the glyph with no padding on either side
2664         float ellipsis_total_width = ellipsis_glyph_width;      // Full width of entire ellipsis
2665 
2666         if (ellipsis_char_count > 1)
2667         {
2668             // Full ellipsis size without free spacing after it.
2669             const float spacing_between_dots = 1.0f * (draw_list->_Data->FontSize / font->FontSize);
2670             ellipsis_glyph_width = glyph->X1 - glyph->X0 + spacing_between_dots;
2671             ellipsis_total_width = ellipsis_glyph_width * (float)ellipsis_char_count - spacing_between_dots;
2672         }
2673 
2674         // We can now claim the space between pos_max.x and ellipsis_max.x
2675         const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_total_width) - pos_min.x, 1.0f);
2676         float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
2677         if (text == text_end_ellipsis && text_end_ellipsis < text_end_full)
2678         {
2679             // Always display at least 1 character if there's no room for character + ellipsis
2680             text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full);
2681             text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x;
2682         }
2683         while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1]))
2684         {
2685             // 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)
2686             text_end_ellipsis--;
2687             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
2688         }
2689 
2690         // Render text, render ellipsis
2691         RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
2692         float ellipsis_x = pos_min.x + text_size_clipped_x;
2693         if (ellipsis_x + ellipsis_total_width <= ellipsis_max_x)
2694             for (int i = 0; i < ellipsis_char_count; i++)
2695             {
2696                 font->RenderChar(draw_list, font_size, ImVec2(ellipsis_x, pos_min.y), GetColorU32(ImGuiCol_Text), ellipsis_char);
2697                 ellipsis_x += ellipsis_glyph_width;
2698             }
2699     }
2700     else
2701     {
2702         RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));
2703     }
2704 
2705     if (g.LogEnabled)
2706         LogRenderedText(&pos_min, text, text_end_full);
2707 }
2708 
2709 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)2710 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2711 {
2712     ImGuiContext& g = *GImGui;
2713     ImGuiWindow* window = g.CurrentWindow;
2714     window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2715     const float border_size = g.Style.FrameBorderSize;
2716     if (border && border_size > 0.0f)
2717     {
2718         window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2719         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2720     }
2721 }
2722 
RenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding)2723 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
2724 {
2725     ImGuiContext& g = *GImGui;
2726     ImGuiWindow* window = g.CurrentWindow;
2727     const float border_size = g.Style.FrameBorderSize;
2728     if (border_size > 0.0f)
2729     {
2730         window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2731         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2732     }
2733 }
2734 
RenderNavHighlight(const ImRect & bb,ImGuiID id,ImGuiNavHighlightFlags flags)2735 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
2736 {
2737     ImGuiContext& g = *GImGui;
2738     if (id != g.NavId)
2739         return;
2740     if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
2741         return;
2742     ImGuiWindow* window = g.CurrentWindow;
2743     if (window->DC.NavHideHighlightOneFrame)
2744         return;
2745 
2746     float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
2747     ImRect display_rect = bb;
2748     display_rect.ClipWith(window->ClipRect);
2749     if (flags & ImGuiNavHighlightFlags_TypeDefault)
2750     {
2751         const float THICKNESS = 2.0f;
2752         const float DISTANCE = 3.0f + THICKNESS * 0.5f;
2753         display_rect.Expand(ImVec2(DISTANCE, DISTANCE));
2754         bool fully_visible = window->ClipRect.Contains(display_rect);
2755         if (!fully_visible)
2756             window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
2757         window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f), display_rect.Max - ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f), GetColorU32(ImGuiCol_NavHighlight), rounding, ImDrawCornerFlags_All, THICKNESS);
2758         if (!fully_visible)
2759             window->DrawList->PopClipRect();
2760     }
2761     if (flags & ImGuiNavHighlightFlags_TypeThin)
2762     {
2763         window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f);
2764     }
2765 }
2766 
2767 //-----------------------------------------------------------------------------
2768 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
2769 //-----------------------------------------------------------------------------
2770 
2771 // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
ImGuiWindow(ImGuiContext * context,const char * name)2772 ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
2773     : DrawListInst(&context->DrawListSharedData)
2774 {
2775     Name = ImStrdup(name);
2776     ID = ImHashStr(name);
2777     IDStack.push_back(ID);
2778     Flags = FlagsPreviousFrame = ImGuiWindowFlags_None;
2779     Viewport = NULL;
2780     ViewportId = 0;
2781     ViewportAllowPlatformMonitorExtend = -1;
2782     ViewportPos = ImVec2(FLT_MAX, FLT_MAX);
2783     Pos = ImVec2(0.0f, 0.0f);
2784     Size = SizeFull = ImVec2(0.0f, 0.0f);
2785     ContentSize = ContentSizeExplicit = ImVec2(0.0f, 0.0f);
2786     WindowPadding = ImVec2(0.0f, 0.0f);
2787     WindowRounding = 0.0f;
2788     WindowBorderSize = 0.0f;
2789     NameBufLen = (int)strlen(name) + 1;
2790     MoveId = GetID("#MOVE");
2791     ChildId = 0;
2792     Scroll = ImVec2(0.0f, 0.0f);
2793     ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
2794     ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
2795     ScrollbarSizes = ImVec2(0.0f, 0.0f);
2796     ScrollbarX = ScrollbarY = false;
2797     ViewportOwned = false;
2798     Active = WasActive = false;
2799     WriteAccessed = false;
2800     Collapsed = false;
2801     WantCollapseToggle = false;
2802     SkipItems = false;
2803     Appearing = false;
2804     Hidden = false;
2805     IsFallbackWindow = false;
2806     HasCloseButton = false;
2807     ResizeBorderHeld = -1;
2808     BeginCount = 0;
2809     BeginOrderWithinParent = -1;
2810     BeginOrderWithinContext = -1;
2811     PopupId = 0;
2812     AutoFitFramesX = AutoFitFramesY = -1;
2813     AutoFitChildAxises = 0x00;
2814     AutoFitOnlyGrows = false;
2815     AutoPosLastDirection = ImGuiDir_None;
2816     HiddenFramesCanSkipItems = HiddenFramesCannotSkipItems = 0;
2817     SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = SetWindowDockAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
2818     SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
2819 
2820     InnerRect = ImRect(0.0f, 0.0f, 0.0f, 0.0f); // Clear so the InnerRect.GetSize() code in Begin() doesn't lead to overflow even if the result isn't used.
2821 
2822     LastFrameActive = -1;
2823     LastFrameJustFocused = -1;
2824     LastTimeActive = -1.0f;
2825     ItemWidthDefault = 0.0f;
2826     FontWindowScale = FontDpiScale = 1.0f;
2827     SettingsOffset = -1;
2828 
2829     DrawList = &DrawListInst;
2830     DrawList->_OwnerName = Name;
2831     ParentWindow = NULL;
2832     RootWindow = NULL;
2833     RootWindowDockStop = NULL;
2834     RootWindowForTitleBarHighlight = NULL;
2835     RootWindowForNav = NULL;
2836 
2837     NavLastIds[0] = NavLastIds[1] = 0;
2838     NavRectRel[0] = NavRectRel[1] = ImRect();
2839     NavLastChildNavWindow = NULL;
2840 
2841     MemoryCompacted = false;
2842     MemoryDrawListIdxCapacity = MemoryDrawListVtxCapacity = 0;
2843 
2844     DockNode = DockNodeAsHost = NULL;
2845     DockId = 0;
2846     DockTabItemStatusFlags = ImGuiItemStatusFlags_None;
2847     DockOrder = -1;
2848     DockIsActive = DockTabIsVisible = DockTabWantClose = false;
2849 }
2850 
~ImGuiWindow()2851 ImGuiWindow::~ImGuiWindow()
2852 {
2853     IM_ASSERT(DrawList == &DrawListInst);
2854     IM_DELETE(Name);
2855     for (int i = 0; i != ColumnsStorage.Size; i++)
2856         ColumnsStorage[i].~ImGuiColumns();
2857 }
2858 
GetID(const char * str,const char * str_end)2859 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
2860 {
2861     ImGuiID seed = IDStack.back();
2862     ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2863     ImGui::KeepAliveID(id);
2864 #ifdef IMGUI_ENABLE_TEST_ENGINE
2865     ImGuiContext& g = *GImGui;
2866     IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
2867 #endif
2868     return id;
2869 }
2870 
GetID(const void * ptr)2871 ImGuiID ImGuiWindow::GetID(const void* ptr)
2872 {
2873     ImGuiID seed = IDStack.back();
2874     ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
2875     ImGui::KeepAliveID(id);
2876 #ifdef IMGUI_ENABLE_TEST_ENGINE
2877     ImGuiContext& g = *GImGui;
2878     IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr);
2879 #endif
2880     return id;
2881 }
2882 
GetID(int n)2883 ImGuiID ImGuiWindow::GetID(int n)
2884 {
2885     ImGuiID seed = IDStack.back();
2886     ImGuiID id = ImHashData(&n, sizeof(n), seed);
2887     ImGui::KeepAliveID(id);
2888 #ifdef IMGUI_ENABLE_TEST_ENGINE
2889     ImGuiContext& g = *GImGui;
2890     IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n);
2891 #endif
2892     return id;
2893 }
2894 
GetIDNoKeepAlive(const char * str,const char * str_end)2895 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
2896 {
2897     ImGuiID seed = IDStack.back();
2898     ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2899 #ifdef IMGUI_ENABLE_TEST_ENGINE
2900     ImGuiContext& g = *GImGui;
2901     IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
2902 #endif
2903     return id;
2904 }
2905 
GetIDNoKeepAlive(const void * ptr)2906 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
2907 {
2908     ImGuiID seed = IDStack.back();
2909     ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
2910 #ifdef IMGUI_ENABLE_TEST_ENGINE
2911     ImGuiContext& g = *GImGui;
2912     IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr);
2913 #endif
2914     return id;
2915 }
2916 
GetIDNoKeepAlive(int n)2917 ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n)
2918 {
2919     ImGuiID seed = IDStack.back();
2920     ImGuiID id = ImHashData(&n, sizeof(n), seed);
2921 #ifdef IMGUI_ENABLE_TEST_ENGINE
2922     ImGuiContext& g = *GImGui;
2923     IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n);
2924 #endif
2925     return id;
2926 }
2927 
2928 // This is only used in rare/specific situations to manufacture an ID out of nowhere.
GetIDFromRectangle(const ImRect & r_abs)2929 ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
2930 {
2931     ImGuiID seed = IDStack.back();
2932     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) };
2933     ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
2934     ImGui::KeepAliveID(id);
2935     return id;
2936 }
2937 
SetCurrentWindow(ImGuiWindow * window)2938 static void SetCurrentWindow(ImGuiWindow* window)
2939 {
2940     ImGuiContext& g = *GImGui;
2941     g.CurrentWindow = window;
2942     if (window)
2943         g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
2944 }
2945 
2946 // Free up/compact internal window buffers, we can use this when a window becomes unused.
2947 // This is currently unused by the library, but you may call this yourself for easy GC.
2948 // Not freed:
2949 // - ImGuiWindow, ImGuiWindowSettings, Name
2950 // - StateStorage, ColumnsStorage (may hold useful data)
2951 // This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.
GcCompactTransientWindowBuffers(ImGuiWindow * window)2952 void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window)
2953 {
2954     window->MemoryCompacted = true;
2955     window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;
2956     window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;
2957     window->IDStack.clear();
2958     window->DrawList->_ClearFreeMemory();
2959     window->DC.ChildWindows.clear();
2960     window->DC.ItemFlagsStack.clear();
2961     window->DC.ItemWidthStack.clear();
2962     window->DC.TextWrapPosStack.clear();
2963     window->DC.GroupStack.clear();
2964 }
2965 
GcAwakeTransientWindowBuffers(ImGuiWindow * window)2966 void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window)
2967 {
2968     // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.
2969     // The other buffers tends to amortize much faster.
2970     window->MemoryCompacted = false;
2971     window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity);
2972     window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity);
2973     window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0;
2974 }
2975 
SetActiveID(ImGuiID id,ImGuiWindow * window)2976 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
2977 {
2978     ImGuiContext& g = *GImGui;
2979     g.ActiveIdIsJustActivated = (g.ActiveId != id);
2980     if (g.ActiveIdIsJustActivated)
2981     {
2982         g.ActiveIdTimer = 0.0f;
2983         g.ActiveIdHasBeenPressedBefore = false;
2984         g.ActiveIdHasBeenEditedBefore = false;
2985         if (id != 0)
2986         {
2987             g.LastActiveId = id;
2988             g.LastActiveIdTimer = 0.0f;
2989         }
2990     }
2991     g.ActiveId = id;
2992     g.ActiveIdAllowOverlap = false;
2993     g.ActiveIdNoClearOnFocusLoss = false;
2994     g.ActiveIdWindow = window;
2995     g.ActiveIdHasBeenEditedThisFrame = false;
2996     if (id)
2997     {
2998         g.ActiveIdIsAlive = id;
2999         g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
3000     }
3001 
3002     // Clear declaration of inputs claimed by the widget
3003     // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
3004     g.ActiveIdUsingNavDirMask = 0x00;
3005     g.ActiveIdUsingNavInputMask = 0x00;
3006     g.ActiveIdUsingKeyInputMask = 0x00;
3007 }
3008 
ClearActiveID()3009 void ImGui::ClearActiveID()
3010 {
3011     SetActiveID(0, NULL); // g.ActiveId = 0;
3012 }
3013 
SetHoveredID(ImGuiID id)3014 void ImGui::SetHoveredID(ImGuiID id)
3015 {
3016     ImGuiContext& g = *GImGui;
3017     g.HoveredId = id;
3018     g.HoveredIdAllowOverlap = false;
3019     if (id != 0 && g.HoveredIdPreviousFrame != id)
3020         g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
3021 }
3022 
GetHoveredID()3023 ImGuiID ImGui::GetHoveredID()
3024 {
3025     ImGuiContext& g = *GImGui;
3026     return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
3027 }
3028 
KeepAliveID(ImGuiID id)3029 void ImGui::KeepAliveID(ImGuiID id)
3030 {
3031     ImGuiContext& g = *GImGui;
3032     if (g.ActiveId == id)
3033         g.ActiveIdIsAlive = id;
3034     if (g.ActiveIdPreviousFrame == id)
3035         g.ActiveIdPreviousFrameIsAlive = true;
3036 }
3037 
MarkItemEdited(ImGuiID id)3038 void ImGui::MarkItemEdited(ImGuiID id)
3039 {
3040     // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
3041     // 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.
3042     ImGuiContext& g = *GImGui;
3043     IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive);
3044     IM_UNUSED(id); // Avoid unused variable warnings when asserts are compiled out.
3045     //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
3046     g.ActiveIdHasBeenEditedThisFrame = true;
3047     g.ActiveIdHasBeenEditedBefore = true;
3048     g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
3049 }
3050 
IsWindowContentHoverable(ImGuiWindow * window,ImGuiHoveredFlags flags)3051 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
3052 {
3053     // An active popup disable hovering on other windows (apart from its own children)
3054     // FIXME-OPT: This could be cached/stored within the window.
3055     ImGuiContext& g = *GImGui;
3056     if (g.NavWindow)
3057         if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
3058             if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
3059             {
3060                 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
3061                 // NB: The order of those two tests is important because Modal windows are also Popups.
3062                 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
3063                     return false;
3064                 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
3065                     return false;
3066             }
3067 
3068     // Filter by viewport
3069     if (window->Viewport != g.MouseViewport)
3070         if (g.MovingWindow == NULL || window->RootWindow != g.MovingWindow->RootWindow)
3071             return false;
3072 
3073     return true;
3074 }
3075 
3076 // This is roughly matching the behavior of internal-facing ItemHoverable()
3077 // - 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()
3078 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
IsItemHovered(ImGuiHoveredFlags flags)3079 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
3080 {
3081     ImGuiContext& g = *GImGui;
3082     ImGuiWindow* window = g.CurrentWindow;
3083     if (g.NavDisableMouseHover && !g.NavDisableHighlight)
3084         return IsItemFocused();
3085 
3086     // Test for bounding box overlap, as updated as ItemAdd()
3087     if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
3088         return false;
3089     IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0);   // Flags not supported by this function
3090 
3091     // Test if we are hovering the right window (our window could be behind another window)
3092     // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable to use IsItemHovered() after EndChild() itself.
3093     // Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was the test that has been running for a long while.
3094     //if (g.HoveredWindow != window)
3095     //    return false;
3096     if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
3097         return false;
3098 
3099     // Test if another item is active (e.g. being dragged)
3100     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
3101         if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
3102             return false;
3103 
3104     // Test if interactions on this window are blocked by an active popup or modal.
3105     // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
3106     if (!IsWindowContentHoverable(window, flags))
3107         return false;
3108 
3109     // Test if the item is disabled
3110     if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
3111         return false;
3112 
3113     // Special handling for calling after Begin() which represent the title bar or tab.
3114     // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
3115     if ((window->DC.LastItemId == window->ID || window->DC.LastItemId == window->MoveId) && window->WriteAccessed)
3116         return false;
3117     return true;
3118 }
3119 
3120 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
ItemHoverable(const ImRect & bb,ImGuiID id)3121 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
3122 {
3123     ImGuiContext& g = *GImGui;
3124     if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
3125         return false;
3126 
3127     ImGuiWindow* window = g.CurrentWindow;
3128     if (g.HoveredWindow != window)
3129         return false;
3130     if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
3131         return false;
3132     if (!IsMouseHoveringRect(bb.Min, bb.Max))
3133         return false;
3134     if (g.NavDisableMouseHover)
3135         return false;
3136     if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_None) || (window->DC.ItemFlags & ImGuiItemFlags_Disabled))
3137     {
3138         g.HoveredIdDisabled = true;
3139         return false;
3140     }
3141 
3142     // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level
3143     // hover test in widgets code. We could also decide to split this function is two.
3144     if (id != 0)
3145     {
3146         SetHoveredID(id);
3147 
3148         // [DEBUG] Item Picker tool!
3149         // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making
3150         // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered
3151         // items if we perform the test in ItemAdd(), but that would incur a small runtime cost.
3152         // #define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX in imconfig.h if you want this check to also be performed in ItemAdd().
3153         if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
3154             GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
3155         if (g.DebugItemPickerBreakId == id)
3156             IM_DEBUG_BREAK();
3157     }
3158 
3159     return true;
3160 }
3161 
IsClippedEx(const ImRect & bb,ImGuiID id,bool clip_even_when_logged)3162 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
3163 {
3164     ImGuiContext& g = *GImGui;
3165     ImGuiWindow* window = g.CurrentWindow;
3166     if (!bb.Overlaps(window->ClipRect))
3167         if (id == 0 || (id != g.ActiveId && id != g.NavId))
3168             if (clip_even_when_logged || !g.LogEnabled)
3169                 return true;
3170     return false;
3171 }
3172 
3173 // This is also inlined in ItemAdd()
3174 // 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)3175 void ImGui::SetLastItemData(ImGuiWindow* window, ImGuiID item_id, ImGuiItemStatusFlags item_flags, const ImRect& item_rect)
3176 {
3177     window->DC.LastItemId = item_id;
3178     window->DC.LastItemStatusFlags = item_flags;
3179     window->DC.LastItemRect = item_rect;
3180 }
3181 
3182 // Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out.
FocusableItemRegister(ImGuiWindow * window,ImGuiID id)3183 bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id)
3184 {
3185     ImGuiContext& g = *GImGui;
3186 
3187     // Increment counters
3188     const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0;
3189     window->DC.FocusCounterRegular++;
3190     if (is_tab_stop)
3191         window->DC.FocusCounterTabStop++;
3192 
3193     // Process TAB/Shift-TAB to tab *OUT* of the currently focused item.
3194     // (Note that we can always TAB out of a widget that doesn't allow tabbing in)
3195     if (g.ActiveId == id && g.FocusTabPressed && !IsActiveIdUsingKey(ImGuiKey_Tab) && g.FocusRequestNextWindow == NULL)
3196     {
3197         g.FocusRequestNextWindow = window;
3198         g.FocusRequestNextCounterTabStop = window->DC.FocusCounterTabStop + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items.
3199     }
3200 
3201     // Handle focus requests
3202     if (g.FocusRequestCurrWindow == window)
3203     {
3204         if (window->DC.FocusCounterRegular == g.FocusRequestCurrCounterRegular)
3205             return true;
3206         if (is_tab_stop && window->DC.FocusCounterTabStop == g.FocusRequestCurrCounterTabStop)
3207         {
3208             g.NavJustTabbedId = id;
3209             return true;
3210         }
3211 
3212         // If another item is about to be focused, we clear our own active id
3213         if (g.ActiveId == id)
3214             ClearActiveID();
3215     }
3216 
3217     return false;
3218 }
3219 
FocusableItemUnregister(ImGuiWindow * window)3220 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
3221 {
3222     window->DC.FocusCounterRegular--;
3223     window->DC.FocusCounterTabStop--;
3224 }
3225 
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)3226 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
3227 {
3228     if (wrap_pos_x < 0.0f)
3229         return 0.0f;
3230 
3231     ImGuiContext& g = *GImGui;
3232     ImGuiWindow* window = g.CurrentWindow;
3233     if (wrap_pos_x == 0.0f)
3234     {
3235         // We could decide to setup a default wrapping max point for auto-resizing windows,
3236         // or have auto-wrap (with unspecified wrapping pos) behave as a ContentSize extending function?
3237         //if (window->Hidden && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize))
3238         //    wrap_pos_x = ImMax(window->WorkRect.Min.x + g.FontSize * 10.0f, window->WorkRect.Max.x);
3239         //else
3240         wrap_pos_x = window->WorkRect.Max.x;
3241     }
3242     else if (wrap_pos_x > 0.0f)
3243     {
3244         wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
3245     }
3246 
3247     return ImMax(wrap_pos_x - pos.x, 1.0f);
3248 }
3249 
3250 // IM_ALLOC() == ImGui::MemAlloc()
MemAlloc(size_t size)3251 void* ImGui::MemAlloc(size_t size)
3252 {
3253     if (ImGuiContext* ctx = GImGui)
3254         ctx->IO.MetricsActiveAllocations++;
3255     return GImAllocatorAllocFunc(size, GImAllocatorUserData);
3256 }
3257 
3258 // IM_FREE() == ImGui::MemFree()
MemFree(void * ptr)3259 void ImGui::MemFree(void* ptr)
3260 {
3261     if (ptr)
3262         if (ImGuiContext* ctx = GImGui)
3263             ctx->IO.MetricsActiveAllocations--;
3264     return GImAllocatorFreeFunc(ptr, GImAllocatorUserData);
3265 }
3266 
GetClipboardText()3267 const char* ImGui::GetClipboardText()
3268 {
3269     ImGuiContext& g = *GImGui;
3270     return g.IO.GetClipboardTextFn ? g.IO.GetClipboardTextFn(g.IO.ClipboardUserData) : "";
3271 }
3272 
SetClipboardText(const char * text)3273 void ImGui::SetClipboardText(const char* text)
3274 {
3275     ImGuiContext& g = *GImGui;
3276     if (g.IO.SetClipboardTextFn)
3277         g.IO.SetClipboardTextFn(g.IO.ClipboardUserData, text);
3278 }
3279 
GetVersion()3280 const char* ImGui::GetVersion()
3281 {
3282     return IMGUI_VERSION;
3283 }
3284 
3285 // Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself
3286 // 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()3287 ImGuiContext* ImGui::GetCurrentContext()
3288 {
3289     return GImGui;
3290 }
3291 
SetCurrentContext(ImGuiContext * ctx)3292 void ImGui::SetCurrentContext(ImGuiContext* ctx)
3293 {
3294 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
3295     IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
3296 #else
3297     GImGui = ctx;
3298 #endif
3299 }
3300 
SetAllocatorFunctions(void * (* alloc_func)(size_t sz,void * user_data),void (* free_func)(void * ptr,void * user_data),void * user_data)3301 void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data)
3302 {
3303     GImAllocatorAllocFunc = alloc_func;
3304     GImAllocatorFreeFunc = free_func;
3305     GImAllocatorUserData = user_data;
3306 }
3307 
CreateContext(ImFontAtlas * shared_font_atlas)3308 ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
3309 {
3310     ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
3311     if (GImGui == NULL)
3312         SetCurrentContext(ctx);
3313     Initialize(ctx);
3314     return ctx;
3315 }
3316 
DestroyContext(ImGuiContext * ctx)3317 void ImGui::DestroyContext(ImGuiContext* ctx)
3318 {
3319     if (ctx == NULL)
3320         ctx = GImGui;
3321     Shutdown(ctx);
3322     if (GImGui == ctx)
3323         SetCurrentContext(NULL);
3324     IM_DELETE(ctx);
3325 }
3326 
GetIO()3327 ImGuiIO& ImGui::GetIO()
3328 {
3329     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3330     return GImGui->IO;
3331 }
3332 
GetPlatformIO()3333 ImGuiPlatformIO& ImGui::GetPlatformIO()
3334 {
3335     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
3336     return GImGui->PlatformIO;
3337 }
3338 
3339 // Same value as passed to the old io.RenderDrawListsFn function. Valid after Render() and until the next call to NewFrame()
GetDrawData()3340 ImDrawData* ImGui::GetDrawData()
3341 {
3342     ImGuiContext& g = *GImGui;
3343     return g.Viewports[0]->DrawDataP.Valid ? &g.Viewports[0]->DrawDataP : NULL;
3344 }
3345 
GetTime()3346 double ImGui::GetTime()
3347 {
3348     return GImGui->Time;
3349 }
3350 
GetFrameCount()3351 int ImGui::GetFrameCount()
3352 {
3353     return GImGui->FrameCount;
3354 }
3355 
GetViewportDrawList(ImGuiViewportP * viewport,size_t drawlist_no,const char * drawlist_name)3356 static ImDrawList* GetViewportDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name)
3357 {
3358     // Create the draw list on demand, because they are not frequently used for all viewports
3359     ImGuiContext& g = *GImGui;
3360     IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->DrawLists));
3361     ImDrawList* draw_list = viewport->DrawLists[drawlist_no];
3362     if (draw_list == NULL)
3363     {
3364         draw_list = IM_NEW(ImDrawList)(&g.DrawListSharedData);
3365         draw_list->_OwnerName = drawlist_name;
3366         viewport->DrawLists[drawlist_no] = draw_list;
3367     }
3368 
3369     // Our ImDrawList system requires that there is always a command
3370     if (viewport->LastFrameDrawLists[drawlist_no] != g.FrameCount)
3371     {
3372         draw_list->_ResetForNewFrame();
3373         draw_list->PushTextureID(g.IO.Fonts->TexID);
3374         draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false);
3375         viewport->LastFrameDrawLists[drawlist_no] = g.FrameCount;
3376     }
3377     return draw_list;
3378 }
3379 
GetBackgroundDrawList(ImGuiViewport * viewport)3380 ImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport)
3381 {
3382     return GetViewportDrawList((ImGuiViewportP*)viewport, 0, "##Background");
3383 }
3384 
GetBackgroundDrawList()3385 ImDrawList* ImGui::GetBackgroundDrawList()
3386 {
3387     ImGuiWindow* window = GImGui->CurrentWindow;
3388     return GetBackgroundDrawList(window->Viewport);
3389 }
3390 
GetForegroundDrawList(ImGuiViewport * viewport)3391 ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport)
3392 {
3393     return GetViewportDrawList((ImGuiViewportP*)viewport, 1, "##Foreground");
3394 }
3395 
GetForegroundDrawList()3396 ImDrawList* ImGui::GetForegroundDrawList()
3397 {
3398     ImGuiWindow* window = GImGui->CurrentWindow;
3399     return GetForegroundDrawList(window->Viewport);
3400 }
3401 
GetDrawListSharedData()3402 ImDrawListSharedData* ImGui::GetDrawListSharedData()
3403 {
3404     return &GImGui->DrawListSharedData;
3405 }
3406 
StartMouseMovingWindow(ImGuiWindow * window)3407 void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
3408 {
3409     // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
3410     // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
3411     // This is because we want ActiveId to be set even when the window is not permitted to move.
3412     ImGuiContext& g = *GImGui;
3413     FocusWindow(window);
3414     SetActiveID(window->MoveId, window);
3415     g.NavDisableHighlight = true;
3416     g.ActiveIdNoClearOnFocusLoss = true;
3417     g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos;
3418 
3419     bool can_move_window = true;
3420     if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
3421         can_move_window = false;
3422     if (ImGuiDockNode* node = window->DockNodeAsHost)
3423         if (node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove))
3424             can_move_window = false;
3425     if (can_move_window)
3426         g.MovingWindow = window;
3427 }
3428 
3429 // We use 'undock_floating_node == false' when dragging from title bar to allow moving groups of floating nodes without undocking them.
3430 // - undock_floating_node == true: when dragging from a floating node within a hierarchy, always undock the node.
3431 // - 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)3432 void ImGui::StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* node, bool undock_floating_node)
3433 {
3434     ImGuiContext& g = *GImGui;
3435     bool can_undock_node = false;
3436     if (node != NULL && node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove) == 0)
3437     {
3438         // Can undock if:
3439         // - part of a floating node hierarchy with more than one visible node (if only one is visible, we'll just move the whole hierarchy)
3440         // - part of a dockspace node hierarchy (trivia: undocking from a fixed/central node will create a new node and copy windows)
3441         ImGuiDockNode* root_node = DockNodeGetRootNode(node);
3442         if (root_node->OnlyNodeWithWindows != node || root_node->CentralNode != NULL)   // -V1051 PVS-Studio thinks node should be root_node and is wrong about that.
3443             if (undock_floating_node || root_node->IsDockSpace())
3444                 can_undock_node = true;
3445     }
3446 
3447     const bool clicked = IsMouseClicked(0);
3448     const bool dragging = IsMouseDragging(0, g.IO.MouseDragThreshold * 1.70f);
3449     if (can_undock_node && dragging)
3450         DockContextQueueUndockNode(&g, node); // Will lead to DockNodeStartMouseMovingWindow() -> StartMouseMovingWindow() being called next frame
3451     else if (!can_undock_node && (clicked || dragging) && g.MovingWindow != window)
3452         StartMouseMovingWindow(window);
3453 }
3454 
3455 // Handle mouse moving window
3456 // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
3457 // FIXME: We don't have strong guarantee that g.MovingWindow stay synched with g.ActiveId == g.MovingWindow->MoveId.
3458 // This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs,
3459 // but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other.
UpdateMouseMovingWindowNewFrame()3460 void ImGui::UpdateMouseMovingWindowNewFrame()
3461 {
3462     ImGuiContext& g = *GImGui;
3463     if (g.MovingWindow != NULL)
3464     {
3465         // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
3466         // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
3467         KeepAliveID(g.ActiveId);
3468         IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
3469         ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
3470         if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
3471         {
3472             ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
3473             if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
3474             {
3475                 MarkIniSettingsDirty(moving_window);
3476                 SetWindowPos(moving_window, pos, ImGuiCond_Always);
3477                 if (moving_window->ViewportOwned) // Synchronize viewport immediately because some overlays may relies on clipping rectangle before we Begin() into the window.
3478                     moving_window->Viewport->Pos = pos;
3479             }
3480             FocusWindow(g.MovingWindow);
3481         }
3482         else
3483         {
3484             // Try to merge the window back into the main viewport.
3485             // This works because MouseViewport should be != MovingWindow->Viewport on release (as per code in UpdateViewports)
3486             if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)
3487                 UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseViewport);
3488 
3489             // Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button.
3490             if (!IsDragDropPayloadBeingAccepted())
3491                 g.MouseViewport = moving_window->Viewport;
3492 
3493             // Clear the NoInput window flag set by the Viewport system
3494             moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs;
3495 
3496             ClearActiveID();
3497             g.MovingWindow = NULL;
3498         }
3499     }
3500     else
3501     {
3502         // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
3503         if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
3504         {
3505             KeepAliveID(g.ActiveId);
3506             if (!g.IO.MouseDown[0])
3507                 ClearActiveID();
3508         }
3509     }
3510 }
3511 
3512 // Initiate moving window when clicking on empty space or title bar.
3513 // Handle left-click and right-click focus.
UpdateMouseMovingWindowEndFrame()3514 void ImGui::UpdateMouseMovingWindowEndFrame()
3515 {
3516     ImGuiContext& g = *GImGui;
3517     if (g.ActiveId != 0 || g.HoveredId != 0)
3518         return;
3519 
3520     // Unless we just made a window/popup appear
3521     if (g.NavWindow && g.NavWindow->Appearing)
3522         return;
3523 
3524     // Click on void to focus window and start moving
3525     // (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!)
3526     if (g.IO.MouseClicked[0])
3527     {
3528         // Handle the edge case of a popup being closed while clicking in its empty space.
3529         // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more.
3530         ImGuiWindow* root_window = g.HoveredWindow ? g.HoveredWindow->RootWindowDockStop : NULL;
3531         const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(root_window->PopupId, ImGuiPopupFlags_AnyPopupLevel);
3532 
3533         if (root_window != NULL && !is_closed_popup)
3534         {
3535             StartMouseMovingWindow(g.HoveredWindow);
3536 
3537             // Cancel moving if clicked outside of title bar
3538             if (g.IO.ConfigWindowsMoveFromTitleBarOnly)
3539                 if (!(root_window->Flags & ImGuiWindowFlags_NoTitleBar) || root_window->DockIsActive)
3540                     if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
3541                         g.MovingWindow = NULL;
3542 
3543             // Cancel moving if clicked over an item which was disabled or inhibited by popups (note that we know HoveredId == 0 already)
3544             if (g.HoveredIdDisabled)
3545                 g.MovingWindow = NULL;
3546         }
3547         else if (root_window == NULL && g.NavWindow != NULL && GetTopMostPopupModal() == NULL)
3548         {
3549             // Clicking on void disable focus
3550             FocusWindow(NULL);
3551         }
3552     }
3553 
3554     // With right mouse button we close popups without changing focus based on where the mouse is aimed
3555     // Instead, focus will be restored to the window under the bottom-most closed popup.
3556     // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
3557     if (g.IO.MouseClicked[1])
3558     {
3559         // Find the top-most window between HoveredWindow and the top-most Modal Window.
3560         // This is where we can trim the popup stack.
3561         ImGuiWindow* modal = GetTopMostPopupModal();
3562         bool hovered_window_above_modal = false;
3563         if (modal == NULL)
3564             hovered_window_above_modal = true;
3565         for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--)
3566         {
3567             ImGuiWindow* window = g.Windows[i];
3568             if (window == modal)
3569                 break;
3570             if (window == g.HoveredWindow)
3571                 hovered_window_above_modal = true;
3572         }
3573         ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true);
3574     }
3575 }
3576 
TranslateWindow(ImGuiWindow * window,const ImVec2 & delta)3577 static void TranslateWindow(ImGuiWindow* window, const ImVec2& delta)
3578 {
3579     window->Pos += delta;
3580     window->ClipRect.Translate(delta);
3581     window->OuterRectClipped.Translate(delta);
3582     window->InnerRect.Translate(delta);
3583     window->DC.CursorPos += delta;
3584     window->DC.CursorStartPos += delta;
3585     window->DC.CursorMaxPos += delta;
3586     window->DC.LastItemRect.Translate(delta);
3587     window->DC.LastItemDisplayRect.Translate(delta);
3588 }
3589 
ScaleWindow(ImGuiWindow * window,float scale)3590 static void ScaleWindow(ImGuiWindow* window, float scale)
3591 {
3592     ImVec2 origin = window->Viewport->Pos;
3593     window->Pos = ImFloor((window->Pos - origin) * scale + origin);
3594     window->Size = ImFloor(window->Size * scale);
3595     window->SizeFull = ImFloor(window->SizeFull * scale);
3596     window->ContentSize = ImFloor(window->ContentSize * scale);
3597 }
3598 
IsWindowActiveAndVisible(ImGuiWindow * window)3599 static bool IsWindowActiveAndVisible(ImGuiWindow* window)
3600 {
3601     return (window->Active) && (!window->Hidden);
3602 }
3603 
UpdateMouseInputs()3604 static void ImGui::UpdateMouseInputs()
3605 {
3606     ImGuiContext& g = *GImGui;
3607 
3608     // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
3609     if (IsMousePosValid(&g.IO.MousePos))
3610         g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos);
3611 
3612     // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
3613     if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
3614         g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
3615     else
3616         g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
3617     if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
3618         g.NavDisableMouseHover = false;
3619 
3620     g.IO.MousePosPrev = g.IO.MousePos;
3621     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3622     {
3623         g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
3624         g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
3625         g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
3626         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;
3627         g.IO.MouseDoubleClicked[i] = false;
3628         if (g.IO.MouseClicked[i])
3629         {
3630             if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime)
3631             {
3632                 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3633                 if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
3634                     g.IO.MouseDoubleClicked[i] = true;
3635                 g.IO.MouseClickedTime[i] = -g.IO.MouseDoubleClickTime * 2.0f; // Mark as "old enough" so the third click isn't turned into a double-click
3636             }
3637             else
3638             {
3639                 g.IO.MouseClickedTime[i] = g.Time;
3640             }
3641             g.IO.MouseClickedPos[i] = g.IO.MousePos;
3642             g.IO.MouseDownWasDoubleClick[i] = g.IO.MouseDoubleClicked[i];
3643             g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
3644             g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
3645         }
3646         else if (g.IO.MouseDown[i])
3647         {
3648             // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
3649             ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3650             g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
3651             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);
3652             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);
3653         }
3654         if (!g.IO.MouseDown[i] && !g.IO.MouseReleased[i])
3655             g.IO.MouseDownWasDoubleClick[i] = false;
3656         if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
3657             g.NavDisableMouseHover = false;
3658     }
3659 }
3660 
StartLockWheelingWindow(ImGuiWindow * window)3661 static void StartLockWheelingWindow(ImGuiWindow* window)
3662 {
3663     ImGuiContext& g = *GImGui;
3664     if (g.WheelingWindow == window)
3665         return;
3666     g.WheelingWindow = window;
3667     g.WheelingWindowRefMousePos = g.IO.MousePos;
3668     g.WheelingWindowTimer = WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER;
3669 }
3670 
UpdateMouseWheel()3671 void ImGui::UpdateMouseWheel()
3672 {
3673     ImGuiContext& g = *GImGui;
3674 
3675     // Reset the locked window if we move the mouse or after the timer elapses
3676     if (g.WheelingWindow != NULL)
3677     {
3678         g.WheelingWindowTimer -= g.IO.DeltaTime;
3679         if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
3680             g.WheelingWindowTimer = 0.0f;
3681         if (g.WheelingWindowTimer <= 0.0f)
3682         {
3683             g.WheelingWindow = NULL;
3684             g.WheelingWindowTimer = 0.0f;
3685         }
3686     }
3687 
3688     if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f)
3689         return;
3690 
3691     ImGuiWindow* window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
3692     if (!window || window->Collapsed)
3693         return;
3694 
3695     // Zoom / Scale window
3696     // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
3697     if (g.IO.MouseWheel != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
3698     {
3699         StartLockWheelingWindow(window);
3700         const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
3701         const float scale = new_font_scale / window->FontWindowScale;
3702         window->FontWindowScale = new_font_scale;
3703         if (!(window->Flags & ImGuiWindowFlags_ChildWindow))
3704         {
3705             const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
3706             SetWindowPos(window, window->Pos + offset, 0);
3707             window->Size = ImFloor(window->Size * scale);
3708             window->SizeFull = ImFloor(window->SizeFull * scale);
3709         }
3710         return;
3711     }
3712 
3713     // Mouse wheel scrolling
3714     // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent
3715 
3716     // Vertical Mouse Wheel scrolling
3717     const float wheel_y = (g.IO.MouseWheel != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f;
3718     if (wheel_y != 0.0f && !g.IO.KeyCtrl)
3719     {
3720         StartLockWheelingWindow(window);
3721         while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3722             window = window->ParentWindow;
3723         if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3724         {
3725             float max_step = window->InnerRect.GetHeight() * 0.67f;
3726             float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step));
3727             SetScrollY(window, window->Scroll.y - wheel_y * scroll_step);
3728         }
3729     }
3730 
3731     // Horizontal Mouse Wheel scrolling, or Vertical Mouse Wheel w/ Shift held
3732     const float wheel_x = (g.IO.MouseWheelH != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheelH : (g.IO.MouseWheel != 0.0f && g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f;
3733     if (wheel_x != 0.0f && !g.IO.KeyCtrl)
3734     {
3735         StartLockWheelingWindow(window);
3736         while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.x == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3737             window = window->ParentWindow;
3738         if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3739         {
3740             float max_step = window->InnerRect.GetWidth() * 0.67f;
3741             float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step));
3742             SetScrollX(window, window->Scroll.x - wheel_x * scroll_step);
3743         }
3744     }
3745 }
3746 
UpdateTabFocus()3747 void ImGui::UpdateTabFocus()
3748 {
3749     ImGuiContext& g = *GImGui;
3750 
3751     // Pressing TAB activate widget focus
3752     g.FocusTabPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab));
3753     if (g.ActiveId == 0 && g.FocusTabPressed)
3754     {
3755         // Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also
3756         // manipulate the Next fields even, even though they will be turned into Curr fields by the code below.
3757         g.FocusRequestNextWindow = g.NavWindow;
3758         g.FocusRequestNextCounterRegular = INT_MAX;
3759         if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
3760             g.FocusRequestNextCounterTabStop = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1);
3761         else
3762             g.FocusRequestNextCounterTabStop = g.IO.KeyShift ? -1 : 0;
3763     }
3764 
3765     // Turn queued focus request into current one
3766     g.FocusRequestCurrWindow = NULL;
3767     g.FocusRequestCurrCounterRegular = g.FocusRequestCurrCounterTabStop = INT_MAX;
3768     if (g.FocusRequestNextWindow != NULL)
3769     {
3770         ImGuiWindow* window = g.FocusRequestNextWindow;
3771         g.FocusRequestCurrWindow = window;
3772         if (g.FocusRequestNextCounterRegular != INT_MAX && window->DC.FocusCounterRegular != -1)
3773             g.FocusRequestCurrCounterRegular = ImModPositive(g.FocusRequestNextCounterRegular, window->DC.FocusCounterRegular + 1);
3774         if (g.FocusRequestNextCounterTabStop != INT_MAX && window->DC.FocusCounterTabStop != -1)
3775             g.FocusRequestCurrCounterTabStop = ImModPositive(g.FocusRequestNextCounterTabStop, window->DC.FocusCounterTabStop + 1);
3776         g.FocusRequestNextWindow = NULL;
3777         g.FocusRequestNextCounterRegular = g.FocusRequestNextCounterTabStop = INT_MAX;
3778     }
3779 
3780     g.NavIdTabCounter = INT_MAX;
3781 }
3782 
3783 // 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()3784 void ImGui::UpdateHoveredWindowAndCaptureFlags()
3785 {
3786     ImGuiContext& g = *GImGui;
3787 
3788     // Find the window hovered by mouse:
3789     // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3790     // - 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.
3791     // - 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.
3792     bool clear_hovered_windows = false;
3793     FindHoveredWindow();
3794     IM_ASSERT(g.HoveredWindow == NULL || g.HoveredWindow == g.MovingWindow || g.HoveredWindow->Viewport == g.MouseViewport);
3795 
3796     // Modal windows prevents mouse from hovering behind them.
3797     ImGuiWindow* modal_window = GetTopMostPopupModal();
3798     if (modal_window && g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window))
3799         clear_hovered_windows = true;
3800 
3801     // Disabled mouse?
3802     if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
3803         clear_hovered_windows = true;
3804 
3805     // 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.
3806     int mouse_earliest_button_down = -1;
3807     bool mouse_any_down = false;
3808     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3809     {
3810         if (g.IO.MouseClicked[i])
3811             g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (g.OpenPopupStack.Size > 0);
3812         mouse_any_down |= g.IO.MouseDown[i];
3813         if (g.IO.MouseDown[i])
3814             if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
3815                 mouse_earliest_button_down = i;
3816     }
3817     const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
3818 
3819     // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
3820     // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
3821     const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
3822     if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
3823         clear_hovered_windows = true;
3824 
3825     if (clear_hovered_windows)
3826         g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL;
3827 
3828     // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app)
3829     if (g.WantCaptureMouseNextFrame != -1)
3830         g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
3831     else
3832         g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.OpenPopupStack.Size > 0);
3833 
3834     // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app)
3835     if (g.WantCaptureKeyboardNextFrame != -1)
3836         g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
3837     else
3838         g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
3839     if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
3840         g.IO.WantCaptureKeyboard = true;
3841 
3842     // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
3843     g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
3844 }
3845 
GetMergedKeyModFlags()3846 ImGuiKeyModFlags ImGui::GetMergedKeyModFlags()
3847 {
3848     ImGuiContext& g = *GImGui;
3849     ImGuiKeyModFlags key_mod_flags = ImGuiKeyModFlags_None;
3850     if (g.IO.KeyCtrl)   { key_mod_flags |= ImGuiKeyModFlags_Ctrl; }
3851     if (g.IO.KeyShift)  { key_mod_flags |= ImGuiKeyModFlags_Shift; }
3852     if (g.IO.KeyAlt)    { key_mod_flags |= ImGuiKeyModFlags_Alt; }
3853     if (g.IO.KeySuper)  { key_mod_flags |= ImGuiKeyModFlags_Super; }
3854     return key_mod_flags;
3855 }
3856 
NewFrame()3857 void ImGui::NewFrame()
3858 {
3859     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3860     ImGuiContext& g = *GImGui;
3861 
3862 #ifdef IMGUI_ENABLE_TEST_ENGINE
3863     ImGuiTestEngineHook_PreNewFrame(&g);
3864 #endif
3865 
3866     // Check and assert for various common IO and Configuration mistakes
3867     g.ConfigFlagsLastFrame = g.ConfigFlagsCurrFrame;
3868     ErrorCheckNewFrameSanityChecks();
3869     g.ConfigFlagsCurrFrame = g.IO.ConfigFlags;
3870 
3871     // Load settings on first frame, save settings when modified (after a delay)
3872     UpdateSettings();
3873 
3874     g.Time += g.IO.DeltaTime;
3875     g.WithinFrameScope = true;
3876     g.FrameCount += 1;
3877     g.TooltipOverrideCount = 0;
3878     g.WindowsActiveCount = 0;
3879     g.MenusIdSubmittedThisFrame.resize(0);
3880 
3881     // Calculate frame-rate for the user, as a purely luxurious feature
3882     g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
3883     g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
3884     g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
3885     g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX;
3886 
3887     UpdateViewportsNewFrame();
3888 
3889     // Setup current font and draw list shared data
3890     // FIXME-VIEWPORT: the concept of a single ClipRectFullscreen is not ideal!
3891     g.IO.Fonts->Locked = true;
3892     SetCurrentFont(GetDefaultFont());
3893     IM_ASSERT(g.Font->IsLoaded());
3894     ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
3895     for (int n = 0; n < g.Viewports.Size; n++)
3896         virtual_space.Add(g.Viewports[n]->GetMainRect());
3897     g.DrawListSharedData.ClipRectFullscreen = ImVec4(virtual_space.Min.x, virtual_space.Min.y, virtual_space.Max.x, virtual_space.Max.y);
3898     g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
3899     g.DrawListSharedData.SetCircleSegmentMaxError(g.Style.CircleSegmentMaxError);
3900     g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
3901     if (g.Style.AntiAliasedLines)
3902         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
3903     if (g.Style.AntiAliasedLinesUseTex && !(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines))
3904         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex;
3905     if (g.Style.AntiAliasedFill)
3906         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
3907     if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
3908         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
3909 
3910     // Mark rendering data as invalid to prevent user who may have a handle on it to use it.
3911     for (int n = 0; n < g.Viewports.Size; n++)
3912     {
3913         ImGuiViewportP* viewport = g.Viewports[n];
3914         viewport->DrawData = NULL;
3915         viewport->DrawDataP.Clear();
3916     }
3917 
3918     // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
3919     if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
3920         KeepAliveID(g.DragDropPayload.SourceId);
3921 
3922     // Update HoveredId data
3923     if (!g.HoveredIdPreviousFrame)
3924         g.HoveredIdTimer = 0.0f;
3925     if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
3926         g.HoveredIdNotActiveTimer = 0.0f;
3927     if (g.HoveredId)
3928         g.HoveredIdTimer += g.IO.DeltaTime;
3929     if (g.HoveredId && g.ActiveId != g.HoveredId)
3930         g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
3931     g.HoveredIdPreviousFrame = g.HoveredId;
3932     g.HoveredId = 0;
3933     g.HoveredIdAllowOverlap = false;
3934     g.HoveredIdDisabled = false;
3935 
3936     // Update ActiveId data (clear reference to active widget if the widget isn't alive anymore)
3937     if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
3938         ClearActiveID();
3939     if (g.ActiveId)
3940         g.ActiveIdTimer += g.IO.DeltaTime;
3941     g.LastActiveIdTimer += g.IO.DeltaTime;
3942     g.ActiveIdPreviousFrame = g.ActiveId;
3943     g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
3944     g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore;
3945     g.ActiveIdIsAlive = 0;
3946     g.ActiveIdHasBeenEditedThisFrame = false;
3947     g.ActiveIdPreviousFrameIsAlive = false;
3948     g.ActiveIdIsJustActivated = false;
3949     if (g.TempInputId != 0 && g.ActiveId != g.TempInputId)
3950         g.TempInputId = 0;
3951     if (g.ActiveId == 0)
3952     {
3953         g.ActiveIdUsingNavDirMask = 0x00;
3954         g.ActiveIdUsingNavInputMask = 0x00;
3955         g.ActiveIdUsingKeyInputMask = 0x00;
3956     }
3957 
3958     // Drag and drop
3959     g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
3960     g.DragDropAcceptIdCurr = 0;
3961     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
3962     g.DragDropWithinSource = false;
3963     g.DragDropWithinTarget = false;
3964     g.DragDropHoldJustPressedId = 0;
3965 
3966     // Update keyboard input state
3967     // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools
3968     g.IO.KeyMods = GetMergedKeyModFlags();
3969     memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
3970     for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
3971         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;
3972 
3973     // Update gamepad/keyboard navigation
3974     NavUpdate();
3975 
3976     // Update mouse input state
3977     UpdateMouseInputs();
3978 
3979     // Undocking
3980     // (needs to be before UpdateMouseMovingWindowNewFrame so the window is already offset and following the mouse on the detaching frame)
3981     DockContextUpdateUndocking(&g);
3982 
3983     // Find hovered window
3984     // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
3985     UpdateHoveredWindowAndCaptureFlags();
3986 
3987     // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
3988     UpdateMouseMovingWindowNewFrame();
3989 
3990     // Background darkening/whitening
3991     if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
3992         g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
3993     else
3994         g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
3995 
3996     g.MouseCursor = ImGuiMouseCursor_Arrow;
3997     g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
3998     g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
3999     g.PlatformImePosViewport = NULL;
4000 
4001     // Mouse wheel scrolling, scale
4002     UpdateMouseWheel();
4003 
4004     // Update legacy TAB focus
4005     UpdateTabFocus();
4006 
4007     // Mark all windows as not visible and compact unused memory.
4008     IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size);
4009     const float memory_compact_start_time = (g.IO.ConfigWindowsMemoryCompactTimer >= 0.0f) ? (float)g.Time - g.IO.ConfigWindowsMemoryCompactTimer : FLT_MAX;
4010     for (int i = 0; i != g.Windows.Size; i++)
4011     {
4012         ImGuiWindow* window = g.Windows[i];
4013         window->WasActive = window->Active;
4014         window->BeginCount = 0;
4015         window->Active = false;
4016         window->WriteAccessed = false;
4017 
4018         // Garbage collect transient buffers of recently unused windows
4019         if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
4020             GcCompactTransientWindowBuffers(window);
4021     }
4022 
4023     // Closing the focused window restore focus to the first active root window in descending z-order
4024     if (g.NavWindow && !g.NavWindow->WasActive)
4025         FocusTopMostWindowUnderOne(NULL, NULL);
4026 
4027     // No window should be open at the beginning of the frame.
4028     // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
4029     g.CurrentWindowStack.resize(0);
4030     g.BeginPopupStack.resize(0);
4031     ClosePopupsOverWindow(g.NavWindow, false);
4032 
4033     // Docking
4034     DockContextUpdateDocking(&g);
4035 
4036     // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
4037     UpdateDebugToolItemPicker();
4038 
4039     // Create implicit/fallback window - which we will only render it if the user has added something to it.
4040     // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
4041     // This fallback is particularly important as it avoid ImGui:: calls from crashing.
4042     g.WithinFrameScopeWithImplicitWindow = true;
4043     SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
4044     Begin("Debug##Default");
4045     IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true);
4046 
4047 #ifdef IMGUI_ENABLE_TEST_ENGINE
4048     ImGuiTestEngineHook_PostNewFrame(&g);
4049 #endif
4050 }
4051 
4052 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
UpdateDebugToolItemPicker()4053 void ImGui::UpdateDebugToolItemPicker()
4054 {
4055     ImGuiContext& g = *GImGui;
4056     g.DebugItemPickerBreakId = 0;
4057     if (g.DebugItemPickerActive)
4058     {
4059         const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
4060         ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
4061         if (ImGui::IsKeyPressedMap(ImGuiKey_Escape))
4062             g.DebugItemPickerActive = false;
4063         if (ImGui::IsMouseClicked(0) && hovered_id)
4064         {
4065             g.DebugItemPickerBreakId = hovered_id;
4066             g.DebugItemPickerActive = false;
4067         }
4068         ImGui::SetNextWindowBgAlpha(0.60f);
4069         ImGui::BeginTooltip();
4070         ImGui::Text("HoveredId: 0x%08X", hovered_id);
4071         ImGui::Text("Press ESC to abort picking.");
4072         ImGui::TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!");
4073         ImGui::EndTooltip();
4074     }
4075 }
4076 
Initialize(ImGuiContext * context)4077 void ImGui::Initialize(ImGuiContext* context)
4078 {
4079     ImGuiContext& g = *context;
4080     IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
4081 
4082     // Add .ini handle for ImGuiWindow type
4083     {
4084         ImGuiSettingsHandler ini_handler;
4085         ini_handler.TypeName = "Window";
4086         ini_handler.TypeHash = ImHashStr("Window");
4087         ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll;
4088         ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen;
4089         ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;
4090         ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll;
4091         ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll;
4092         g.SettingsHandlers.push_back(ini_handler);
4093     }
4094 
4095 #ifdef IMGUI_HAS_TABLE
4096     // Add .ini handle for ImGuiTable type
4097     {
4098         ImGuiSettingsHandler ini_handler;
4099         ini_handler.TypeName = "Table";
4100         ini_handler.TypeHash = ImHashStr("Table");
4101         ini_handler.ReadOpenFn = TableSettingsHandler_ReadOpen;
4102         ini_handler.ReadLineFn = TableSettingsHandler_ReadLine;
4103         ini_handler.WriteAllFn = TableSettingsHandler_WriteAll;
4104         g.SettingsHandlers.push_back(ini_handler);
4105     }
4106 #endif // #ifdef IMGUI_HAS_TABLE
4107 
4108 #ifdef IMGUI_HAS_DOCK
4109     // Create default viewport
4110     ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)();
4111     viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID;
4112     viewport->Idx = 0;
4113     viewport->PlatformWindowCreated = true;
4114     g.Viewports.push_back(viewport);
4115     g.PlatformIO.MainViewport = g.Viewports[0]; // Make it accessible in public-facing GetPlatformIO() immediately (before the first call to EndFrame)
4116     g.PlatformIO.Viewports.push_back(g.Viewports[0]);
4117 
4118     // Extensions
4119     DockContextInitialize(&g);
4120 #endif // #ifdef IMGUI_HAS_DOCK
4121 
4122     g.Initialized = true;
4123 }
4124 
4125 // This function is merely here to free heap allocations.
Shutdown(ImGuiContext * context)4126 void ImGui::Shutdown(ImGuiContext* context)
4127 {
4128     // 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)
4129     ImGuiContext& g = *context;
4130     if (g.IO.Fonts && g.FontAtlasOwnedByContext)
4131     {
4132         g.IO.Fonts->Locked = false;
4133         IM_DELETE(g.IO.Fonts);
4134     }
4135     g.IO.Fonts = NULL;
4136 
4137     // Cleanup of other data are conditional on actually having initialized Dear ImGui.
4138     if (!g.Initialized)
4139         return;
4140 
4141     // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
4142     if (g.SettingsLoaded && g.IO.IniFilename != NULL)
4143     {
4144         ImGuiContext* backup_context = GImGui;
4145         SetCurrentContext(context);
4146         SaveIniSettingsToDisk(g.IO.IniFilename);
4147         SetCurrentContext(backup_context);
4148     }
4149 
4150     // Destroy platform windows
4151     ImGuiContext* backup_context = ImGui::GetCurrentContext();
4152     SetCurrentContext(context);
4153     DestroyPlatformWindows();
4154     SetCurrentContext(backup_context);
4155 
4156     // Shutdown extensions
4157     DockContextShutdown(&g);
4158 
4159     // Notify hooked test engine, if any
4160 #ifdef IMGUI_ENABLE_TEST_ENGINE
4161     ImGuiTestEngineHook_Shutdown(context);
4162 #endif
4163 
4164     // Clear everything else
4165     for (int i = 0; i < g.Windows.Size; i++)
4166         IM_DELETE(g.Windows[i]);
4167     g.Windows.clear();
4168     g.WindowsFocusOrder.clear();
4169     g.WindowsTempSortBuffer.clear();
4170     g.CurrentWindow = NULL;
4171     g.CurrentWindowStack.clear();
4172     g.WindowsById.Clear();
4173     g.NavWindow = NULL;
4174     g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL;
4175     g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
4176     g.MovingWindow = NULL;
4177     g.ColorModifiers.clear();
4178     g.StyleModifiers.clear();
4179     g.FontStack.clear();
4180     g.OpenPopupStack.clear();
4181     g.BeginPopupStack.clear();
4182 
4183     g.CurrentViewport = g.MouseViewport = g.MouseLastHoveredViewport = NULL;
4184     for (int i = 0; i < g.Viewports.Size; i++)
4185         IM_DELETE(g.Viewports[i]);
4186     g.Viewports.clear();
4187 
4188     g.TabBars.Clear();
4189     g.CurrentTabBarStack.clear();
4190     g.ShrinkWidthBuffer.clear();
4191 
4192     g.ClipboardHandlerData.clear();
4193     g.MenusIdSubmittedThisFrame.clear();
4194     g.InputTextState.ClearFreeMemory();
4195 
4196     g.SettingsWindows.clear();
4197     g.SettingsHandlers.clear();
4198 
4199     if (g.LogFile)
4200     {
4201 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
4202         if (g.LogFile != stdout)
4203 #endif
4204             ImFileClose(g.LogFile);
4205         g.LogFile = NULL;
4206     }
4207     g.LogBuffer.clear();
4208 
4209     g.Initialized = false;
4210 }
4211 
4212 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)4213 static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
4214 {
4215     const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
4216     const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
4217     if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
4218         return d;
4219     if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
4220         return d;
4221     return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
4222 }
4223 
AddWindowToSortBuffer(ImVector<ImGuiWindow * > * out_sorted_windows,ImGuiWindow * window)4224 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
4225 {
4226     out_sorted_windows->push_back(window);
4227     if (window->Active)
4228     {
4229         int count = window->DC.ChildWindows.Size;
4230         if (count > 1)
4231             ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
4232         for (int i = 0; i < count; i++)
4233         {
4234             ImGuiWindow* child = window->DC.ChildWindows[i];
4235             if (child->Active)
4236                 AddWindowToSortBuffer(out_sorted_windows, child);
4237         }
4238     }
4239 }
4240 
AddDrawListToDrawData(ImVector<ImDrawList * > * out_list,ImDrawList * draw_list)4241 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
4242 {
4243     // Remove trailing command if unused.
4244     // Technically we could return directly instead of popping, but this make things looks neat in Metrics window as well.
4245     draw_list->_PopUnusedDrawCmd();
4246     if (draw_list->CmdBuffer.Size == 0)
4247         return;
4248 
4249     // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
4250     // May trigger for you if you are using PrimXXX functions incorrectly.
4251     IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
4252     IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
4253     if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset))
4254         IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
4255 
4256     // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
4257     // If this assert triggers because you are drawing lots of stuff manually:
4258     // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds.
4259     //   Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics window to inspect draw list contents.
4260     // - If you want large meshes with more than 64K vertices, you can either:
4261     //   (A) Handle the ImDrawCmd::VtxOffset value in your renderer back-end, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'.
4262     //       Most example back-ends already support this from 1.71. Pre-1.71 back-ends won't.
4263     //       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.
4264     //   (B) Or handle 32-bit indices in your renderer back-end, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h.
4265     //       Most example back-ends already support this. For example, the OpenGL example code detect index size at compile-time:
4266     //         glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
4267     //       Your own engine or render API may use different parameters or function calls to specify index sizes.
4268     //       2 and 4 bytes indices are generally supported by most graphics API.
4269     // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching
4270     //   the 64K limit to split your draw commands in multiple draw lists.
4271     if (sizeof(ImDrawIdx) == 2)
4272         IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
4273 
4274     out_list->push_back(draw_list);
4275 }
4276 
AddWindowToDrawData(ImGuiWindow * window,int layer)4277 static void AddWindowToDrawData(ImGuiWindow* window, int layer)
4278 {
4279     ImGuiContext& g = *GImGui;
4280     g.IO.MetricsRenderWindows++;
4281     AddDrawListToDrawData(&window->Viewport->DrawDataBuilder.Layers[layer], window->DrawList);
4282     for (int i = 0; i < window->DC.ChildWindows.Size; i++)
4283     {
4284         ImGuiWindow* child = window->DC.ChildWindows[i];
4285         if (IsWindowActiveAndVisible(child)) // Clipped children may have been marked not active
4286             AddWindowToDrawData(child, layer);
4287     }
4288 }
4289 
4290 // Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)
AddRootWindowToDrawData(ImGuiWindow * window)4291 static void AddRootWindowToDrawData(ImGuiWindow* window)
4292 {
4293     int layer = (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0;
4294     AddWindowToDrawData(window, layer);
4295 }
4296 
FlattenIntoSingleLayer()4297 void ImDrawDataBuilder::FlattenIntoSingleLayer()
4298 {
4299     int n = Layers[0].Size;
4300     int size = n;
4301     for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
4302         size += Layers[i].Size;
4303     Layers[0].resize(size);
4304     for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
4305     {
4306         ImVector<ImDrawList*>& layer = Layers[layer_n];
4307         if (layer.empty())
4308             continue;
4309         memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
4310         n += layer.Size;
4311         layer.resize(0);
4312     }
4313 }
4314 
SetupViewportDrawData(ImGuiViewportP * viewport,ImVector<ImDrawList * > * draw_lists)4315 static void SetupViewportDrawData(ImGuiViewportP* viewport, ImVector<ImDrawList*>* draw_lists)
4316 {
4317     // When minimized, we report draw_data->DisplaySize as zero to be consistent with non-viewport mode,
4318     // and to allow applications/back-ends to easily skip rendering.
4319     // FIXME: Note that we however do NOT attempt to report "zero drawlist / vertices" into the ImDrawData structure.
4320     // This is because the work has been done already, and its wasted! We should fix that and add optimizations for
4321     // it earlier in the pipeline, rather than pretend to hide the data at the end of the pipeline.
4322     const bool is_minimized = (viewport->Flags & ImGuiViewportFlags_Minimized) != 0;
4323 
4324     ImDrawData* draw_data = &viewport->DrawDataP;
4325     viewport->DrawData = draw_data; // Make publicly accessible
4326     draw_data->Valid = true;
4327     draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
4328     draw_data->CmdListsCount = draw_lists->Size;
4329     draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
4330     draw_data->DisplayPos = viewport->Pos;
4331     draw_data->DisplaySize = is_minimized ? ImVec2(0.0f, 0.0f) : viewport->Size;
4332     draw_data->FramebufferScale = ImGui::GetIO().DisplayFramebufferScale; // FIXME-VIEWPORT: This may vary on a per-monitor/viewport basis?
4333     draw_data->OwnerViewport = viewport;
4334     for (int n = 0; n < draw_lists->Size; n++)
4335     {
4336         draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size;
4337         draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size;
4338     }
4339 }
4340 
4341 // Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering.
4342 // - When using this function it is sane to ensure that float are perfectly rounded to integer values,
4343 //   so that e.g. (int)(max.x-min.x) in user's render produce correct result.
4344 // - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect():
4345 //   some frequently called functions which to modify both channels and clipping simultaneously tend to use the
4346 //   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)4347 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
4348 {
4349     ImGuiWindow* window = GetCurrentWindow();
4350     window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
4351     window->ClipRect = window->DrawList->_ClipRectStack.back();
4352 }
4353 
PopClipRect()4354 void ImGui::PopClipRect()
4355 {
4356     ImGuiWindow* window = GetCurrentWindow();
4357     window->DrawList->PopClipRect();
4358     window->ClipRect = window->DrawList->_ClipRectStack.back();
4359 }
4360 
FindFrontMostVisibleChildWindow(ImGuiWindow * window)4361 static ImGuiWindow* FindFrontMostVisibleChildWindow(ImGuiWindow* window)
4362 {
4363     for (int n = window->DC.ChildWindows.Size - 1; n >= 0; n--)
4364         if (IsWindowActiveAndVisible(window->DC.ChildWindows[n]))
4365             return FindFrontMostVisibleChildWindow(window->DC.ChildWindows[n]);
4366     return window;
4367 }
4368 
EndFrameDrawDimmedBackgrounds()4369 static void ImGui::EndFrameDrawDimmedBackgrounds()
4370 {
4371     ImGuiContext& g = *GImGui;
4372 
4373     // Draw modal whitening background on _other_ viewports than the one the modal is one
4374     ImGuiWindow* modal_window = GetTopMostPopupModal();
4375     const bool dim_bg_for_modal = (modal_window != NULL);
4376     const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL);
4377     if (dim_bg_for_modal || dim_bg_for_window_list)
4378         for (int viewport_n = 0; viewport_n < g.Viewports.Size; viewport_n++)
4379         {
4380             ImGuiViewportP* viewport = g.Viewports[viewport_n];
4381             if (modal_window && viewport == modal_window->Viewport)
4382                 continue;
4383             if (g.NavWindowingListWindow && viewport == g.NavWindowingListWindow->Viewport)
4384                 continue;
4385             if (g.NavWindowingTargetAnim && viewport == g.NavWindowingTargetAnim->Viewport)
4386                 continue;
4387             ImDrawList* draw_list = GetForegroundDrawList(viewport);
4388             const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
4389             draw_list->AddRectFilled(viewport->Pos, viewport->Pos + viewport->Size, dim_bg_col);
4390         }
4391 
4392     // Draw modal whitening background between CTRL-TAB list
4393     if (dim_bg_for_window_list && g.NavWindowingTargetAnim->Active)
4394     {
4395         // Choose a draw list that will be front-most across all our children
4396         // In the unlikely case that the window wasn't made active we can't rely on its drawlist and skip rendering all-together.
4397         ImGuiWindow* window = g.NavWindowingTargetAnim;
4398         ImDrawList* draw_list = FindFrontMostVisibleChildWindow(window->RootWindow)->DrawList;
4399         draw_list->PushClipRectFullScreen();
4400 
4401         // Docking: draw modal whitening background on other nodes of a same dock tree
4402         // For CTRL+TAB within a docking node we need to render the dimming background in 8 steps
4403         // (Because the root node renders the background in one shot, in order to avoid flickering when a child dock node is not submitted)
4404         if (window->RootWindowDockStop->DockIsActive)
4405             if (window->RootWindow != window->RootWindowDockStop)
4406                 RenderRectFilledWithHole(draw_list, window->RootWindow->Rect(), window->RootWindowDockStop->Rect(), GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio), g.Style.WindowRounding);
4407 
4408         // Draw navigation selection/windowing rectangle border
4409         float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
4410         ImRect bb = window->Rect();
4411         bb.Expand(g.FontSize);
4412         if (bb.Contains(window->Viewport->GetMainRect())) // If a window fits the entire viewport, adjust its highlight inward
4413         {
4414             bb.Expand(-g.FontSize - 1.0f);
4415             rounding = window->WindowRounding;
4416         }
4417         draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f);
4418         draw_list->PopClipRect();
4419     }
4420 }
4421 
4422 // 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()4423 void ImGui::EndFrame()
4424 {
4425     ImGuiContext& g = *GImGui;
4426     IM_ASSERT(g.Initialized);
4427 
4428     // Don't process EndFrame() multiple times.
4429     if (g.FrameCountEnded == g.FrameCount)
4430         return;
4431     IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?");
4432 
4433     ErrorCheckEndFrameSanityChecks();
4434 
4435     // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
4436     if (g.PlatformIO.Platform_SetImeInputPos && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImePos - g.PlatformImeLastPos) > 0.0001f))
4437         if (g.PlatformImePosViewport && g.PlatformImePosViewport->PlatformWindowCreated)
4438         {
4439             g.PlatformIO.Platform_SetImeInputPos(g.PlatformImePosViewport, g.PlatformImePos);
4440             g.PlatformImeLastPos = g.PlatformImePos;
4441             g.PlatformImePosViewport = NULL;
4442         }
4443 
4444     // Hide implicit/fallback "Debug" window if it hasn't been used
4445     g.WithinFrameScopeWithImplicitWindow = false;
4446     if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
4447         g.CurrentWindow->Active = false;
4448     End();
4449 
4450     // Draw modal whitening background on _other_ viewports than the one the modal is one
4451     EndFrameDrawDimmedBackgrounds();
4452 
4453     // Update navigation: CTRL+Tab, wrap-around requests
4454     NavEndFrame();
4455 
4456     SetCurrentViewport(NULL, NULL);
4457 
4458     // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
4459     if (g.DragDropActive)
4460     {
4461         bool is_delivered = g.DragDropPayload.Delivery;
4462         bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton));
4463         if (is_delivered || is_elapsed)
4464             ClearDragDrop();
4465     }
4466 
4467     // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.
4468     if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
4469     {
4470         g.DragDropWithinSource = true;
4471         SetTooltip("...");
4472         g.DragDropWithinSource = false;
4473     }
4474 
4475     // End frame
4476     g.WithinFrameScope = false;
4477     g.FrameCountEnded = g.FrameCount;
4478 
4479     // Initiate moving window + handle left-click and right-click focus
4480     UpdateMouseMovingWindowEndFrame();
4481 
4482     // Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some)
4483     UpdateViewportsEndFrame();
4484 
4485     // Sort the window list so that all child windows are after their parent
4486     // We cannot do that on FocusWindow() because children may not exist yet
4487     g.WindowsTempSortBuffer.resize(0);
4488     g.WindowsTempSortBuffer.reserve(g.Windows.Size);
4489     for (int i = 0; i != g.Windows.Size; i++)
4490     {
4491         ImGuiWindow* window = g.Windows[i];
4492         if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow))       // if a child is active its parent will add it
4493             continue;
4494         AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window);
4495     }
4496 
4497     // 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.
4498     IM_ASSERT(g.Windows.Size == g.WindowsTempSortBuffer.Size);
4499     g.Windows.swap(g.WindowsTempSortBuffer);
4500     g.IO.MetricsActiveWindows = g.WindowsActiveCount;
4501 
4502     // Unlock font atlas
4503     g.IO.Fonts->Locked = false;
4504 
4505     // Clear Input data for next frame
4506     g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
4507     g.IO.InputQueueCharacters.resize(0);
4508     memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
4509 }
4510 
Render()4511 void ImGui::Render()
4512 {
4513     ImGuiContext& g = *GImGui;
4514     IM_ASSERT(g.Initialized);
4515 
4516     if (g.FrameCountEnded != g.FrameCount)
4517         EndFrame();
4518     g.FrameCountRendered = g.FrameCount;
4519     g.IO.MetricsRenderWindows = 0;
4520 
4521     // Add background ImDrawList (for each active viewport)
4522     for (int n = 0; n != g.Viewports.Size; n++)
4523     {
4524         ImGuiViewportP* viewport = g.Viewports[n];
4525         viewport->DrawDataBuilder.Clear();
4526         if (viewport->DrawLists[0] != NULL)
4527             AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport));
4528     }
4529 
4530     // Add ImDrawList to render
4531     ImGuiWindow* windows_to_render_top_most[2];
4532     windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
4533     windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL);
4534     for (int n = 0; n != g.Windows.Size; n++)
4535     {
4536         ImGuiWindow* window = g.Windows[n];
4537         if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])
4538             AddRootWindowToDrawData(window);
4539     }
4540     for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++)
4541         if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window
4542             AddRootWindowToDrawData(windows_to_render_top_most[n]);
4543 
4544     ImVec2 mouse_cursor_offset, mouse_cursor_size, mouse_cursor_uv[4];
4545     if (g.IO.MouseDrawCursor && g.MouseCursor != ImGuiMouseCursor_None)
4546         g.IO.Fonts->GetMouseCursorTexData(g.MouseCursor, &mouse_cursor_offset, &mouse_cursor_size, &mouse_cursor_uv[0], &mouse_cursor_uv[2]);
4547 
4548     // Setup ImDrawData structures for end-user
4549     g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0;
4550     for (int n = 0; n < g.Viewports.Size; n++)
4551     {
4552         ImGuiViewportP* viewport = g.Viewports[n];
4553         viewport->DrawDataBuilder.FlattenIntoSingleLayer();
4554 
4555         // Draw software mouse cursor if requested by io.MouseDrawCursor flag
4556         // (note we scale cursor by current viewport/monitor, however Windows 10 for its own hardware cursor seems to be using a different scale factor)
4557         if (mouse_cursor_size.x > 0.0f && mouse_cursor_size.y > 0.0f)
4558         {
4559             float scale = g.Style.MouseCursorScale * viewport->DpiScale;
4560             if (viewport->GetMainRect().Overlaps(ImRect(g.IO.MousePos, g.IO.MousePos + ImVec2(mouse_cursor_size.x + 2, mouse_cursor_size.y + 2) * scale)))
4561                 RenderMouseCursor(GetForegroundDrawList(viewport), g.IO.MousePos, scale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
4562         }
4563 
4564         // Add foreground ImDrawList (for each active viewport)
4565         if (viewport->DrawLists[1] != NULL)
4566             AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport));
4567 
4568         SetupViewportDrawData(viewport, &viewport->DrawDataBuilder.Layers[0]);
4569         g.IO.MetricsRenderVertices += viewport->DrawData->TotalVtxCount;
4570         g.IO.MetricsRenderIndices += viewport->DrawData->TotalIdxCount;
4571     }
4572 
4573     // (Legacy) Call the Render callback function. The current prefer way is to let the user retrieve GetDrawData() and call the render function themselves.
4574 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
4575     if (g.Viewports[0]->DrawData->CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)
4576         g.IO.RenderDrawListsFn(g.Viewports[0]->DrawData);
4577 #endif
4578 }
4579 
4580 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
4581 // 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)4582 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
4583 {
4584     ImGuiContext& g = *GImGui;
4585 
4586     const char* text_display_end;
4587     if (hide_text_after_double_hash)
4588         text_display_end = FindRenderedTextEnd(text, text_end);      // Hide anything after a '##' string
4589     else
4590         text_display_end = text_end;
4591 
4592     ImFont* font = g.Font;
4593     const float font_size = g.FontSize;
4594     if (text == text_display_end)
4595         return ImVec2(0.0f, font_size);
4596     ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
4597 
4598     // Round
4599     text_size.x = IM_FLOOR(text_size.x + 0.95f);
4600 
4601     return text_size;
4602 }
4603 
4604 // Find window given position, search front-to-back
4605 // FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically
4606 // with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
4607 // called, aka before the next Begin(). Moving window isn't affected.
FindHoveredWindow()4608 static void FindHoveredWindow()
4609 {
4610     ImGuiContext& g = *GImGui;
4611 
4612     // Special handling for the window being moved: Ignore the mouse viewport check (because it may reset/lose its viewport during the undocking frame)
4613     ImGuiViewportP* moving_window_viewport = g.MovingWindow ? g.MovingWindow->Viewport : NULL;
4614     if (g.MovingWindow)
4615         g.MovingWindow->Viewport = g.MouseViewport;
4616 
4617     ImGuiWindow* hovered_window = NULL;
4618     ImGuiWindow* hovered_window_ignoring_moving_window = NULL;
4619     if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
4620         hovered_window = g.MovingWindow;
4621 
4622     ImVec2 padding_regular = g.Style.TouchExtraPadding;
4623     ImVec2 padding_for_resize_from_edges = g.IO.ConfigWindowsResizeFromEdges ? ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS)) : padding_regular;
4624     for (int i = g.Windows.Size - 1; i >= 0; i--)
4625     {
4626         ImGuiWindow* window = g.Windows[i];
4627         if (!window->Active || window->Hidden)
4628             continue;
4629         if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
4630             continue;
4631         IM_ASSERT(window->Viewport);
4632         if (window->Viewport != g.MouseViewport)
4633             continue;
4634 
4635         // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
4636         ImRect bb(window->OuterRectClipped);
4637         if (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize))
4638             bb.Expand(padding_regular);
4639         else
4640             bb.Expand(padding_for_resize_from_edges);
4641         if (!bb.Contains(g.IO.MousePos))
4642             continue;
4643 
4644         // Support for one rectangular hole in any given window
4645         // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512)
4646         if (window->HitTestHoleSize.x != 0)
4647         {
4648             ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y);
4649             ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y);
4650             if (ImRect(hole_pos, hole_pos + hole_size).Contains(g.IO.MousePos))
4651                 continue;
4652         }
4653 
4654         if (hovered_window == NULL)
4655             hovered_window = window;
4656         if (hovered_window_ignoring_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow))
4657             hovered_window_ignoring_moving_window = window;
4658         if (hovered_window && hovered_window_ignoring_moving_window)
4659             break;
4660     }
4661 
4662     g.HoveredWindow = hovered_window;
4663     g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
4664     g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window;
4665 
4666     if (g.MovingWindow)
4667         g.MovingWindow->Viewport = moving_window_viewport;
4668 }
4669 
4670 // Test if mouse cursor is hovering given rectangle
4671 // NB- Rectangle is clipped by our current clip setting
4672 // 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)4673 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
4674 {
4675     ImGuiContext& g = *GImGui;
4676 
4677     // Clip
4678     ImRect rect_clipped(r_min, r_max);
4679     if (clip)
4680         rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
4681 
4682     // Expand for touch input
4683     const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
4684     if (!rect_for_touch.Contains(g.IO.MousePos))
4685         return false;
4686     if (!g.MouseViewport->GetMainRect().Overlaps(rect_clipped))
4687         return false;
4688     return true;
4689 }
4690 
GetKeyIndex(ImGuiKey imgui_key)4691 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
4692 {
4693     IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
4694     ImGuiContext& g = *GImGui;
4695     return g.IO.KeyMap[imgui_key];
4696 }
4697 
4698 // Note that dear imgui doesn't know the semantic of each entry of io.KeysDown[]!
4699 // Use your own indices/enums according to how your back-end/engine stored them into io.KeysDown[]!
IsKeyDown(int user_key_index)4700 bool ImGui::IsKeyDown(int user_key_index)
4701 {
4702     if (user_key_index < 0)
4703         return false;
4704     ImGuiContext& g = *GImGui;
4705     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4706     return g.IO.KeysDown[user_key_index];
4707 }
4708 
4709 // t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
4710 // t1 = current time (e.g.: g.Time)
4711 // An event is triggered at:
4712 //  t = 0.0f     t = repeat_delay,    t = repeat_delay + repeat_rate*N
CalcTypematicRepeatAmount(float t0,float t1,float repeat_delay,float repeat_rate)4713 int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)
4714 {
4715     if (t1 == 0.0f)
4716         return 1;
4717     if (t0 >= t1)
4718         return 0;
4719     if (repeat_rate <= 0.0f)
4720         return (t0 < repeat_delay) && (t1 >= repeat_delay);
4721     const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);
4722     const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);
4723     const int count = count_t1 - count_t0;
4724     return count;
4725 }
4726 
GetKeyPressedAmount(int key_index,float repeat_delay,float repeat_rate)4727 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
4728 {
4729     ImGuiContext& g = *GImGui;
4730     if (key_index < 0)
4731         return 0;
4732     IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4733     const float t = g.IO.KeysDownDuration[key_index];
4734     return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
4735 }
4736 
IsKeyPressed(int user_key_index,bool repeat)4737 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
4738 {
4739     ImGuiContext& g = *GImGui;
4740     if (user_key_index < 0)
4741         return false;
4742     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4743     const float t = g.IO.KeysDownDuration[user_key_index];
4744     if (t == 0.0f)
4745         return true;
4746     if (repeat && t > g.IO.KeyRepeatDelay)
4747         return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
4748     return false;
4749 }
4750 
IsKeyReleased(int user_key_index)4751 bool ImGui::IsKeyReleased(int user_key_index)
4752 {
4753     ImGuiContext& g = *GImGui;
4754     if (user_key_index < 0) return false;
4755     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4756     return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
4757 }
4758 
IsMouseDown(ImGuiMouseButton button)4759 bool ImGui::IsMouseDown(ImGuiMouseButton button)
4760 {
4761     ImGuiContext& g = *GImGui;
4762     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4763     return g.IO.MouseDown[button];
4764 }
4765 
IsMouseClicked(ImGuiMouseButton button,bool repeat)4766 bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)
4767 {
4768     ImGuiContext& g = *GImGui;
4769     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4770     const float t = g.IO.MouseDownDuration[button];
4771     if (t == 0.0f)
4772         return true;
4773 
4774     if (repeat && t > g.IO.KeyRepeatDelay)
4775     {
4776         // 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.
4777         int amount = CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.50f);
4778         if (amount > 0)
4779             return true;
4780     }
4781     return false;
4782 }
4783 
IsMouseReleased(ImGuiMouseButton button)4784 bool ImGui::IsMouseReleased(ImGuiMouseButton button)
4785 {
4786     ImGuiContext& g = *GImGui;
4787     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4788     return g.IO.MouseReleased[button];
4789 }
4790 
IsMouseDoubleClicked(ImGuiMouseButton button)4791 bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
4792 {
4793     ImGuiContext& g = *GImGui;
4794     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4795     return g.IO.MouseDoubleClicked[button];
4796 }
4797 
4798 // Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame.
4799 // [Internal] This doesn't test if the button is pressed
IsMouseDragPastThreshold(ImGuiMouseButton button,float lock_threshold)4800 bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold)
4801 {
4802     ImGuiContext& g = *GImGui;
4803     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4804     if (lock_threshold < 0.0f)
4805         lock_threshold = g.IO.MouseDragThreshold;
4806     return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
4807 }
4808 
IsMouseDragging(ImGuiMouseButton button,float lock_threshold)4809 bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold)
4810 {
4811     ImGuiContext& g = *GImGui;
4812     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4813     if (!g.IO.MouseDown[button])
4814         return false;
4815     return IsMouseDragPastThreshold(button, lock_threshold);
4816 }
4817 
GetMousePos()4818 ImVec2 ImGui::GetMousePos()
4819 {
4820     ImGuiContext& g = *GImGui;
4821     return g.IO.MousePos;
4822 }
4823 
4824 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()4825 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
4826 {
4827     ImGuiContext& g = *GImGui;
4828     if (g.BeginPopupStack.Size > 0)
4829         return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos;
4830     return g.IO.MousePos;
4831 }
4832 
4833 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
IsMousePosValid(const ImVec2 * mouse_pos)4834 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
4835 {
4836     // The assert is only to silence a false-positive in XCode Static Analysis.
4837     // 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).
4838     IM_ASSERT(GImGui != NULL);
4839     const float MOUSE_INVALID = -256000.0f;
4840     ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
4841     return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
4842 }
4843 
IsAnyMouseDown()4844 bool ImGui::IsAnyMouseDown()
4845 {
4846     ImGuiContext& g = *GImGui;
4847     for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
4848         if (g.IO.MouseDown[n])
4849             return true;
4850     return false;
4851 }
4852 
4853 // Return the delta from the initial clicking position while the mouse button is clicked or was just released.
4854 // This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
4855 // NB: This is only valid if IsMousePosValid(). Back-ends in theory should always keep mouse position valid when dragging even outside the client window.
GetMouseDragDelta(ImGuiMouseButton button,float lock_threshold)4856 ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold)
4857 {
4858     ImGuiContext& g = *GImGui;
4859     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4860     if (lock_threshold < 0.0f)
4861         lock_threshold = g.IO.MouseDragThreshold;
4862     if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])
4863         if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
4864             if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))
4865                 return g.IO.MousePos - g.IO.MouseClickedPos[button];
4866     return ImVec2(0.0f, 0.0f);
4867 }
4868 
ResetMouseDragDelta(ImGuiMouseButton button)4869 void ImGui::ResetMouseDragDelta(ImGuiMouseButton button)
4870 {
4871     ImGuiContext& g = *GImGui;
4872     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4873     // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
4874     g.IO.MouseClickedPos[button] = g.IO.MousePos;
4875 }
4876 
GetMouseCursor()4877 ImGuiMouseCursor ImGui::GetMouseCursor()
4878 {
4879     return GImGui->MouseCursor;
4880 }
4881 
SetMouseCursor(ImGuiMouseCursor cursor_type)4882 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
4883 {
4884     GImGui->MouseCursor = cursor_type;
4885 }
4886 
CaptureKeyboardFromApp(bool capture)4887 void ImGui::CaptureKeyboardFromApp(bool capture)
4888 {
4889     GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
4890 }
4891 
CaptureMouseFromApp(bool capture)4892 void ImGui::CaptureMouseFromApp(bool capture)
4893 {
4894     GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
4895 }
4896 
IsItemActive()4897 bool ImGui::IsItemActive()
4898 {
4899     ImGuiContext& g = *GImGui;
4900     if (g.ActiveId)
4901     {
4902         ImGuiWindow* window = g.CurrentWindow;
4903         return g.ActiveId == window->DC.LastItemId;
4904     }
4905     return false;
4906 }
4907 
IsItemActivated()4908 bool ImGui::IsItemActivated()
4909 {
4910     ImGuiContext& g = *GImGui;
4911     if (g.ActiveId)
4912     {
4913         ImGuiWindow* window = g.CurrentWindow;
4914         if (g.ActiveId == window->DC.LastItemId && g.ActiveIdPreviousFrame != window->DC.LastItemId)
4915             return true;
4916     }
4917     return false;
4918 }
4919 
IsItemDeactivated()4920 bool ImGui::IsItemDeactivated()
4921 {
4922     ImGuiContext& g = *GImGui;
4923     ImGuiWindow* window = g.CurrentWindow;
4924     if (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDeactivated)
4925         return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Deactivated) != 0;
4926     return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId);
4927 }
4928 
IsItemDeactivatedAfterEdit()4929 bool ImGui::IsItemDeactivatedAfterEdit()
4930 {
4931     ImGuiContext& g = *GImGui;
4932     return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore));
4933 }
4934 
IsItemFocused()4935 bool ImGui::IsItemFocused()
4936 {
4937     ImGuiContext& g = *GImGui;
4938     ImGuiWindow* window = g.CurrentWindow;
4939 
4940     if (g.NavId == 0 || g.NavDisableHighlight || g.NavId != window->DC.LastItemId)
4941         return false;
4942 
4943     // Special handling for the dummy item after Begin() which represent the title bar or tab.
4944     // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
4945     if (window->DC.LastItemId == window->ID && window->WriteAccessed)
4946         return false;
4947 
4948     return true;
4949 }
4950 
IsItemClicked(ImGuiMouseButton mouse_button)4951 bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button)
4952 {
4953     return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
4954 }
4955 
IsItemToggledOpen()4956 bool ImGui::IsItemToggledOpen()
4957 {
4958     ImGuiContext& g = *GImGui;
4959     return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false;
4960 }
4961 
IsItemToggledSelection()4962 bool ImGui::IsItemToggledSelection()
4963 {
4964     ImGuiContext& g = *GImGui;
4965     return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;
4966 }
4967 
IsAnyItemHovered()4968 bool ImGui::IsAnyItemHovered()
4969 {
4970     ImGuiContext& g = *GImGui;
4971     return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
4972 }
4973 
IsAnyItemActive()4974 bool ImGui::IsAnyItemActive()
4975 {
4976     ImGuiContext& g = *GImGui;
4977     return g.ActiveId != 0;
4978 }
4979 
IsAnyItemFocused()4980 bool ImGui::IsAnyItemFocused()
4981 {
4982     ImGuiContext& g = *GImGui;
4983     return g.NavId != 0 && !g.NavDisableHighlight;
4984 }
4985 
IsItemVisible()4986 bool ImGui::IsItemVisible()
4987 {
4988     ImGuiWindow* window = GetCurrentWindowRead();
4989     return window->ClipRect.Overlaps(window->DC.LastItemRect);
4990 }
4991 
IsItemEdited()4992 bool ImGui::IsItemEdited()
4993 {
4994     ImGuiWindow* window = GetCurrentWindowRead();
4995     return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0;
4996 }
4997 
4998 // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
SetItemAllowOverlap()4999 void ImGui::SetItemAllowOverlap()
5000 {
5001     ImGuiContext& g = *GImGui;
5002     if (g.HoveredId == g.CurrentWindow->DC.LastItemId)
5003         g.HoveredIdAllowOverlap = true;
5004     if (g.ActiveId == g.CurrentWindow->DC.LastItemId)
5005         g.ActiveIdAllowOverlap = true;
5006 }
5007 
GetItemRectMin()5008 ImVec2 ImGui::GetItemRectMin()
5009 {
5010     ImGuiWindow* window = GetCurrentWindowRead();
5011     return window->DC.LastItemRect.Min;
5012 }
5013 
GetItemRectMax()5014 ImVec2 ImGui::GetItemRectMax()
5015 {
5016     ImGuiWindow* window = GetCurrentWindowRead();
5017     return window->DC.LastItemRect.Max;
5018 }
5019 
GetItemRectSize()5020 ImVec2 ImGui::GetItemRectSize()
5021 {
5022     ImGuiWindow* window = GetCurrentWindowRead();
5023     return window->DC.LastItemRect.GetSize();
5024 }
5025 
BeginChildEx(const char * name,ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags flags)5026 bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags)
5027 {
5028     ImGuiContext& g = *GImGui;
5029     ImGuiWindow* parent_window = g.CurrentWindow;
5030 
5031     flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoDocking;
5032     flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove);  // Inherit the NoMove flag
5033 
5034     // Size
5035     const ImVec2 content_avail = GetContentRegionAvail();
5036     ImVec2 size = ImFloor(size_arg);
5037     const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
5038     if (size.x <= 0.0f)
5039         size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
5040     if (size.y <= 0.0f)
5041         size.y = ImMax(content_avail.y + size.y, 4.0f);
5042     SetNextWindowSize(size);
5043 
5044     // 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.
5045     char title[256];
5046     if (name)
5047         ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s_%08X", parent_window->Name, name, id);
5048     else
5049         ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id);
5050 
5051     const float backup_border_size = g.Style.ChildBorderSize;
5052     if (!border)
5053         g.Style.ChildBorderSize = 0.0f;
5054     bool ret = Begin(title, NULL, flags);
5055     g.Style.ChildBorderSize = backup_border_size;
5056 
5057     ImGuiWindow* child_window = g.CurrentWindow;
5058     child_window->ChildId = id;
5059     child_window->AutoFitChildAxises = (ImS8)auto_fit_axises;
5060 
5061     // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
5062     // While this is not really documented/defined, it seems that the expected thing to do.
5063     if (child_window->BeginCount == 1)
5064         parent_window->DC.CursorPos = child_window->Pos;
5065 
5066     // Process navigation-in immediately so NavInit can run on first frame
5067     if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll))
5068     {
5069         FocusWindow(child_window);
5070         NavInitWindow(child_window, false);
5071         SetActiveID(id + 1, child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item
5072         g.ActiveIdSource = ImGuiInputSource_Nav;
5073     }
5074     return ret;
5075 }
5076 
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)5077 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
5078 {
5079     ImGuiWindow* window = GetCurrentWindow();
5080     return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
5081 }
5082 
BeginChild(ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)5083 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
5084 {
5085     IM_ASSERT(id != 0);
5086     return BeginChildEx(NULL, id, size_arg, border, extra_flags);
5087 }
5088 
EndChild()5089 void ImGui::EndChild()
5090 {
5091     ImGuiContext& g = *GImGui;
5092     ImGuiWindow* window = g.CurrentWindow;
5093 
5094     IM_ASSERT(g.WithinEndChild == false);
5095     IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow);   // Mismatched BeginChild()/EndChild() calls
5096 
5097     g.WithinEndChild = true;
5098     if (window->BeginCount > 1)
5099     {
5100         End();
5101     }
5102     else
5103     {
5104         ImVec2 sz = window->Size;
5105         if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
5106             sz.x = ImMax(4.0f, sz.x);
5107         if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
5108             sz.y = ImMax(4.0f, sz.y);
5109         End();
5110 
5111         ImGuiWindow* parent_window = g.CurrentWindow;
5112         ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
5113         ItemSize(sz);
5114         if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
5115         {
5116             ItemAdd(bb, window->ChildId);
5117             RenderNavHighlight(bb, window->ChildId);
5118 
5119             // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
5120             if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow)
5121                 RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
5122         }
5123         else
5124         {
5125             // Not navigable into
5126             ItemAdd(bb, 0);
5127         }
5128     }
5129     g.WithinEndChild = false;
5130 }
5131 
5132 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)5133 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
5134 {
5135     ImGuiContext& g = *GImGui;
5136     const ImGuiStyle& style = g.Style;
5137     PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
5138     PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
5139     PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
5140     PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
5141     bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
5142     PopStyleVar(3);
5143     PopStyleColor();
5144     return ret;
5145 }
5146 
EndChildFrame()5147 void ImGui::EndChildFrame()
5148 {
5149     EndChild();
5150 }
5151 
SetWindowConditionAllowFlags(ImGuiWindow * window,ImGuiCond flags,bool enabled)5152 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
5153 {
5154     window->SetWindowPosAllowFlags       = enabled ? (window->SetWindowPosAllowFlags       | flags) : (window->SetWindowPosAllowFlags       & ~flags);
5155     window->SetWindowSizeAllowFlags      = enabled ? (window->SetWindowSizeAllowFlags      | flags) : (window->SetWindowSizeAllowFlags      & ~flags);
5156     window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
5157     window->SetWindowDockAllowFlags      = enabled ? (window->SetWindowDockAllowFlags      | flags) : (window->SetWindowDockAllowFlags      & ~flags);
5158 }
5159 
FindWindowByID(ImGuiID id)5160 ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
5161 {
5162     ImGuiContext& g = *GImGui;
5163     return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
5164 }
5165 
FindWindowByName(const char * name)5166 ImGuiWindow* ImGui::FindWindowByName(const char* name)
5167 {
5168     ImGuiID id = ImHashStr(name);
5169     return FindWindowByID(id);
5170 }
5171 
ApplyWindowSettings(ImGuiWindow * window,ImGuiWindowSettings * settings)5172 static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
5173 {
5174     ImGuiViewport* main_viewport = ImGui::GetMainViewport();
5175     window->ViewportPos = main_viewport->Pos;
5176     if (settings->ViewportId)
5177     {
5178         window->ViewportId = settings->ViewportId;
5179         window->ViewportPos = ImVec2(settings->ViewportPos.x, settings->ViewportPos.y);
5180     }
5181     window->Pos = ImFloor(ImVec2(settings->Pos.x + window->ViewportPos.x, settings->Pos.y + window->ViewportPos.y));
5182     if (settings->Size.x > 0 && settings->Size.y > 0)
5183         window->Size = window->SizeFull = ImFloor(ImVec2(settings->Size.x, settings->Size.y));
5184     window->Collapsed = settings->Collapsed;
5185     window->DockId = settings->DockId;
5186     window->DockOrder = settings->DockOrder;
5187 }
5188 
CreateNewWindow(const char * name,ImGuiWindowFlags flags)5189 static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags)
5190 {
5191     ImGuiContext& g = *GImGui;
5192     //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags);
5193 
5194     // Create window the first time
5195     ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
5196     window->Flags = flags;
5197     g.WindowsById.SetVoidPtr(window->ID, window);
5198 
5199     // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
5200     ImGuiViewport* main_viewport = ImGui::GetMainViewport();
5201     window->Pos = main_viewport->Pos + ImVec2(60, 60);
5202     window->ViewportPos = main_viewport->Pos;
5203 
5204     // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
5205     if (!(flags & ImGuiWindowFlags_NoSavedSettings))
5206         if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
5207         {
5208             // Retrieve settings from .ini file
5209             window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
5210             SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
5211             ApplyWindowSettings(window, settings);
5212         }
5213     window->DC.CursorStartPos = window->DC.CursorMaxPos = window->Pos; // So first call to CalcContentSize() doesn't return crazy values
5214 
5215     if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
5216     {
5217         window->AutoFitFramesX = window->AutoFitFramesY = 2;
5218         window->AutoFitOnlyGrows = false;
5219     }
5220     else
5221     {
5222         if (window->Size.x <= 0.0f)
5223             window->AutoFitFramesX = 2;
5224         if (window->Size.y <= 0.0f)
5225             window->AutoFitFramesY = 2;
5226         window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
5227     }
5228 
5229     g.WindowsFocusOrder.push_back(window);
5230     if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
5231         g.Windows.push_front(window); // Quite slow but rare and only once
5232     else
5233         g.Windows.push_back(window);
5234     return window;
5235 }
5236 
GetWindowForTitleDisplay(ImGuiWindow * window)5237 static ImGuiWindow* GetWindowForTitleDisplay(ImGuiWindow* window)
5238 {
5239     return window->DockNodeAsHost ? window->DockNodeAsHost->VisibleWindow : window;
5240 }
5241 
GetWindowForTitleAndMenuHeight(ImGuiWindow * window)5242 static ImGuiWindow* GetWindowForTitleAndMenuHeight(ImGuiWindow* window)
5243 {
5244     return (window->DockNodeAsHost && window->DockNodeAsHost->VisibleWindow) ? window->DockNodeAsHost->VisibleWindow : window;
5245 }
5246 
CalcWindowSizeAfterConstraint(ImGuiWindow * window,ImVec2 new_size)5247 static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size)
5248 {
5249     ImGuiContext& g = *GImGui;
5250     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)
5251     {
5252         // Using -1,-1 on either X/Y axis to preserve the current size.
5253         ImRect cr = g.NextWindowData.SizeConstraintRect;
5254         new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
5255         new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
5256         if (g.NextWindowData.SizeCallback)
5257         {
5258             ImGuiSizeCallbackData data;
5259             data.UserData = g.NextWindowData.SizeCallbackUserData;
5260             data.Pos = window->Pos;
5261             data.CurrentSize = window->SizeFull;
5262             data.DesiredSize = new_size;
5263             g.NextWindowData.SizeCallback(&data);
5264             new_size = data.DesiredSize;
5265         }
5266         new_size.x = IM_FLOOR(new_size.x);
5267         new_size.y = IM_FLOOR(new_size.y);
5268     }
5269 
5270     // Minimum size
5271     if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
5272     {
5273         ImGuiWindow* window_for_height = GetWindowForTitleAndMenuHeight(window);
5274         new_size = ImMax(new_size, g.Style.WindowMinSize);
5275         new_size.y = ImMax(new_size.y, window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows
5276     }
5277     return new_size;
5278 }
5279 
CalcWindowContentSize(ImGuiWindow * window)5280 static ImVec2 CalcWindowContentSize(ImGuiWindow* window)
5281 {
5282     if (window->Collapsed)
5283         if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5284             return window->ContentSize;
5285     if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0)
5286         return window->ContentSize;
5287 
5288     ImVec2 sz;
5289     sz.x = IM_FLOOR((window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);
5290     sz.y = IM_FLOOR((window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);
5291     return sz;
5292 }
5293 
CalcWindowAutoFitSize(ImGuiWindow * window,const ImVec2 & size_contents)5294 static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents)
5295 {
5296     ImGuiContext& g = *GImGui;
5297     ImGuiStyle& style = g.Style;
5298     ImVec2 size_decorations = ImVec2(0.0f, window->TitleBarHeight() + window->MenuBarHeight());
5299     ImVec2 size_pad = window->WindowPadding * 2.0f;
5300     ImVec2 size_desired = size_contents + size_pad + size_decorations;
5301     if (window->Flags & ImGuiWindowFlags_Tooltip)
5302     {
5303         // Tooltip always resize
5304         return size_desired;
5305     }
5306     else
5307     {
5308         // Maximum window size is determined by the viewport size or monitor size
5309         const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0;
5310         const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0;
5311         ImVec2 size_min = style.WindowMinSize;
5312         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)
5313             size_min = ImMin(size_min, ImVec2(4.0f, 4.0f));
5314 
5315         // FIXME-VIEWPORT-WORKAREA: May want to use GetWorkSize() instead of Size depending on the type of windows?
5316         ImVec2 avail_size = window->Viewport->Size;
5317         if (window->ViewportOwned)
5318             avail_size = ImVec2(FLT_MAX, FLT_MAX);
5319         const int monitor_idx = window->ViewportAllowPlatformMonitorExtend;
5320         if (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size)
5321             avail_size = g.PlatformIO.Monitors[monitor_idx].WorkSize;
5322         ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, avail_size - g.Style.DisplaySafeAreaPadding * 2.0f));
5323 
5324         // When the window cannot fit all contents (either because of constraints, either because screen is too small),
5325         // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
5326         ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5327         bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - size_decorations.x < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar);
5328         bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - size_decorations.y < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar);
5329         if (will_have_scrollbar_x)
5330             size_auto_fit.y += style.ScrollbarSize;
5331         if (will_have_scrollbar_y)
5332             size_auto_fit.x += style.ScrollbarSize;
5333         return size_auto_fit;
5334     }
5335 }
5336 
CalcWindowExpectedSize(ImGuiWindow * window)5337 ImVec2 ImGui::CalcWindowExpectedSize(ImGuiWindow* window)
5338 {
5339     ImVec2 size_contents = CalcWindowContentSize(window);
5340     ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents);
5341     ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5342     return size_final;
5343 }
5344 
GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)5345 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
5346 {
5347     if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
5348         return ImGuiCol_PopupBg;
5349     if (flags & ImGuiWindowFlags_ChildWindow)
5350         return ImGuiCol_ChildBg;
5351     return ImGuiCol_WindowBg;
5352 }
5353 
CalcResizePosSizeFromAnyCorner(ImGuiWindow * window,const ImVec2 & corner_target,const ImVec2 & corner_norm,ImVec2 * out_pos,ImVec2 * out_size)5354 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
5355 {
5356     ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm);                // Expected window upper-left
5357     ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
5358     ImVec2 size_expected = pos_max - pos_min;
5359     ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected);
5360     *out_pos = pos_min;
5361     if (corner_norm.x == 0.0f)
5362         out_pos->x -= (size_constrained.x - size_expected.x);
5363     if (corner_norm.y == 0.0f)
5364         out_pos->y -= (size_constrained.y - size_expected.y);
5365     *out_size = size_constrained;
5366 }
5367 
5368 struct ImGuiResizeGripDef
5369 {
5370     ImVec2  CornerPosN;
5371     ImVec2  InnerDir;
5372     int     AngleMin12, AngleMax12;
5373 };
5374 
5375 static const ImGuiResizeGripDef resize_grip_def[4] =
5376 {
5377     { ImVec2(1, 1), ImVec2(-1, -1), 0, 3 }, // Lower-right
5378     { ImVec2(0, 1), ImVec2(+1, -1), 3, 6 }, // Lower-left
5379     { ImVec2(0, 0), ImVec2(+1, +1), 6, 9 }, // Upper-left (Unused)
5380     { ImVec2(1, 0), ImVec2(-1, +1), 9, 12 }, // Upper-right (Unused)
5381 };
5382 
5383 struct ImGuiResizeBorderDef
5384 {
5385     ImVec2 InnerDir;
5386     ImVec2 CornerPosN1, CornerPosN2;
5387     float  OuterAngle;
5388 };
5389 
5390 static const ImGuiResizeBorderDef resize_border_def[4] =
5391 {
5392     { ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Top
5393     { ImVec2(-1, 0), ImVec2(1, 0), ImVec2(1, 1), IM_PI * 0.00f }, // Right
5394     { ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f }, // Bottom
5395     { ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f } // Left
5396 };
5397 
GetResizeBorderRect(ImGuiWindow * window,int border_n,float perp_padding,float thickness)5398 static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
5399 {
5400     ImRect rect = window->Rect();
5401     if (thickness == 0.0f) rect.Max -= ImVec2(1, 1);
5402     if (border_n == 0) { return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness,    rect.Max.x - perp_padding, rect.Min.y + thickness);    } // Top
5403     if (border_n == 1) { return ImRect(rect.Max.x - thickness,    rect.Min.y + perp_padding, rect.Max.x + thickness,    rect.Max.y - perp_padding); } // Right
5404     if (border_n == 2) { return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness,    rect.Max.x - perp_padding, rect.Max.y + thickness);    } // Bottom
5405     if (border_n == 3) { return ImRect(rect.Min.x - thickness,    rect.Min.y + perp_padding, rect.Min.x + thickness,    rect.Max.y - perp_padding); } // Left
5406     IM_ASSERT(0);
5407     return ImRect();
5408 }
5409 
5410 // 0..3: corners (Lower-right, Lower-left, Unused, Unused)
5411 // 4..7: borders (Top, Right, Bottom, Left)
GetWindowResizeID(ImGuiWindow * window,int n)5412 ImGuiID ImGui::GetWindowResizeID(ImGuiWindow* window, int n)
5413 {
5414     IM_ASSERT(n >= 0 && n <= 7);
5415     ImGuiID id = window->DockIsActive ? window->DockNode->HostWindow->ID : window->ID;
5416     id = ImHashStr("#RESIZE", 0, id);
5417     id = ImHashData(&n, sizeof(int), id);
5418     return id;
5419 }
5420 
5421 // Handle resize for: Resize Grips, Borders, Gamepad
5422 // 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)5423 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)
5424 {
5425     ImGuiContext& g = *GImGui;
5426     ImGuiWindowFlags flags = window->Flags;
5427 
5428     if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
5429         return false;
5430     if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window.
5431         return false;
5432 
5433     bool ret_auto_fit = false;
5434     const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0;
5435     const float grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
5436     const float grip_hover_inner_size = IM_FLOOR(grip_draw_size * 0.75f);
5437     const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS : 0.0f;
5438 
5439     ImVec2 pos_target(FLT_MAX, FLT_MAX);
5440     ImVec2 size_target(FLT_MAX, FLT_MAX);
5441 
5442     // Clip mouse interaction rectangles within the viewport rectangle (in practice the narrowing is going to happen most of the time).
5443     // - Not narrowing would mostly benefit the situation where OS windows _without_ decoration have a threshold for hovering when outside their limits.
5444     //   This is however not the case with current back-ends under Win32, but a custom borderless window implementation would benefit from it.
5445     // - 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.
5446     // - Note that we are unable to tell if the platform setup allows hovering with a distance threshold (on Win32, decorated window have such threshold).
5447     // We only clip interaction so we overwrite window->ClipRect, cannot call PushClipRect() yet as DrawList is not yet setup.
5448     const bool clip_with_viewport_rect = !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) || (g.IO.MouseHoveredViewport != window->ViewportId) || !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration);
5449     if (clip_with_viewport_rect)
5450         window->ClipRect = window->Viewport->GetMainRect();
5451 
5452     // Resize grips and borders are on layer 1
5453     window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5454     window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu);
5455 
5456     // Manual resize grips
5457     PushID("#RESIZE");
5458     for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5459     {
5460         const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5461         const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
5462 
5463         // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
5464         ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size);
5465         if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
5466         if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
5467         bool hovered, held;
5468         ButtonBehavior(resize_rect, window->GetID(resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
5469         //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
5470         if (hovered || held)
5471             g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
5472 
5473         if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
5474         {
5475             // Manual auto-fit when double-clicking
5476             size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5477             ret_auto_fit = true;
5478             ClearActiveID();
5479         }
5480         else if (held)
5481         {
5482             // Resize from any of the four corners
5483             // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
5484             ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(grip.InnerDir * grip_hover_outer_size, grip.InnerDir * -grip_hover_inner_size, grip.CornerPosN); // Corner of the window corresponding to our corner grip
5485             ImVec2 clamp_min = ImVec2(grip.CornerPosN.x == 1.0f ? visibility_rect.Min.x : -FLT_MAX, grip.CornerPosN.y == 1.0f ? visibility_rect.Min.y : -FLT_MAX);
5486             ImVec2 clamp_max = ImVec2(grip.CornerPosN.x == 0.0f ? visibility_rect.Max.x : +FLT_MAX, grip.CornerPosN.y == 0.0f ? visibility_rect.Max.y : +FLT_MAX);
5487             corner_target = ImClamp(corner_target, clamp_min, clamp_max);
5488             CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPosN, &pos_target, &size_target);
5489         }
5490         if (resize_grip_n == 0 || held || hovered)
5491             resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
5492     }
5493     for (int border_n = 0; border_n < resize_border_count; border_n++)
5494     {
5495         bool hovered, held;
5496         ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS);
5497         ButtonBehavior(border_rect, window->GetID(border_n + 4), &hovered, &held, ImGuiButtonFlags_FlattenChildren);
5498         //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
5499         if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held)
5500         {
5501             g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
5502             if (held)
5503                 *border_held = border_n;
5504         }
5505         if (held)
5506         {
5507             ImVec2 border_target = window->Pos;
5508             ImVec2 border_posn;
5509             if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Top
5510             if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Right
5511             if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Bottom
5512             if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Left
5513             ImVec2 clamp_min = ImVec2(border_n == 1 ? visibility_rect.Min.x : -FLT_MAX, border_n == 2 ? visibility_rect.Min.y : -FLT_MAX);
5514             ImVec2 clamp_max = ImVec2(border_n == 3 ? visibility_rect.Max.x : +FLT_MAX, border_n == 0 ? visibility_rect.Max.y : +FLT_MAX);
5515             border_target = ImClamp(border_target, clamp_min, clamp_max);
5516             CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target);
5517         }
5518     }
5519     PopID();
5520 
5521     // Restore nav layer
5522     window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5523     window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
5524 
5525     // Navigation resize (keyboard/gamepad)
5526     if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
5527     {
5528         ImVec2 nav_resize_delta;
5529         if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift)
5530             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
5531         if (g.NavInputSource == ImGuiInputSource_NavGamepad)
5532             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down);
5533         if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f)
5534         {
5535             const float NAV_RESIZE_SPEED = 600.0f;
5536             nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
5537             nav_resize_delta = ImMax(nav_resize_delta, visibility_rect.Min - window->Pos - window->Size);
5538             g.NavWindowingToggleLayer = false;
5539             g.NavDisableMouseHover = true;
5540             resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
5541             // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
5542             size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + nav_resize_delta);
5543         }
5544     }
5545 
5546     // Apply back modified position/size to window
5547     if (size_target.x != FLT_MAX)
5548     {
5549         window->SizeFull = size_target;
5550         MarkIniSettingsDirty(window);
5551     }
5552     if (pos_target.x != FLT_MAX)
5553     {
5554         window->Pos = ImFloor(pos_target);
5555         MarkIniSettingsDirty(window);
5556     }
5557 
5558     window->Size = window->SizeFull;
5559     return ret_auto_fit;
5560 }
5561 
ClampWindowRect(ImGuiWindow * window,const ImRect & visibility_rect)5562 static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& visibility_rect)
5563 {
5564     ImGuiContext& g = *GImGui;
5565     ImVec2 size_for_clamping = window->Size;
5566     if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
5567         size_for_clamping.y = window->TitleBarHeight();
5568     window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max);
5569 }
5570 
RenderWindowOuterBorders(ImGuiWindow * window)5571 static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
5572 {
5573     ImGuiContext& g = *GImGui;
5574     float rounding = window->WindowRounding;
5575     float border_size = window->WindowBorderSize;
5576     if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground))
5577         window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
5578 
5579     int border_held = window->ResizeBorderHeld;
5580     if (border_held != -1)
5581     {
5582         const ImGuiResizeBorderDef& def = resize_border_def[border_held];
5583         ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f);
5584         window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI * 0.25f, def.OuterAngle);
5585         window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f);
5586         window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), false, ImMax(2.0f, border_size)); // Thicker than usual
5587     }
5588     if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
5589     {
5590         float y = window->Pos.y + window->TitleBarHeight() - 1;
5591         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);
5592     }
5593 }
5594 
5595 // Draw background and borders
5596 // 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)5597 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)
5598 {
5599     ImGuiContext& g = *GImGui;
5600     ImGuiStyle& style = g.Style;
5601     ImGuiWindowFlags flags = window->Flags;
5602 
5603     // Ensure that ScrollBar doesn't read last frame's SkipItems
5604     window->SkipItems = false;
5605 
5606     // Draw window + handle manual resize
5607     // 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.
5608     const float window_rounding = window->WindowRounding;
5609     const float window_border_size = window->WindowBorderSize;
5610     if (window->Collapsed)
5611     {
5612         // Title bar only
5613         float backup_border_size = style.FrameBorderSize;
5614         g.Style.FrameBorderSize = window->WindowBorderSize;
5615         ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
5616         RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
5617         g.Style.FrameBorderSize = backup_border_size;
5618     }
5619     else
5620     {
5621         // Window background
5622         if (!(flags & ImGuiWindowFlags_NoBackground))
5623         {
5624             bool is_docking_transparent_payload = false;
5625             if (g.DragDropActive && (g.FrameCount - g.DragDropAcceptFrameCount) <= 1 && g.IO.ConfigDockingTransparentPayload)
5626                 if (g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) && *(ImGuiWindow**)g.DragDropPayload.Data == window)
5627                     is_docking_transparent_payload = true;
5628 
5629             ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
5630             if (window->ViewportOwned)
5631             {
5632                 // No alpha
5633                 bg_col = (bg_col | IM_COL32_A_MASK);
5634                 if (is_docking_transparent_payload)
5635                     window->Viewport->Alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA;
5636             }
5637             else
5638             {
5639                 // Adjust alpha. For docking
5640                 bool override_alpha = false;
5641                 float alpha = 1.0f;
5642                 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha)
5643                 {
5644                     alpha = g.NextWindowData.BgAlphaVal;
5645                     override_alpha = true;
5646                 }
5647                 if (is_docking_transparent_payload)
5648                 {
5649                     alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA; // FIXME-DOCK: Should that be an override?
5650                     override_alpha = true;
5651                 }
5652                 if (override_alpha)
5653                     bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
5654             }
5655             window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot);
5656         }
5657 
5658         // Title bar
5659         // (when docked, DockNode are drawing their own title bar. Individual windows however do NOT set the _NoTitleBar flag,
5660         // in order for their pos/size to be matching their undocking state.)
5661         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
5662         {
5663             ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
5664             window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top);
5665         }
5666 
5667         // Menu bar
5668         if (flags & ImGuiWindowFlags_MenuBar)
5669         {
5670             ImRect menu_bar_rect = window->MenuBarRect();
5671             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.
5672             window->DrawList->AddRectFilled(menu_bar_rect.Min + ImVec2(window_border_size, 0), menu_bar_rect.Max - ImVec2(window_border_size, 0), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top);
5673             if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
5674                 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
5675         }
5676 
5677         // Docking: Unhide tab bar (small triangle in the corner), drag from small triangle to quickly undock
5678         ImGuiDockNode* node = window->DockNode;
5679         if (window->DockIsActive && node->IsHiddenTabBar() && !node->IsNoTabBar())
5680         {
5681             float unhide_sz_draw = ImFloor(g.FontSize * 0.70f);
5682             float unhide_sz_hit = ImFloor(g.FontSize * 0.55f);
5683             ImVec2 p = node->Pos;
5684             ImRect r(p, p + ImVec2(unhide_sz_hit, unhide_sz_hit));
5685             bool hovered, held;
5686             if (ButtonBehavior(r, window->GetID("#UNHIDE"), &hovered, &held, ImGuiButtonFlags_FlattenChildren))
5687                 node->WantHiddenTabBarToggle = true;
5688             else if (held && IsMouseDragging(0))
5689                 StartMouseMovingWindowOrNode(window, node, true);
5690 
5691             // FIXME-DOCK: Ideally we'd use ImGuiCol_TitleBgActive/ImGuiCol_TitleBg here, but neither is guaranteed to be visible enough at this sort of size..
5692             ImU32 col = GetColorU32(((held && hovered) || (node->IsFocused && !hovered)) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
5693             window->DrawList->AddTriangleFilled(p, p + ImVec2(unhide_sz_draw, 0.0f), p + ImVec2(0.0f, unhide_sz_draw), col);
5694         }
5695 
5696         // Scrollbars
5697         if (window->ScrollbarX)
5698             Scrollbar(ImGuiAxis_X);
5699         if (window->ScrollbarY)
5700             Scrollbar(ImGuiAxis_Y);
5701 
5702         // Render resize grips (after their input handling so we don't have a frame of latency)
5703         if (handle_borders_and_resize_grips && !(flags & ImGuiWindowFlags_NoResize))
5704         {
5705             for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5706             {
5707                 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5708                 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
5709                 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)));
5710                 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)));
5711                 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);
5712                 window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]);
5713             }
5714         }
5715 
5716         // Borders (for dock node host they will be rendered over after the tab bar)
5717         if (handle_borders_and_resize_grips && !window->DockNodeAsHost)
5718             RenderWindowOuterBorders(window);
5719     }
5720 }
5721 
5722 // Render title text, collapse button, close button
5723 // When inside a dock node, this is handled in DockNodeUpdateTabBar() instead.
RenderWindowTitleBarContents(ImGuiWindow * window,const ImRect & title_bar_rect,const char * name,bool * p_open)5724 void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)
5725 {
5726     ImGuiContext& g = *GImGui;
5727     ImGuiStyle& style = g.Style;
5728     ImGuiWindowFlags flags = window->Flags;
5729 
5730     const bool has_close_button = (p_open != NULL);
5731     const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None);
5732 
5733     // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)
5734     const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
5735     window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
5736     window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5737     window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu);
5738 
5739     // Layout buttons
5740     // FIXME: Would be nice to generalize the subtleties expressed here into reusable code.
5741     float pad_l = style.FramePadding.x;
5742     float pad_r = style.FramePadding.x;
5743     float button_sz = g.FontSize;
5744     ImVec2 close_button_pos;
5745     ImVec2 collapse_button_pos;
5746     if (has_close_button)
5747     {
5748         pad_r += button_sz;
5749         close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5750     }
5751     if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)
5752     {
5753         pad_r += button_sz;
5754         collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5755     }
5756     if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)
5757     {
5758         collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l - style.FramePadding.x, title_bar_rect.Min.y);
5759         pad_l += button_sz;
5760     }
5761 
5762     // Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
5763     if (has_collapse_button)
5764         if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos, NULL))
5765             window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function
5766 
5767     // Close button
5768     if (has_close_button)
5769         if (CloseButton(window->GetID("#CLOSE"), close_button_pos))
5770             *p_open = false;
5771 
5772     window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5773     window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
5774     window->DC.ItemFlags = item_flags_backup;
5775 
5776     // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
5777     // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
5778     const char* UNSAVED_DOCUMENT_MARKER = "*";
5779     const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f;
5780     const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
5781 
5782     // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
5783     // while uncentered title text will still reach edges correct.
5784     if (pad_l > style.FramePadding.x)
5785         pad_l += g.Style.ItemInnerSpacing.x;
5786     if (pad_r > style.FramePadding.x)
5787         pad_r += g.Style.ItemInnerSpacing.x;
5788     if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f)
5789     {
5790         float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center
5791         float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x);
5792         pad_l = ImMax(pad_l, pad_extend * centerness);
5793         pad_r = ImMax(pad_r, pad_extend * centerness);
5794     }
5795 
5796     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);
5797     ImRect clip_r(layout_r.Min.x, layout_r.Min.y, layout_r.Max.x + g.Style.ItemInnerSpacing.x, layout_r.Max.y);
5798     //if (g.IO.KeyCtrl) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
5799     RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r);
5800     if (flags & ImGuiWindowFlags_UnsavedDocument)
5801     {
5802         ImVec2 marker_pos = ImVec2(ImMax(layout_r.Min.x, layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x) + text_size.x, layout_r.Min.y) + ImVec2(2 - marker_size_x, 0.0f);
5803         ImVec2 off = ImVec2(0.0f, IM_FLOOR(-g.FontSize * 0.25f));
5804         RenderTextClipped(marker_pos + off, layout_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_r);
5805     }
5806 }
5807 
UpdateWindowParentAndRootLinks(ImGuiWindow * window,ImGuiWindowFlags flags,ImGuiWindow * parent_window)5808 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
5809 {
5810     window->ParentWindow = parent_window;
5811     window->RootWindow = window->RootWindowDockStop = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
5812     if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
5813     {
5814         window->RootWindow = parent_window->RootWindow;
5815         if (!window->DockIsActive && !(parent_window->Flags & ImGuiWindowFlags_DockNodeHost))
5816             window->RootWindowDockStop = parent_window->RootWindowDockStop;
5817     }
5818     if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
5819         window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
5820     while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
5821     {
5822         IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);
5823         window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
5824     }
5825 }
5826 
5827 // Push a new Dear ImGui window to add widgets to.
5828 // - 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.
5829 // - Begin/End can be called multiple times during the frame with the same window name to append content.
5830 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
5831 //   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.
5832 // - 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.
5833 // - 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)5834 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
5835 {
5836     ImGuiContext& g = *GImGui;
5837     const ImGuiStyle& style = g.Style;
5838     IM_ASSERT(name != NULL && name[0] != '\0');     // Window name required
5839     IM_ASSERT(g.WithinFrameScope);                  // Forgot to call ImGui::NewFrame()
5840     IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
5841 
5842     // Find or create
5843     ImGuiWindow* window = FindWindowByName(name);
5844     const bool window_just_created = (window == NULL);
5845     if (window_just_created)
5846         window = CreateNewWindow(name, flags);
5847 
5848     // Automatically disable manual moving/resizing when NoInputs is set
5849     if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
5850         flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
5851 
5852     if (flags & ImGuiWindowFlags_NavFlattened)
5853         IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
5854 
5855     const int current_frame = g.FrameCount;
5856     const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
5857     window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow);
5858 
5859     // Update the Appearing flag
5860     bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1);   // Not using !WasActive because the implicit "Debug" window would always toggle off->on
5861     const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);
5862     if (flags & ImGuiWindowFlags_Popup)
5863     {
5864         ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5865         window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
5866         window_just_activated_by_user |= (window != popup_ref.Window);
5867     }
5868     window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
5869     if (window->Appearing)
5870         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
5871 
5872     // Update Flags, LastFrameActive, BeginOrderXXX fields
5873     if (first_begin_of_the_frame)
5874     {
5875         window->FlagsPreviousFrame = window->Flags;
5876         window->Flags = (ImGuiWindowFlags)flags;
5877         window->LastFrameActive = current_frame;
5878         window->LastTimeActive = (float)g.Time;
5879         window->BeginOrderWithinParent = 0;
5880         window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
5881     }
5882     else
5883     {
5884         flags = window->Flags;
5885     }
5886 
5887     // Docking
5888     // (NB: during the frame dock nodes are created, it is possible that (window->DockIsActive == false) even though (window->DockNode->Windows.Size > 1)
5889     IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL); // Cannot be both
5890     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasDock)
5891         SetWindowDock(window, g.NextWindowData.DockId, g.NextWindowData.DockCond);
5892     if (first_begin_of_the_frame)
5893     {
5894         bool has_dock_node = (window->DockId != 0 || window->DockNode != NULL);
5895         bool new_auto_dock_node = !has_dock_node && GetWindowAlwaysWantOwnTabBar(window);
5896         if (has_dock_node || new_auto_dock_node)
5897         {
5898             BeginDocked(window, p_open);
5899             flags = window->Flags;
5900             if (window->DockIsActive)
5901                 IM_ASSERT(window->DockNode != NULL);
5902 
5903             // Docking currently override constraints
5904             g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint;
5905         }
5906     }
5907 
5908     // 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
5909     ImGuiWindow* parent_window_in_stack = window->DockIsActive ? window->DockNode->HostWindow : g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
5910     ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
5911     IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
5912 
5913     // We allow window memory to be compacted so recreate the base stack when needed.
5914     if (window->IDStack.Size == 0)
5915         window->IDStack.push_back(window->ID);
5916 
5917     // Add to stack
5918     // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
5919     g.CurrentWindowStack.push_back(window);
5920     g.CurrentWindow = NULL;
5921     ErrorCheckBeginEndCompareStacksSize(window, true);
5922     if (flags & ImGuiWindowFlags_Popup)
5923     {
5924         ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5925         popup_ref.Window = window;
5926         g.BeginPopupStack.push_back(popup_ref);
5927         window->PopupId = popup_ref.PopupId;
5928     }
5929 
5930     if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow))
5931         window->NavLastIds[0] = 0;
5932 
5933     // Update ->RootWindow and others pointers (before any possible call to FocusWindow)
5934     if (first_begin_of_the_frame)
5935         UpdateWindowParentAndRootLinks(window, flags, parent_window);
5936 
5937     // Process SetNextWindow***() calls
5938     // (FIXME: Consider splitting the HasXXX flags into X/Y components
5939     bool window_pos_set_by_api = false;
5940     bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
5941     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos)
5942     {
5943         window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
5944         if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
5945         {
5946             // May be processed on the next frame if this is our first frame and we are measuring size
5947             // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
5948             window->SetWindowPosVal = g.NextWindowData.PosVal;
5949             window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
5950             window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5951         }
5952         else
5953         {
5954             SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
5955         }
5956     }
5957     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)
5958     {
5959         window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
5960         window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
5961         SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
5962     }
5963     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasScroll)
5964     {
5965         if (g.NextWindowData.ScrollVal.x >= 0.0f)
5966         {
5967             window->ScrollTarget.x = g.NextWindowData.ScrollVal.x;
5968             window->ScrollTargetCenterRatio.x = 0.0f;
5969         }
5970         if (g.NextWindowData.ScrollVal.y >= 0.0f)
5971         {
5972             window->ScrollTarget.y = g.NextWindowData.ScrollVal.y;
5973             window->ScrollTargetCenterRatio.y = 0.0f;
5974         }
5975     }
5976     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize)
5977         window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;
5978     else if (first_begin_of_the_frame)
5979         window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);
5980     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasWindowClass)
5981         window->WindowClass = g.NextWindowData.WindowClass;
5982     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed)
5983         SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
5984     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus)
5985         FocusWindow(window);
5986     if (window->Appearing)
5987         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
5988 
5989     // When reusing window again multiple times a frame, just append content (don't need to setup again)
5990     if (first_begin_of_the_frame)
5991     {
5992         // Initialize
5993         const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
5994         window->Active = true;
5995         window->HasCloseButton = (p_open != NULL);
5996         window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX);
5997         window->IDStack.resize(1);
5998         window->DrawList->_ResetForNewFrame();
5999 
6000         // Restore buffer capacity when woken from a compacted state, to avoid
6001         if (window->MemoryCompacted)
6002             GcAwakeTransientWindowBuffers(window);
6003 
6004         // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
6005         // 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.
6006         bool window_title_visible_elsewhere = false;
6007         if ((window->Viewport && window->Viewport->Window == window) || (window->DockIsActive))
6008             window_title_visible_elsewhere = true;
6009         else if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0)   // Window titles visible when using CTRL+TAB
6010             window_title_visible_elsewhere = true;
6011         if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
6012         {
6013             size_t buf_len = (size_t)window->NameBufLen;
6014             window->Name = ImStrdupcpy(window->Name, &buf_len, name);
6015             window->NameBufLen = (int)buf_len;
6016         }
6017 
6018         // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
6019 
6020         // Update contents size from last frame for auto-fitting (or use explicit size)
6021         window->ContentSize = CalcWindowContentSize(window);
6022         if (window->HiddenFramesCanSkipItems > 0)
6023             window->HiddenFramesCanSkipItems--;
6024         if (window->HiddenFramesCannotSkipItems > 0)
6025             window->HiddenFramesCannotSkipItems--;
6026 
6027         // Hide new windows for one frame until they calculate their size
6028         if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
6029             window->HiddenFramesCannotSkipItems = 1;
6030 
6031         // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
6032         // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
6033         if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
6034         {
6035             window->HiddenFramesCannotSkipItems = 1;
6036             if (flags & ImGuiWindowFlags_AlwaysAutoResize)
6037             {
6038                 if (!window_size_x_set_by_api)
6039                     window->Size.x = window->SizeFull.x = 0.f;
6040                 if (!window_size_y_set_by_api)
6041                     window->Size.y = window->SizeFull.y = 0.f;
6042                 window->ContentSize = ImVec2(0.f, 0.f);
6043             }
6044         }
6045 
6046         // SELECT VIEWPORT
6047         // We need to do this before using any style/font sizes, as viewport with a different DPI may affect font sizes.
6048 
6049         UpdateSelectWindowViewport(window);
6050         SetCurrentViewport(window, window->Viewport);
6051         window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f;
6052         SetCurrentWindow(window);
6053         flags = window->Flags;
6054 
6055         // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)
6056         // We read Style data after the call to UpdateSelectWindowViewport() which might be swapping the style.
6057 
6058         if (flags & ImGuiWindowFlags_ChildWindow)
6059             window->WindowBorderSize = style.ChildBorderSize;
6060         else
6061             window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
6062         if (!window->DockIsActive && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
6063             window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
6064         else
6065             window->WindowPadding = style.WindowPadding;
6066 
6067         // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size.
6068         window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
6069         window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
6070 
6071         // Collapse window by double-clicking on title bar
6072         // 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
6073         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse) && !window->DockIsActive)
6074         {
6075             // 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.
6076             ImRect title_bar_rect = window->TitleBarRect();
6077             if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
6078                 window->WantCollapseToggle = true;
6079             if (window->WantCollapseToggle)
6080             {
6081                 window->Collapsed = !window->Collapsed;
6082                 MarkIniSettingsDirty(window);
6083                 FocusWindow(window);
6084             }
6085         }
6086         else
6087         {
6088             window->Collapsed = false;
6089         }
6090         window->WantCollapseToggle = false;
6091 
6092         // SIZE
6093 
6094         // Calculate auto-fit size, handle automatic resize
6095         const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSize);
6096         bool use_current_size_for_scrollbar_x = window_just_created;
6097         bool use_current_size_for_scrollbar_y = window_just_created;
6098         if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
6099         {
6100             // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
6101             if (!window_size_x_set_by_api)
6102             {
6103                 window->SizeFull.x = size_auto_fit.x;
6104                 use_current_size_for_scrollbar_x = true;
6105             }
6106             if (!window_size_y_set_by_api)
6107             {
6108                 window->SizeFull.y = size_auto_fit.y;
6109                 use_current_size_for_scrollbar_y = true;
6110             }
6111         }
6112         else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
6113         {
6114             // Auto-fit may only grow window during the first few frames
6115             // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
6116             if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
6117             {
6118                 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
6119                 use_current_size_for_scrollbar_x = true;
6120             }
6121             if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
6122             {
6123                 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
6124                 use_current_size_for_scrollbar_y = true;
6125             }
6126             if (!window->Collapsed)
6127                 MarkIniSettingsDirty(window);
6128         }
6129 
6130         // Apply minimum/maximum window size constraints and final size
6131         window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull);
6132         window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
6133 
6134         // Decoration size
6135         const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
6136 
6137         // POSITION
6138 
6139         // Popup latch its initial position, will position itself when it appears next frame
6140         if (window_just_activated_by_user)
6141         {
6142             window->AutoPosLastDirection = ImGuiDir_None;
6143             if ((flags & ImGuiWindowFlags_Popup) != 0 && !(flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api) // FIXME: BeginPopup() could use SetNextWindowPos()
6144                 window->Pos = g.BeginPopupStack.back().OpenPopupPos;
6145         }
6146 
6147         // Position child window
6148         if (flags & ImGuiWindowFlags_ChildWindow)
6149         {
6150             IM_ASSERT(parent_window && parent_window->Active);
6151             window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
6152             parent_window->DC.ChildWindows.push_back(window);
6153             if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
6154                 window->Pos = parent_window->DC.CursorPos;
6155         }
6156 
6157         const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0);
6158         if (window_pos_with_pivot)
6159             SetWindowPos(window, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering)
6160         else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
6161             window->Pos = FindBestWindowPosForPopup(window);
6162         else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
6163             window->Pos = FindBestWindowPosForPopup(window);
6164         else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
6165             window->Pos = FindBestWindowPosForPopup(window);
6166 
6167         // Late create viewport if we don't fit within our current host viewport.
6168         if (window->ViewportAllowPlatformMonitorExtend >= 0 && !window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_Minimized))
6169             if (!window->Viewport->GetMainRect().Contains(window->Rect()))
6170             {
6171                 // This is based on the assumption that the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport)
6172                 //ImGuiViewport* old_viewport = window->Viewport;
6173                 window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing);
6174 
6175                 // FIXME-DPI
6176                 //IM_ASSERT(old_viewport->DpiScale == window->Viewport->DpiScale); // FIXME-DPI: Something went wrong
6177                 SetCurrentViewport(window, window->Viewport);
6178                 window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f;
6179                 SetCurrentWindow(window);
6180             }
6181 
6182         bool viewport_rect_changed = false;
6183         if (window->ViewportOwned)
6184         {
6185             // Synchronize window --> viewport in most situations
6186             // Synchronize viewport -> window in case the platform window has been moved or resized from the OS/WM
6187             if (window->Viewport->PlatformRequestMove)
6188             {
6189                 window->Pos = window->Viewport->Pos;
6190                 MarkIniSettingsDirty(window);
6191             }
6192             else if (memcmp(&window->Viewport->Pos, &window->Pos, sizeof(window->Pos)) != 0)
6193             {
6194                 viewport_rect_changed = true;
6195                 window->Viewport->Pos = window->Pos;
6196             }
6197 
6198             if (window->Viewport->PlatformRequestResize)
6199             {
6200                 window->Size = window->SizeFull = window->Viewport->Size;
6201                 MarkIniSettingsDirty(window);
6202             }
6203             else if (memcmp(&window->Viewport->Size, &window->Size, sizeof(window->Size)) != 0)
6204             {
6205                 viewport_rect_changed = true;
6206                 window->Viewport->Size = window->Size;
6207             }
6208 
6209             // The viewport may have changed monitor since the global update in UpdateViewportsNewFrame()
6210             // Either a SetNextWindowPos() call in the current frame or a SetWindowPos() call in the previous frame may have this effect.
6211             if (viewport_rect_changed)
6212                 UpdateViewportPlatformMonitor(window->Viewport);
6213 
6214             // Update common viewport flags
6215             const ImGuiViewportFlags viewport_flags_to_clear = ImGuiViewportFlags_TopMost | ImGuiViewportFlags_NoTaskBarIcon | ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoRendererClear;
6216             ImGuiViewportFlags viewport_flags = window->Viewport->Flags & ~viewport_flags_to_clear;
6217             const bool is_short_lived_floating_window = (flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0;
6218             if (flags & ImGuiWindowFlags_Tooltip)
6219                 viewport_flags |= ImGuiViewportFlags_TopMost;
6220             if (g.IO.ConfigViewportsNoTaskBarIcon || is_short_lived_floating_window)
6221                 viewport_flags |= ImGuiViewportFlags_NoTaskBarIcon;
6222             if (g.IO.ConfigViewportsNoDecoration || is_short_lived_floating_window)
6223                 viewport_flags |= ImGuiViewportFlags_NoDecoration;
6224 
6225             // For popups and menus that may be protruding out of their parent viewport, we enable _NoFocusOnClick so that clicking on them
6226             // won't steal the OS focus away from their parent window (which may be reflected in OS the title bar decoration).
6227             // 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,
6228             // but it shouldn't be much of a problem considering those are already popups that are closed when clicking elsewhere.
6229             if (is_short_lived_floating_window && (flags & ImGuiWindowFlags_Modal) == 0)
6230                 viewport_flags |= ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoFocusOnClick;
6231 
6232             // We can overwrite viewport flags using ImGuiWindowClass (advanced users)
6233             // We don't default to the main viewport because.
6234             if (window->WindowClass.ParentViewportId)
6235                 window->Viewport->ParentViewportId = window->WindowClass.ParentViewportId;
6236             else if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && parent_window_in_stack)
6237                 window->Viewport->ParentViewportId = parent_window_in_stack->Viewport->ID;
6238             else
6239                 window->Viewport->ParentViewportId = g.IO.ConfigViewportsNoDefaultParent ? 0 : IMGUI_VIEWPORT_DEFAULT_ID;
6240             if (window->WindowClass.ViewportFlagsOverrideSet)
6241                 viewport_flags |= window->WindowClass.ViewportFlagsOverrideSet;
6242             if (window->WindowClass.ViewportFlagsOverrideClear)
6243                 viewport_flags &= ~window->WindowClass.ViewportFlagsOverrideClear;
6244 
6245             // We also tell the back-end that clearing the platform window won't be necessary, as our window is filling the viewport and we have disabled BgAlpha
6246             if (!(flags & ImGuiWindowFlags_NoBackground))
6247                 viewport_flags &= ~ImGuiViewportFlags_NoRendererClear;
6248 
6249             window->Viewport->Flags = viewport_flags;
6250         }
6251 
6252         // Calculate the range of allowed position for that window (to be movable and visible past safe area padding)
6253         // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect.
6254         ImRect viewport_rect(window->Viewport->GetMainRect());
6255         ImRect viewport_work_rect(window->Viewport->GetWorkRect());
6256         ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
6257         ImRect visibility_rect(viewport_work_rect.Min + visibility_padding, viewport_work_rect.Max - visibility_padding);
6258 
6259         // Clamp position/size so window stays visible within its viewport or monitor
6260         // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
6261         // FIXME: Similar to code in GetWindowAllowedExtentRect()
6262         if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
6263         {
6264             if (!window->ViewportOwned && viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f)
6265             {
6266                 ClampWindowRect(window, visibility_rect);
6267             }
6268             else if (window->ViewportOwned && g.PlatformIO.Monitors.Size > 0)
6269             {
6270                 if (window->Viewport->PlatformMonitor == -1)
6271                 {
6272                     // Fallback for "lost" window (e.g. a monitor disconnected): we move the window back over the main viewport
6273                     SetWindowPos(window, g.Viewports[0]->Pos + style.DisplayWindowPadding, ImGuiCond_Always);
6274                 }
6275                 else
6276                 {
6277                     ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->Viewport->PlatformMonitor];
6278                     visibility_rect.Min = monitor.WorkPos + visibility_padding;
6279                     visibility_rect.Max = monitor.WorkPos + monitor.WorkSize - visibility_padding;
6280                     ClampWindowRect(window, visibility_rect);
6281                 }
6282             }
6283         }
6284         window->Pos = ImFloor(window->Pos);
6285 
6286         // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
6287         // Large values tend to lead to variety of artifacts and are not recommended.
6288         if (window->ViewportOwned || window->DockIsActive)
6289             window->WindowRounding = 0.0f;
6290         else
6291             window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
6292 
6293         // For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts.
6294         //if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar))
6295         //    window->WindowRounding = ImMin(window->WindowRounding, g.FontSize + style.FramePadding.y * 2.0f);
6296 
6297         // Apply window focus (new and reactivated windows are moved to front)
6298         bool want_focus = false;
6299         if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
6300         {
6301             if (flags & ImGuiWindowFlags_Popup)
6302                 want_focus = true;
6303             else if ((window->DockIsActive || (flags & ImGuiWindowFlags_ChildWindow) == 0) && !(flags & ImGuiWindowFlags_Tooltip))
6304                 want_focus = true;
6305         }
6306 
6307         // Decide if we are going to handle borders and resize grips
6308         const bool handle_borders_and_resize_grips = (window->DockNodeAsHost || !window->DockIsActive);
6309 
6310         // Handle manual resize: Resize Grips, Borders, Gamepad
6311         int border_held = -1;
6312         ImU32 resize_grip_col[4] = {};
6313         const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it.
6314         const float resize_grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
6315         if (handle_borders_and_resize_grips && !window->Collapsed)
6316             if (UpdateWindowManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect))
6317                 use_current_size_for_scrollbar_x = use_current_size_for_scrollbar_y = true;
6318         window->ResizeBorderHeld = (signed char)border_held;
6319 
6320         // Synchronize window --> viewport again and one last time (clamping and manual resize may have affected either)
6321         if (window->ViewportOwned)
6322         {
6323             if (!window->Viewport->PlatformRequestMove)
6324                 window->Viewport->Pos = window->Pos;
6325             if (!window->Viewport->PlatformRequestResize)
6326                 window->Viewport->Size = window->Size;
6327             viewport_rect = window->Viewport->GetMainRect();
6328         }
6329 
6330         // Save last known viewport position within the window itself (so it can be saved in .ini file and restored)
6331         window->ViewportPos = window->Viewport->Pos;
6332 
6333         // SCROLLBAR VISIBILITY
6334 
6335         // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).
6336         if (!window->Collapsed)
6337         {
6338             // When reading the current size we need to read it after size constraints have been applied.
6339             // When we use InnerRect here we are intentionally reading last frame size, same for ScrollbarSizes values before we set them again.
6340             ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - decoration_up_height);
6341             ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + window->ScrollbarSizes;
6342             ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;
6343             float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;
6344             float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;
6345             //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?
6346             window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
6347             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));
6348             if (window->ScrollbarX && !window->ScrollbarY)
6349                 window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar);
6350             window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
6351         }
6352 
6353         // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)
6354         // Update various regions. Variables they depends on should be set above in this function.
6355         // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.
6356 
6357         // Outer rectangle
6358         // Not affected by window border size. Used by:
6359         // - FindHoveredWindow() (w/ extra padding when border resize is enabled)
6360         // - Begin() initial clipping rect for drawing window background and borders.
6361         // - Begin() clipping whole child
6362         const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;
6363         const ImRect outer_rect = window->Rect();
6364         const ImRect title_bar_rect = window->TitleBarRect();
6365         window->OuterRectClipped = outer_rect;
6366         if (window->DockIsActive)
6367             window->OuterRectClipped.Min.y += window->TitleBarHeight();
6368         window->OuterRectClipped.ClipWith(host_rect);
6369 
6370         // Inner rectangle
6371         // Not affected by window border size. Used by:
6372         // - InnerClipRect
6373         // - ScrollToBringRectIntoView()
6374         // - NavUpdatePageUpPageDown()
6375         // - Scrollbar()
6376         window->InnerRect.Min.x = window->Pos.x;
6377         window->InnerRect.Min.y = window->Pos.y + decoration_up_height;
6378         window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x;
6379         window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y;
6380 
6381         // Inner clipping rectangle.
6382         // Will extend a little bit outside the normal work region.
6383         // This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
6384         // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
6385         // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
6386         // Affected by window/frame border size. Used by:
6387         // - Begin() initial clip rect
6388         float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
6389         window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
6390         window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size);
6391         window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
6392         window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize);
6393         window->InnerClipRect.ClipWithFull(host_rect);
6394 
6395         // Default item width. Make it proportional to window size if window manually resizes
6396         if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
6397             window->ItemWidthDefault = ImFloor(window->Size.x * 0.65f);
6398         else
6399             window->ItemWidthDefault = ImFloor(g.FontSize * 16.0f);
6400 
6401         // SCROLLING
6402 
6403         // Lock down maximum scrolling
6404         // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate
6405         // for right/bottom aligned items without creating a scrollbar.
6406         window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth());
6407         window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());
6408 
6409         // Apply scrolling
6410         window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);
6411         window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
6412 
6413         // DRAWING
6414 
6415         // Setup draw list and outer clipping rectangle
6416         IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0);
6417         window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
6418         PushClipRect(host_rect.Min, host_rect.Max, false);
6419 
6420         // Draw modal or window list full viewport dimming background (for other viewports we'll render them in EndFrame)
6421         ImGuiWindow* window_window_list = g.NavWindowingListWindow;
6422         const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0;
6423         const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && ((window == g.NavWindowingTargetAnim->RootWindow) || (window == window_window_list && window_window_list->Viewport != g.NavWindowingTargetAnim->Viewport));
6424         if (dim_bg_for_modal || dim_bg_for_window_list)
6425         {
6426             const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
6427             window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col);
6428         }
6429 
6430         // Draw navigation selection/windowing rectangle background
6431         if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim)
6432         {
6433             ImRect bb = window->Rect();
6434             bb.Expand(g.FontSize);
6435             if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway
6436                 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
6437         }
6438 
6439         // Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call.
6440         // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
6441         // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child.
6442         // We also disabled this when we have dimming overlay behind this specific one child.
6443         // 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.
6444         const bool is_undocked_or_docked_visible = !window->DockIsActive || window->DockTabIsVisible;
6445         if (is_undocked_or_docked_visible)
6446         {
6447             bool render_decorations_in_parent = false;
6448             if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
6449                 if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0)
6450                     render_decorations_in_parent = true;
6451             if (render_decorations_in_parent)
6452                 window->DrawList = parent_window->DrawList;
6453 
6454             // Handle title bar, scrollbar, resize grips and resize borders
6455             const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
6456             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)));
6457             RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, handle_borders_and_resize_grips, resize_grip_count, resize_grip_col, resize_grip_draw_size);
6458 
6459             if (render_decorations_in_parent)
6460                 window->DrawList = &window->DrawListInst;
6461         }
6462 
6463         // Draw navigation selection/windowing rectangle border
6464         if (g.NavWindowingTargetAnim == window)
6465         {
6466             float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
6467             ImRect bb = window->Rect();
6468             bb.Expand(g.FontSize);
6469             if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward
6470             {
6471                 bb.Expand(-g.FontSize - 1.0f);
6472                 rounding = window->WindowRounding;
6473             }
6474             window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f);
6475         }
6476 
6477         // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
6478 
6479         // Work rectangle.
6480         // Affected by window padding and border size. Used by:
6481         // - Columns() for right-most edge
6482         // - TreeNode(), CollapsingHeader() for right-most edge
6483         // - BeginTabBar() for right-most edge
6484         const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);
6485         const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);
6486         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));
6487         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));
6488         window->WorkRect.Min.x = ImFloor(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));
6489         window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
6490         window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
6491         window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
6492         window->ParentWorkRect = window->WorkRect;
6493 
6494         // [LEGACY] Content Region
6495         // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
6496         // Used by:
6497         // - Mouse wheel scrolling + many other things
6498         window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
6499         window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + decoration_up_height;
6500         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));
6501         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));
6502 
6503         // Setup drawing context
6504         // (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.)
6505         window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x;
6506         window->DC.GroupOffset.x = 0.0f;
6507         window->DC.ColumnsOffset.x = 0.0f;
6508         window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, decoration_up_height + window->WindowPadding.y - window->Scroll.y);
6509         window->DC.CursorPos = window->DC.CursorStartPos;
6510         window->DC.CursorPosPrevLine = window->DC.CursorPos;
6511         window->DC.CursorMaxPos = window->DC.CursorStartPos;
6512         window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
6513         window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
6514 
6515         window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
6516         window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
6517         window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext;
6518         window->DC.NavLayerActiveMaskNext = 0x00;
6519         window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : 0; // -V595
6520         window->DC.NavHideHighlightOneFrame = false;
6521         window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f);
6522 
6523         window->DC.MenuBarAppending = false;
6524         window->DC.MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user);
6525         window->DC.TreeDepth = 0;
6526         window->DC.TreeJumpToParentOnPopMask = 0x00;
6527         window->DC.ChildWindows.resize(0);
6528         window->DC.StateStorage = &window->StateStorage;
6529         window->DC.CurrentColumns = NULL;
6530         window->DC.LayoutType = ImGuiLayoutType_Vertical;
6531         window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
6532         window->DC.FocusCounterRegular = window->DC.FocusCounterTabStop = -1;
6533 
6534         window->DC.ItemWidth = window->ItemWidthDefault;
6535         window->DC.TextWrapPos = -1.0f; // disabled
6536         window->DC.ItemFlagsStack.resize(0);
6537         window->DC.ItemWidthStack.resize(0);
6538         window->DC.TextWrapPosStack.resize(0);
6539         window->DC.GroupStack.resize(0);
6540         window->DC.ItemFlags = parent_window ? parent_window->DC.ItemFlags : ImGuiItemFlags_Default_;
6541         if (parent_window)
6542             window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
6543 
6544         if (window->AutoFitFramesX > 0)
6545             window->AutoFitFramesX--;
6546         if (window->AutoFitFramesY > 0)
6547             window->AutoFitFramesY--;
6548 
6549         // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
6550         if (want_focus)
6551         {
6552             FocusWindow(window);
6553             NavInitWindow(window, false);
6554         }
6555 
6556         // Close requested by platform window
6557         if (p_open != NULL && window->Viewport->PlatformRequestClose && window->Viewport != GetMainViewport())
6558         {
6559             if (!window->DockIsActive || window->DockTabIsVisible)
6560             {
6561                 window->Viewport->PlatformRequestClose = false;
6562                 g.NavWindowingToggleLayer = false; // Assume user mapped PlatformRequestClose on ALT-F4 so we disable ALT for menu toggle. False positive not an issue.
6563                 IMGUI_DEBUG_LOG_VIEWPORT("Window '%s' PlatformRequestClose\n", window->Name);
6564                 *p_open = false;
6565             }
6566         }
6567 
6568         // Title bar
6569         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
6570             RenderWindowTitleBarContents(window, title_bar_rect, name, p_open);
6571 
6572         // Clear hit test shape every frame
6573         window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0;
6574 
6575         // Pressing CTRL+C while holding on a window copy its content to the clipboard
6576         // 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.
6577         // Maybe we can support CTRL+C on every element?
6578         /*
6579         if (g.ActiveId == move_id)
6580             if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
6581                 LogToClipboard();
6582         */
6583 
6584         if (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)
6585         {
6586             // Docking: Dragging a dockable window (or any of its child) turns it into a drag and drop source.
6587             // We need to do this _before_ we overwrite window->DC.LastItemId below because BeginDockableDragDropSource() also overwrites it.
6588             if ((g.MovingWindow == window) && (g.IO.ConfigDockingWithShift == g.IO.KeyShift))
6589                 if ((window->RootWindow->Flags & ImGuiWindowFlags_NoDocking) == 0)
6590                     BeginDockableDragDropSource(window);
6591 
6592             // Docking: Any dockable window can act as a target. For dock node hosts we call BeginDockableDragDropTarget() in DockNodeUpdate() instead.
6593             if (g.DragDropActive && !(flags & ImGuiWindowFlags_NoDocking))
6594                 if (g.MovingWindow == NULL || g.MovingWindow->RootWindow != window)
6595                     if ((window == window->RootWindow) && !(window->Flags & ImGuiWindowFlags_DockNodeHost))
6596                         BeginDockableDragDropTarget(window);
6597         }
6598 
6599         // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
6600         // This is useful to allow creating context menus on title bar only, etc.
6601         if (window->DockIsActive)
6602             SetLastItemData(window, window->ID, window->DockTabItemStatusFlags, window->DockTabItemRect);
6603         else
6604             SetLastItemData(window, window->MoveId, IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, title_bar_rect);
6605 
6606 #ifdef IMGUI_ENABLE_TEST_ENGINE
6607         if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
6608             IMGUI_TEST_ENGINE_ITEM_ADD(window->DC.LastItemRect, window->DC.LastItemId);
6609 #endif
6610     }
6611     else
6612     {
6613         // Append
6614         SetCurrentViewport(window, window->Viewport);
6615         SetCurrentWindow(window);
6616     }
6617 
6618     if (!(flags & ImGuiWindowFlags_DockNodeHost))
6619         PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
6620 
6621     // 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)
6622     if (first_begin_of_the_frame)
6623         window->WriteAccessed = false;
6624 
6625     window->BeginCount++;
6626     g.NextWindowData.ClearFlags();
6627 
6628     // When we are about to select this tab (which will only be visible on the _next frame_), flag it with a non-zero HiddenFramesCannotSkipItems.
6629     // This will have the important effect of actually returning true in Begin() and not setting SkipItems, allowing an earlier submission of the window contents.
6630     // This is analogous to regular windows being hidden from one frame.
6631     // 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.
6632     if (window->DockIsActive && !window->DockTabIsVisible)
6633     {
6634         if (window->LastFrameJustFocused == g.FrameCount)
6635             window->HiddenFramesCannotSkipItems = 1;
6636         else
6637             window->HiddenFramesCanSkipItems = 1;
6638     }
6639 
6640     if (flags & ImGuiWindowFlags_ChildWindow)
6641     {
6642         // Child window can be out of sight and have "negative" clip windows.
6643         // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
6644         IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0 || (window->DockIsActive));
6645         if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
6646             if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
6647                 window->HiddenFramesCanSkipItems = 1;
6648 
6649         // Hide along with parent or if parent is collapsed
6650         if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))
6651             window->HiddenFramesCanSkipItems = 1;
6652         if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0))
6653             window->HiddenFramesCannotSkipItems = 1;
6654     }
6655 
6656     // 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)
6657     if (style.Alpha <= 0.0f)
6658         window->HiddenFramesCanSkipItems = 1;
6659 
6660     // Update the Hidden flag
6661     window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0);
6662 
6663     // Update the SkipItems flag, used to early out of all items functions (no layout required)
6664     bool skip_items = false;
6665     if (window->Collapsed || !window->Active || window->Hidden)
6666         if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0)
6667             skip_items = true;
6668     window->SkipItems = skip_items;
6669 
6670     return !skip_items;
6671 }
6672 
End()6673 void ImGui::End()
6674 {
6675     ImGuiContext& g = *GImGui;
6676     ImGuiWindow* window = g.CurrentWindow;
6677 
6678     // Error checking: verify that user hasn't called End() too many times!
6679     if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow)
6680     {
6681         IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!");
6682         return;
6683     }
6684     IM_ASSERT(g.CurrentWindowStack.Size > 0);
6685 
6686     // Error checking: verify that user doesn't directly call End() on a child window.
6687     if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !window->DockIsActive)
6688         IM_ASSERT_USER_ERROR(g.WithinEndChild, "Must call EndChild() and not End()!");
6689 
6690     // Close anything that is open
6691     if (window->DC.CurrentColumns)
6692         EndColumns();
6693     if (!(window->Flags & ImGuiWindowFlags_DockNodeHost))   // Pop inner window clip rectangle
6694         PopClipRect();
6695 
6696     // Stop logging
6697     if (!(window->Flags & ImGuiWindowFlags_ChildWindow))    // FIXME: add more options for scope of logging
6698         LogFinish();
6699 
6700     // Docking: report contents sizes to parent to allow for auto-resize
6701     if (window->DockNode && window->DockTabIsVisible)
6702         if (ImGuiWindow* host_window = window->DockNode->HostWindow)         // FIXME-DOCK
6703             host_window->DC.CursorMaxPos = window->DC.CursorMaxPos + window->WindowPadding - host_window->WindowPadding;
6704 
6705     // Pop from window stack
6706     g.CurrentWindowStack.pop_back();
6707     if (window->Flags & ImGuiWindowFlags_Popup)
6708         g.BeginPopupStack.pop_back();
6709     ErrorCheckBeginEndCompareStacksSize(window, false);
6710     SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
6711     if (g.CurrentWindow)
6712         SetCurrentViewport(g.CurrentWindow, g.CurrentWindow->Viewport);
6713 }
6714 
BringWindowToFocusFront(ImGuiWindow * window)6715 void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
6716 {
6717     ImGuiContext& g = *GImGui;
6718     if (g.WindowsFocusOrder.back() == window)
6719         return;
6720     for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the top-most window
6721         if (g.WindowsFocusOrder[i] == window)
6722         {
6723             memmove(&g.WindowsFocusOrder[i], &g.WindowsFocusOrder[i + 1], (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*));
6724             g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window;
6725             break;
6726         }
6727 }
6728 
BringWindowToDisplayFront(ImGuiWindow * window)6729 void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
6730 {
6731     ImGuiContext& g = *GImGui;
6732     ImGuiWindow* current_front_window = g.Windows.back();
6733     if (current_front_window == window || current_front_window->RootWindow == window) // Cheap early out (could be better)
6734         return;
6735     for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window
6736         if (g.Windows[i] == window)
6737         {
6738             memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
6739             g.Windows[g.Windows.Size - 1] = window;
6740             break;
6741         }
6742 }
6743 
BringWindowToDisplayBack(ImGuiWindow * window)6744 void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
6745 {
6746     ImGuiContext& g = *GImGui;
6747     if (g.Windows[0] == window)
6748         return;
6749     for (int i = 0; i < g.Windows.Size; i++)
6750         if (g.Windows[i] == window)
6751         {
6752             memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
6753             g.Windows[0] = window;
6754             break;
6755         }
6756 }
6757 
6758 // Moving window to front of display and set focus (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)6759 void ImGui::FocusWindow(ImGuiWindow* window)
6760 {
6761     ImGuiContext& g = *GImGui;
6762 
6763     if (g.NavWindow != window)
6764     {
6765         g.NavWindow = window;
6766         if (window && g.NavDisableMouseHover)
6767             g.NavMousePosDirty = true;
6768         g.NavInitRequest = false;
6769         g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
6770         g.NavFocusScopeId = 0;
6771         g.NavIdIsAlive = false;
6772         g.NavLayer = ImGuiNavLayer_Main;
6773         //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL);
6774     }
6775 
6776     // Close popups if any
6777     ClosePopupsOverWindow(window, false);
6778 
6779     // Move the root window to the top of the pile
6780     IM_ASSERT(window == NULL || window->RootWindow != NULL);
6781     ImGuiWindow* focus_front_window = window ? window->RootWindowDockStop : NULL;
6782     ImGuiWindow* display_front_window = window ? window->RootWindow : NULL;
6783     ImGuiDockNode* dock_node = window ? window->DockNode : NULL;
6784     bool active_id_window_is_dock_node_host = (g.ActiveIdWindow && dock_node && dock_node->HostWindow == g.ActiveIdWindow);
6785 
6786     // Steal active widgets. Some of the cases it triggers includes:
6787     // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run.
6788     // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId)
6789     // - Using dock host items (tab, collapse button) can trigger this before we redirect the ActiveIdWindow toward the child window.
6790     if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindowDockStop != focus_front_window)
6791         if (!g.ActiveIdNoClearOnFocusLoss && !active_id_window_is_dock_node_host)
6792             ClearActiveID();
6793 
6794     // Passing NULL allow to disable keyboard focus
6795     if (!window)
6796         return;
6797     window->LastFrameJustFocused = g.FrameCount;
6798 
6799     // Select in dock node
6800     if (dock_node && dock_node->TabBar)
6801         dock_node->TabBar->SelectedTabId = dock_node->TabBar->NextSelectedTabId = window->ID;
6802 
6803     // Bring to front
6804     BringWindowToFocusFront(focus_front_window);
6805     if (((window->Flags | focus_front_window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
6806         BringWindowToDisplayFront(display_front_window);
6807 }
6808 
FocusTopMostWindowUnderOne(ImGuiWindow * under_this_window,ImGuiWindow * ignore_window)6809 void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window)
6810 {
6811     ImGuiContext& g = *GImGui;
6812 
6813     int start_idx = g.WindowsFocusOrder.Size - 1;
6814     if (under_this_window != NULL)
6815     {
6816         int under_this_window_idx = FindWindowFocusIndex(under_this_window);
6817         if (under_this_window_idx != -1)
6818             start_idx = under_this_window_idx - 1;
6819     }
6820     for (int i = start_idx; i >= 0; i--)
6821     {
6822         // 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.
6823         ImGuiWindow* window = g.WindowsFocusOrder[i];
6824         if (window != ignore_window && window->WasActive && window->RootWindowDockStop == window)
6825             if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
6826             {
6827                 // FIXME-DOCK: This is failing (lagging by one frame) for docked windows.
6828                 // If A and B are docked into window and B disappear, at the NewFrame() call site window->NavLastChildNavWindow will still point to B.
6829                 // 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)
6830                 // to tell which is the "previous" window. Or we may leverage 'LastFrameFocused/LastFrameJustFocused' and have this function handle child window itself?
6831                 ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window);
6832                 FocusWindow(focus_window);
6833                 return;
6834             }
6835     }
6836     FocusWindow(NULL);
6837 }
6838 
SetCurrentFont(ImFont * font)6839 void ImGui::SetCurrentFont(ImFont* font)
6840 {
6841     ImGuiContext& g = *GImGui;
6842     IM_ASSERT(font && font->IsLoaded());    // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
6843     IM_ASSERT(font->Scale > 0.0f);
6844     g.Font = font;
6845     g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale);
6846     g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
6847 
6848     ImFontAtlas* atlas = g.Font->ContainerAtlas;
6849     g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
6850     g.DrawListSharedData.TexUvLines = atlas->TexUvLines;
6851     g.DrawListSharedData.Font = g.Font;
6852     g.DrawListSharedData.FontSize = g.FontSize;
6853 }
6854 
PushFont(ImFont * font)6855 void ImGui::PushFont(ImFont* font)
6856 {
6857     ImGuiContext& g = *GImGui;
6858     if (!font)
6859         font = GetDefaultFont();
6860     SetCurrentFont(font);
6861     g.FontStack.push_back(font);
6862     g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
6863 }
6864 
PopFont()6865 void  ImGui::PopFont()
6866 {
6867     ImGuiContext& g = *GImGui;
6868     g.CurrentWindow->DrawList->PopTextureID();
6869     g.FontStack.pop_back();
6870     SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
6871 }
6872 
PushItemFlag(ImGuiItemFlags option,bool enabled)6873 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
6874 {
6875     ImGuiWindow* window = GetCurrentWindow();
6876     if (enabled)
6877         window->DC.ItemFlags |= option;
6878     else
6879         window->DC.ItemFlags &= ~option;
6880     window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
6881 }
6882 
PopItemFlag()6883 void ImGui::PopItemFlag()
6884 {
6885     ImGuiWindow* window = GetCurrentWindow();
6886     window->DC.ItemFlagsStack.pop_back();
6887     window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back();
6888 }
6889 
6890 // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system.
PushAllowKeyboardFocus(bool allow_keyboard_focus)6891 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
6892 {
6893     PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus);
6894 }
6895 
PopAllowKeyboardFocus()6896 void ImGui::PopAllowKeyboardFocus()
6897 {
6898     PopItemFlag();
6899 }
6900 
PushButtonRepeat(bool repeat)6901 void ImGui::PushButtonRepeat(bool repeat)
6902 {
6903     PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
6904 }
6905 
PopButtonRepeat()6906 void ImGui::PopButtonRepeat()
6907 {
6908     PopItemFlag();
6909 }
6910 
PushTextWrapPos(float wrap_pos_x)6911 void ImGui::PushTextWrapPos(float wrap_pos_x)
6912 {
6913     ImGuiWindow* window = GetCurrentWindow();
6914     window->DC.TextWrapPos = wrap_pos_x;
6915     window->DC.TextWrapPosStack.push_back(wrap_pos_x);
6916 }
6917 
PopTextWrapPos()6918 void ImGui::PopTextWrapPos()
6919 {
6920     ImGuiWindow* window = GetCurrentWindow();
6921     window->DC.TextWrapPosStack.pop_back();
6922     window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
6923 }
6924 
IsWindowChildOf(ImGuiWindow * window,ImGuiWindow * potential_parent)6925 bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
6926 {
6927     if (window->RootWindow == potential_parent)
6928         return true;
6929     while (window != NULL)
6930     {
6931         if (window == potential_parent)
6932             return true;
6933         window = window->ParentWindow;
6934     }
6935     return false;
6936 }
6937 
IsWindowHovered(ImGuiHoveredFlags flags)6938 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
6939 {
6940     IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0);   // Flags not supported by this function
6941     ImGuiContext& g = *GImGui;
6942 
6943     if (flags & ImGuiHoveredFlags_AnyWindow)
6944     {
6945         if (g.HoveredWindow == NULL)
6946             return false;
6947     }
6948     else
6949     {
6950         switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
6951         {
6952         case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
6953             if (g.HoveredWindow == NULL || g.HoveredWindow->RootWindowDockStop != g.CurrentWindow->RootWindowDockStop)
6954                 return false;
6955             break;
6956         case ImGuiHoveredFlags_RootWindow:
6957             if (g.HoveredWindow != g.CurrentWindow->RootWindowDockStop)
6958                 return false;
6959             break;
6960         case ImGuiHoveredFlags_ChildWindows:
6961             if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow))
6962                 return false;
6963             break;
6964         default:
6965             if (g.HoveredWindow != g.CurrentWindow)
6966                 return false;
6967             break;
6968         }
6969     }
6970 
6971     if (!IsWindowContentHoverable(g.HoveredWindow, flags))
6972         return false;
6973     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
6974         if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
6975             return false;
6976     return true;
6977 }
6978 
IsWindowFocused(ImGuiFocusedFlags flags)6979 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
6980 {
6981     ImGuiContext& g = *GImGui;
6982 
6983     if (flags & ImGuiFocusedFlags_AnyWindow)
6984         return g.NavWindow != NULL;
6985 
6986     IM_ASSERT(g.CurrentWindow);     // Not inside a Begin()/End()
6987     switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
6988     {
6989     case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
6990         return g.NavWindow && g.NavWindow->RootWindowDockStop == g.CurrentWindow->RootWindowDockStop;
6991     case ImGuiFocusedFlags_RootWindow:
6992         return g.NavWindow == g.CurrentWindow->RootWindowDockStop;
6993     case ImGuiFocusedFlags_ChildWindows:
6994         return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
6995     default:
6996         return g.NavWindow == g.CurrentWindow;
6997     }
6998 }
6999 
GetWindowDockID()7000 ImGuiID ImGui::GetWindowDockID()
7001 {
7002     ImGuiContext& g = *GImGui;
7003     return g.CurrentWindow->DockId;
7004 }
7005 
IsWindowDocked()7006 bool ImGui::IsWindowDocked()
7007 {
7008     ImGuiContext& g = *GImGui;
7009     return g.CurrentWindow->DockIsActive;
7010 }
7011 
7012 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
7013 // Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmatically.
7014 // If you want a window to never be focused, you may use the e.g. NoInputs flag.
IsWindowNavFocusable(ImGuiWindow * window)7015 bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
7016 {
7017     return window->Active && window == window->RootWindowDockStop && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
7018 }
7019 
GetWindowWidth()7020 float ImGui::GetWindowWidth()
7021 {
7022     ImGuiWindow* window = GImGui->CurrentWindow;
7023     return window->Size.x;
7024 }
7025 
GetWindowHeight()7026 float ImGui::GetWindowHeight()
7027 {
7028     ImGuiWindow* window = GImGui->CurrentWindow;
7029     return window->Size.y;
7030 }
7031 
GetWindowPos()7032 ImVec2 ImGui::GetWindowPos()
7033 {
7034     ImGuiContext& g = *GImGui;
7035     ImGuiWindow* window = g.CurrentWindow;
7036     return window->Pos;
7037 }
7038 
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiCond cond)7039 void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
7040 {
7041     // Test condition (NB: bit 0 is always true) and clear flags for next time
7042     if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
7043         return;
7044 
7045     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
7046     window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
7047     window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
7048 
7049     // Set
7050     const ImVec2 old_pos = window->Pos;
7051     window->Pos = ImFloor(pos);
7052     ImVec2 offset = window->Pos - old_pos;
7053     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
7054     window->DC.CursorMaxPos += offset;      // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.
7055     window->DC.CursorStartPos += offset;
7056 }
7057 
SetWindowPos(const ImVec2 & pos,ImGuiCond cond)7058 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
7059 {
7060     ImGuiWindow* window = GetCurrentWindowRead();
7061     SetWindowPos(window, pos, cond);
7062 }
7063 
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiCond cond)7064 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
7065 {
7066     if (ImGuiWindow* window = FindWindowByName(name))
7067         SetWindowPos(window, pos, cond);
7068 }
7069 
GetWindowSize()7070 ImVec2 ImGui::GetWindowSize()
7071 {
7072     ImGuiWindow* window = GetCurrentWindowRead();
7073     return window->Size;
7074 }
7075 
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiCond cond)7076 void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
7077 {
7078     // Test condition (NB: bit 0 is always true) and clear flags for next time
7079     if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
7080         return;
7081 
7082     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
7083     window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
7084 
7085     // Set
7086     if (size.x > 0.0f)
7087     {
7088         window->AutoFitFramesX = 0;
7089         window->SizeFull.x = IM_FLOOR(size.x);
7090     }
7091     else
7092     {
7093         window->AutoFitFramesX = 2;
7094         window->AutoFitOnlyGrows = false;
7095     }
7096     if (size.y > 0.0f)
7097     {
7098         window->AutoFitFramesY = 0;
7099         window->SizeFull.y = IM_FLOOR(size.y);
7100     }
7101     else
7102     {
7103         window->AutoFitFramesY = 2;
7104         window->AutoFitOnlyGrows = false;
7105     }
7106 }
7107 
SetWindowSize(const ImVec2 & size,ImGuiCond cond)7108 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
7109 {
7110     SetWindowSize(GImGui->CurrentWindow, size, cond);
7111 }
7112 
SetWindowSize(const char * name,const ImVec2 & size,ImGuiCond cond)7113 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
7114 {
7115     if (ImGuiWindow* window = FindWindowByName(name))
7116         SetWindowSize(window, size, cond);
7117 }
7118 
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiCond cond)7119 void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
7120 {
7121     // Test condition (NB: bit 0 is always true) and clear flags for next time
7122     if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
7123         return;
7124     window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
7125 
7126     // Set
7127     window->Collapsed = collapsed;
7128 }
7129 
SetWindowHitTestHole(ImGuiWindow * window,const ImVec2 & pos,const ImVec2 & size)7130 void ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size)
7131 {
7132     IM_ASSERT(window->HitTestHoleSize.x == 0);     // We don't support multiple holes/hit test filters
7133     window->HitTestHoleSize = ImVec2ih(size);
7134     window->HitTestHoleOffset = ImVec2ih(pos - window->Pos);
7135 }
7136 
SetWindowCollapsed(bool collapsed,ImGuiCond cond)7137 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
7138 {
7139     SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
7140 }
7141 
IsWindowCollapsed()7142 bool ImGui::IsWindowCollapsed()
7143 {
7144     ImGuiWindow* window = GetCurrentWindowRead();
7145     return window->Collapsed;
7146 }
7147 
IsWindowAppearing()7148 bool ImGui::IsWindowAppearing()
7149 {
7150     ImGuiWindow* window = GetCurrentWindowRead();
7151     return window->Appearing;
7152 }
7153 
SetWindowCollapsed(const char * name,bool collapsed,ImGuiCond cond)7154 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
7155 {
7156     if (ImGuiWindow* window = FindWindowByName(name))
7157         SetWindowCollapsed(window, collapsed, cond);
7158 }
7159 
SetWindowFocus()7160 void ImGui::SetWindowFocus()
7161 {
7162     FocusWindow(GImGui->CurrentWindow);
7163 }
7164 
SetWindowFocus(const char * name)7165 void ImGui::SetWindowFocus(const char* name)
7166 {
7167     if (name)
7168     {
7169         if (ImGuiWindow* window = FindWindowByName(name))
7170             FocusWindow(window);
7171     }
7172     else
7173     {
7174         FocusWindow(NULL);
7175     }
7176 }
7177 
SetNextWindowPos(const ImVec2 & pos,ImGuiCond cond,const ImVec2 & pivot)7178 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
7179 {
7180     ImGuiContext& g = *GImGui;
7181     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
7182     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos;
7183     g.NextWindowData.PosVal = pos;
7184     g.NextWindowData.PosPivotVal = pivot;
7185     g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
7186     g.NextWindowData.PosUndock = true;
7187 }
7188 
SetNextWindowSize(const ImVec2 & size,ImGuiCond cond)7189 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
7190 {
7191     ImGuiContext& g = *GImGui;
7192     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
7193     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize;
7194     g.NextWindowData.SizeVal = size;
7195     g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
7196 }
7197 
SetNextWindowSizeConstraints(const ImVec2 & size_min,const ImVec2 & size_max,ImGuiSizeCallback custom_callback,void * custom_callback_user_data)7198 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
7199 {
7200     ImGuiContext& g = *GImGui;
7201     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint;
7202     g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
7203     g.NextWindowData.SizeCallback = custom_callback;
7204     g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
7205 }
7206 
7207 // Content size = inner scrollable rectangle, padded with WindowPadding.
7208 // SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
SetNextWindowContentSize(const ImVec2 & size)7209 void ImGui::SetNextWindowContentSize(const ImVec2& size)
7210 {
7211     ImGuiContext& g = *GImGui;
7212     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize;
7213     g.NextWindowData.ContentSizeVal = size;
7214 }
7215 
SetNextWindowScroll(const ImVec2 & scroll)7216 void ImGui::SetNextWindowScroll(const ImVec2& scroll)
7217 {
7218     ImGuiContext& g = *GImGui;
7219     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasScroll;
7220     g.NextWindowData.ScrollVal = scroll;
7221 }
7222 
SetNextWindowCollapsed(bool collapsed,ImGuiCond cond)7223 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
7224 {
7225     ImGuiContext& g = *GImGui;
7226     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
7227     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed;
7228     g.NextWindowData.CollapsedVal = collapsed;
7229     g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
7230 }
7231 
SetNextWindowFocus()7232 void ImGui::SetNextWindowFocus()
7233 {
7234     ImGuiContext& g = *GImGui;
7235     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus;
7236 }
7237 
SetNextWindowBgAlpha(float alpha)7238 void ImGui::SetNextWindowBgAlpha(float alpha)
7239 {
7240     ImGuiContext& g = *GImGui;
7241     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha;
7242     g.NextWindowData.BgAlphaVal = alpha;
7243 }
7244 
SetNextWindowViewport(ImGuiID id)7245 void ImGui::SetNextWindowViewport(ImGuiID id)
7246 {
7247     ImGuiContext& g = *GImGui;
7248     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasViewport;
7249     g.NextWindowData.ViewportId = id;
7250 }
7251 
SetNextWindowDockID(ImGuiID id,ImGuiCond cond)7252 void ImGui::SetNextWindowDockID(ImGuiID id, ImGuiCond cond)
7253 {
7254     ImGuiContext& g = *GImGui;
7255     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasDock;
7256     g.NextWindowData.DockCond = cond ? cond : ImGuiCond_Always;
7257     g.NextWindowData.DockId = id;
7258 }
7259 
SetNextWindowClass(const ImGuiWindowClass * window_class)7260 void ImGui::SetNextWindowClass(const ImGuiWindowClass* window_class)
7261 {
7262     ImGuiContext& g = *GImGui;
7263     IM_ASSERT((window_class->ViewportFlagsOverrideSet & window_class->ViewportFlagsOverrideClear) == 0); // Cannot set both set and clear for the same bit
7264     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasWindowClass;
7265     g.NextWindowData.WindowClass = *window_class;
7266 }
7267 
GetWindowDrawList()7268 ImDrawList* ImGui::GetWindowDrawList()
7269 {
7270     ImGuiWindow* window = GetCurrentWindow();
7271     return window->DrawList;
7272 }
7273 
GetWindowDpiScale()7274 float ImGui::GetWindowDpiScale()
7275 {
7276     ImGuiContext& g = *GImGui;
7277     return g.CurrentDpiScale;
7278 }
7279 
GetWindowViewport()7280 ImGuiViewport* ImGui::GetWindowViewport()
7281 {
7282     ImGuiContext& g = *GImGui;
7283     IM_ASSERT(g.CurrentViewport != NULL && g.CurrentViewport == g.CurrentWindow->Viewport);
7284     return g.CurrentViewport;
7285 }
7286 
GetFont()7287 ImFont* ImGui::GetFont()
7288 {
7289     return GImGui->Font;
7290 }
7291 
GetFontSize()7292 float ImGui::GetFontSize()
7293 {
7294     return GImGui->FontSize;
7295 }
7296 
GetFontTexUvWhitePixel()7297 ImVec2 ImGui::GetFontTexUvWhitePixel()
7298 {
7299     return GImGui->DrawListSharedData.TexUvWhitePixel;
7300 }
7301 
SetWindowFontScale(float scale)7302 void ImGui::SetWindowFontScale(float scale)
7303 {
7304     IM_ASSERT(scale > 0.0f);
7305     ImGuiContext& g = *GImGui;
7306     ImGuiWindow* window = GetCurrentWindow();
7307     window->FontWindowScale = scale;
7308     g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
7309 }
7310 
ActivateItem(ImGuiID id)7311 void ImGui::ActivateItem(ImGuiID id)
7312 {
7313     ImGuiContext& g = *GImGui;
7314     g.NavNextActivateId = id;
7315 }
7316 
7317 // Note: this is storing in same stack as IDStack, so Push/Pop mismatch will be reported there.
PushFocusScope(ImGuiID id)7318 void ImGui::PushFocusScope(ImGuiID id)
7319 {
7320     ImGuiContext& g = *GImGui;
7321     ImGuiWindow* window = g.CurrentWindow;
7322     window->IDStack.push_back(window->DC.NavFocusScopeIdCurrent);
7323     window->DC.NavFocusScopeIdCurrent = id;
7324 }
7325 
PopFocusScope()7326 void ImGui::PopFocusScope()
7327 {
7328     ImGuiContext& g = *GImGui;
7329     ImGuiWindow* window = g.CurrentWindow;
7330     window->DC.NavFocusScopeIdCurrent = window->IDStack.back();
7331     window->IDStack.pop_back();
7332 }
7333 
SetKeyboardFocusHere(int offset)7334 void ImGui::SetKeyboardFocusHere(int offset)
7335 {
7336     IM_ASSERT(offset >= -1);    // -1 is allowed but not below
7337     ImGuiContext& g = *GImGui;
7338     ImGuiWindow* window = g.CurrentWindow;
7339     g.FocusRequestNextWindow = window;
7340     g.FocusRequestNextCounterRegular = window->DC.FocusCounterRegular + 1 + offset;
7341     g.FocusRequestNextCounterTabStop = INT_MAX;
7342 }
7343 
SetItemDefaultFocus()7344 void ImGui::SetItemDefaultFocus()
7345 {
7346     ImGuiContext& g = *GImGui;
7347     ImGuiWindow* window = g.CurrentWindow;
7348     if (!window->Appearing)
7349         return;
7350     if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent)
7351     {
7352         g.NavInitRequest = false;
7353         g.NavInitResultId = g.NavWindow->DC.LastItemId;
7354         g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos);
7355         NavUpdateAnyRequestFlag();
7356         if (!IsItemVisible())
7357             SetScrollHereY();
7358     }
7359 }
7360 
SetStateStorage(ImGuiStorage * tree)7361 void ImGui::SetStateStorage(ImGuiStorage* tree)
7362 {
7363     ImGuiWindow* window = GImGui->CurrentWindow;
7364     window->DC.StateStorage = tree ? tree : &window->StateStorage;
7365 }
7366 
GetStateStorage()7367 ImGuiStorage* ImGui::GetStateStorage()
7368 {
7369     ImGuiWindow* window = GImGui->CurrentWindow;
7370     return window->DC.StateStorage;
7371 }
7372 
PushID(const char * str_id)7373 void ImGui::PushID(const char* str_id)
7374 {
7375     ImGuiContext& g = *GImGui;
7376     ImGuiWindow* window = g.CurrentWindow;
7377     ImGuiID id = window->GetIDNoKeepAlive(str_id);
7378     window->IDStack.push_back(id);
7379 }
7380 
PushID(const char * str_id_begin,const char * str_id_end)7381 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
7382 {
7383     ImGuiContext& g = *GImGui;
7384     ImGuiWindow* window = g.CurrentWindow;
7385     ImGuiID id = window->GetIDNoKeepAlive(str_id_begin, str_id_end);
7386     window->IDStack.push_back(id);
7387 }
7388 
PushID(const void * ptr_id)7389 void ImGui::PushID(const void* ptr_id)
7390 {
7391     ImGuiContext& g = *GImGui;
7392     ImGuiWindow* window = g.CurrentWindow;
7393     ImGuiID id = window->GetIDNoKeepAlive(ptr_id);
7394     window->IDStack.push_back(id);
7395 }
7396 
PushID(int int_id)7397 void ImGui::PushID(int int_id)
7398 {
7399     ImGuiContext& g = *GImGui;
7400     ImGuiWindow* window = g.CurrentWindow;
7401     ImGuiID id = window->GetIDNoKeepAlive(int_id);
7402     window->IDStack.push_back(id);
7403 }
7404 
7405 // Push a given id value ignoring the ID stack as a seed.
PushOverrideID(ImGuiID id)7406 void ImGui::PushOverrideID(ImGuiID id)
7407 {
7408     ImGuiContext& g = *GImGui;
7409     ImGuiWindow* window = g.CurrentWindow;
7410     window->IDStack.push_back(id);
7411 }
7412 
PopID()7413 void ImGui::PopID()
7414 {
7415     ImGuiWindow* window = GImGui->CurrentWindow;
7416     window->IDStack.pop_back();
7417 }
7418 
GetID(const char * str_id)7419 ImGuiID ImGui::GetID(const char* str_id)
7420 {
7421     ImGuiWindow* window = GImGui->CurrentWindow;
7422     return window->GetID(str_id);
7423 }
7424 
GetID(const char * str_id_begin,const char * str_id_end)7425 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
7426 {
7427     ImGuiWindow* window = GImGui->CurrentWindow;
7428     return window->GetID(str_id_begin, str_id_end);
7429 }
7430 
GetID(const void * ptr_id)7431 ImGuiID ImGui::GetID(const void* ptr_id)
7432 {
7433     ImGuiWindow* window = GImGui->CurrentWindow;
7434     return window->GetID(ptr_id);
7435 }
7436 
IsRectVisible(const ImVec2 & size)7437 bool ImGui::IsRectVisible(const ImVec2& size)
7438 {
7439     ImGuiWindow* window = GImGui->CurrentWindow;
7440     return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
7441 }
7442 
IsRectVisible(const ImVec2 & rect_min,const ImVec2 & rect_max)7443 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
7444 {
7445     ImGuiWindow* window = GImGui->CurrentWindow;
7446     return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
7447 }
7448 
7449 
7450 //-----------------------------------------------------------------------------
7451 // [SECTION] ERROR CHECKING
7452 //-----------------------------------------------------------------------------
7453 
7454 // Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui.
7455 // Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
7456 // If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. your user code
7457 // may see different structures than what imgui.cpp sees, which is problematic.
7458 // 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)7459 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)
7460 {
7461     bool error = false;
7462     if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); }
7463     if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
7464     if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
7465     if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
7466     if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
7467     if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
7468     if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); }
7469     return !error;
7470 }
7471 
ErrorCheckNewFrameSanityChecks()7472 static void ImGui::ErrorCheckNewFrameSanityChecks()
7473 {
7474     ImGuiContext& g = *GImGui;
7475 
7476     // Check user IM_ASSERT macro
7477     // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means you assert macro is incorrectly defined!
7478     //  If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block.
7479     //  This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.)
7480     // #define IM_ASSERT(EXPR)   SomeCode(EXPR); SomeMoreCode();                    // Wrong!
7481     // #define IM_ASSERT(EXPR)   do { SomeCode(EXPR); SomeMoreCode(); } while (0)   // Correct!
7482     if (true) IM_ASSERT(1); else IM_ASSERT(0);
7483 
7484     // Check user data
7485     // (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)
7486     IM_ASSERT(g.Initialized);
7487     IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0)              && "Need a positive DeltaTime!");
7488     IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount)  && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
7489     IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f  && "Invalid DisplaySize value!");
7490     IM_ASSERT(g.IO.Fonts->Fonts.Size > 0                                && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
7491     IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded()                          && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
7492     IM_ASSERT(g.Style.CurveTessellationTol > 0.0f                       && "Invalid style setting!");
7493     IM_ASSERT(g.Style.CircleSegmentMaxError > 0.0f                      && "Invalid style setting!");
7494     IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f            && "Invalid style setting. Alpha cannot be negative (allows us to avoid a few clamps in color computations)!");
7495     IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting.");
7496     IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
7497     for (int n = 0; n < ImGuiKey_COUNT; n++)
7498         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)");
7499 
7500     // Perform simple check: required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only recently added in 1.60 WIP)
7501     if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
7502         IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
7503 
7504     // Perform simple check: the beta io.ConfigWindowsResizeFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
7505     if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
7506         g.IO.ConfigWindowsResizeFromEdges = false;
7507 
7508     // 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.
7509     if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_DockingEnable) == 0)
7510         IM_ASSERT(0 && "Please set DockingEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!");
7511     if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_ViewportsEnable) == 0)
7512         IM_ASSERT(0 && "Please set ViewportsEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!");
7513 
7514     // Perform simple checks: multi-viewport and platform windows support
7515     if (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
7516     {
7517         if ((g.IO.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasViewports))
7518         {
7519             IM_ASSERT((g.FrameCount == 0 || g.FrameCount == g.FrameCountPlatformEnded) && "Forgot to call UpdatePlatformWindows() in main loop after EndFrame()? Check examples/ applications for reference.");
7520             IM_ASSERT(g.PlatformIO.Platform_CreateWindow  != NULL && "Platform init didn't install handlers?");
7521             IM_ASSERT(g.PlatformIO.Platform_DestroyWindow != NULL && "Platform init didn't install handlers?");
7522             IM_ASSERT(g.PlatformIO.Platform_GetWindowPos  != NULL && "Platform init didn't install handlers?");
7523             IM_ASSERT(g.PlatformIO.Platform_SetWindowPos  != NULL && "Platform init didn't install handlers?");
7524             IM_ASSERT(g.PlatformIO.Platform_GetWindowSize != NULL && "Platform init didn't install handlers?");
7525             IM_ASSERT(g.PlatformIO.Platform_SetWindowSize != NULL && "Platform init didn't install handlers?");
7526             IM_ASSERT(g.PlatformIO.Monitors.Size > 0 && "Platform init didn't setup Monitors list?");
7527             IM_ASSERT((g.Viewports[0]->PlatformUserData != NULL || g.Viewports[0]->PlatformHandle != NULL) && "Platform init didn't setup main viewport.");
7528             if (g.IO.ConfigDockingTransparentPayload && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
7529                 IM_ASSERT(g.PlatformIO.Platform_SetWindowAlpha != NULL && "Platform_SetWindowAlpha handler is required to use io.ConfigDockingTransparent!");
7530 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
7531             IM_ASSERT(g.IO.RenderDrawListsFn == NULL);  // Call ImGui::Render() then pass ImGui::GetDrawData() yourself to your render function!
7532 #endif
7533         }
7534         else
7535         {
7536             // Disable feature, our back-ends do not support it
7537             g.IO.ConfigFlags &= ~ImGuiConfigFlags_ViewportsEnable;
7538         }
7539 
7540         // Perform simple checks on platform monitor data + compute a total bounding box for quick early outs
7541         for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++)
7542         {
7543             ImGuiPlatformMonitor& mon = g.PlatformIO.Monitors[monitor_n];
7544 	    IM_UNUSED(mon); // [Bruno Levy] silence a warning
7545             IM_ASSERT(mon.MainSize.x > 0.0f && mon.MainSize.y > 0.0f && "Monitor main bounds not setup properly.");
7546             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.");
7547             IM_ASSERT(mon.DpiScale != 0.0f);
7548         }
7549     }
7550 }
7551 
ErrorCheckEndFrameSanityChecks()7552 static void ImGui::ErrorCheckEndFrameSanityChecks()
7553 {
7554     ImGuiContext& g = *GImGui;
7555 
7556     // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame()
7557     // One possible reason leading to this assert is that your back-ends update inputs _AFTER_ NewFrame().
7558     const ImGuiKeyModFlags expected_key_mod_flags = GetMergedKeyModFlags();
7559     IM_ASSERT(g.IO.KeyMods == expected_key_mod_flags && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
7560     IM_UNUSED(expected_key_mod_flags);
7561 
7562     // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you
7563     // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API).
7564     if (g.CurrentWindowStack.Size != 1)
7565     {
7566         if (g.CurrentWindowStack.Size > 1)
7567         {
7568             IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?");
7569             while (g.CurrentWindowStack.Size > 1)
7570                 End();
7571         }
7572         else
7573         {
7574             IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?");
7575         }
7576     }
7577 }
7578 
7579 // Save and compare stack sizes on Begin()/End() to detect usage errors
7580 // Begin() calls this with write=true
7581 // End() calls this with write=false
ErrorCheckBeginEndCompareStacksSize(ImGuiWindow * window,bool write)7582 static void ImGui::ErrorCheckBeginEndCompareStacksSize(ImGuiWindow* window, bool write)
7583 {
7584     ImGuiContext& g = *GImGui;
7585     short* p = &window->DC.StackSizesBackup[0];
7586 
7587     // Window stacks
7588     // NOT checking: DC.ItemWidth, DC.AllowKeyboardFocus, DC.ButtonRepeat, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin)
7589     { int n = window->IDStack.Size;       if (write) *p = (short)n; else IM_ASSERT(*p == n && "PushID/PopID or TreeNode/TreePop Mismatch!");   p++; }    // Too few or too many PopID()/TreePop()
7590     { int n = window->DC.GroupStack.Size; if (write) *p = (short)n; else IM_ASSERT(*p == n && "BeginGroup/EndGroup Mismatch!");                p++; }    // Too few or too many EndGroup()
7591 
7592     // Global stacks
7593     // 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.
7594     { int n = g.BeginPopupStack.Size;     if (write) *p = (short)n; else IM_ASSERT(*p == n && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch!"); p++; }// Too few or too many EndMenu()/EndPopup()
7595     { int n = g.ColorModifiers.Size;      if (write) *p = (short)n; else IM_ASSERT(*p >= n && "PushStyleColor/PopStyleColor Mismatch!");       p++; }    // Too few or too many PopStyleColor()
7596     { int n = g.StyleModifiers.Size;      if (write) *p = (short)n; else IM_ASSERT(*p >= n && "PushStyleVar/PopStyleVar Mismatch!");           p++; }    // Too few or too many PopStyleVar()
7597     { int n = g.FontStack.Size;           if (write) *p = (short)n; else IM_ASSERT(*p >= n && "PushFont/PopFont Mismatch!");                   p++; }    // Too few or too many PopFont()
7598     IM_ASSERT(p == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
7599 }
7600 
7601 
7602 //-----------------------------------------------------------------------------
7603 // [SECTION] LAYOUT
7604 //-----------------------------------------------------------------------------
7605 // - ItemSize()
7606 // - ItemAdd()
7607 // - SameLine()
7608 // - GetCursorScreenPos()
7609 // - SetCursorScreenPos()
7610 // - GetCursorPos(), GetCursorPosX(), GetCursorPosY()
7611 // - SetCursorPos(), SetCursorPosX(), SetCursorPosY()
7612 // - GetCursorStartPos()
7613 // - Indent()
7614 // - Unindent()
7615 // - SetNextItemWidth()
7616 // - PushItemWidth()
7617 // - PushMultiItemsWidths()
7618 // - PopItemWidth()
7619 // - CalcItemWidth()
7620 // - CalcItemSize()
7621 // - GetTextLineHeight()
7622 // - GetTextLineHeightWithSpacing()
7623 // - GetFrameHeight()
7624 // - GetFrameHeightWithSpacing()
7625 // - GetContentRegionMax()
7626 // - GetContentRegionMaxAbs() [Internal]
7627 // - GetContentRegionAvail(),
7628 // - GetWindowContentRegionMin(), GetWindowContentRegionMax()
7629 // - GetWindowContentRegionWidth()
7630 // - BeginGroup()
7631 // - EndGroup()
7632 // Also see in imgui_widgets: tab bars, columns.
7633 //-----------------------------------------------------------------------------
7634 
7635 // Advance cursor given item size for layout.
7636 // Register minimum needed size so it can extend the bounding box used for auto-fit calculation.
7637 // 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)7638 void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
7639 {
7640     ImGuiContext& g = *GImGui;
7641     ImGuiWindow* window = g.CurrentWindow;
7642     if (window->SkipItems)
7643         return;
7644 
7645     // We increase the height in this function to accommodate for baseline offset.
7646     // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
7647     // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
7648     const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
7649     const float line_height = ImMax(window->DC.CurrLineSize.y, size.y + offset_to_match_baseline_y);
7650 
7651     // Always align ourselves on pixel boundaries
7652     //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]
7653     window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
7654     window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y;
7655     window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);    // Next line
7656     window->DC.CursorPos.y = IM_FLOOR(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y);        // Next line
7657     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
7658     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
7659     //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
7660 
7661     window->DC.PrevLineSize.y = line_height;
7662     window->DC.CurrLineSize.y = 0.0f;
7663     window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
7664     window->DC.CurrLineTextBaseOffset = 0.0f;
7665 
7666     // Horizontal layout mode
7667     if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
7668         SameLine();
7669 }
7670 
ItemSize(const ImRect & bb,float text_baseline_y)7671 void ImGui::ItemSize(const ImRect& bb, float text_baseline_y)
7672 {
7673     ItemSize(bb.GetSize(), text_baseline_y);
7674 }
7675 
7676 // Declare item bounding box for clipping and interaction.
7677 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
7678 // 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)7679 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg)
7680 {
7681     ImGuiContext& g = *GImGui;
7682     ImGuiWindow* window = g.CurrentWindow;
7683 
7684     if (id != 0)
7685     {
7686         // Navigation processing runs prior to clipping early-out
7687         //  (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
7688         //  (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
7689         //      unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
7690         //      thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
7691         //      We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
7692         //      to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
7693         // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
7694         // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
7695         window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask;
7696         if (g.NavId == id || g.NavAnyRequest)
7697             if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
7698                 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
7699                     NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
7700 
7701         // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd()
7702 #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
7703         if (id == g.DebugItemPickerBreakId)
7704         {
7705             IM_DEBUG_BREAK();
7706             g.DebugItemPickerBreakId = 0;
7707         }
7708 #endif
7709     }
7710 
7711     // Equivalent to calling SetLastItemData()
7712     window->DC.LastItemId = id;
7713     window->DC.LastItemRect = bb;
7714     window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None;
7715     g.NextItemData.Flags = ImGuiNextItemDataFlags_None;
7716 
7717 #ifdef IMGUI_ENABLE_TEST_ENGINE
7718     if (id != 0)
7719         IMGUI_TEST_ENGINE_ITEM_ADD(nav_bb_arg ? *nav_bb_arg : bb, id);
7720 #endif
7721 
7722     // Clipping test
7723     const bool is_clipped = IsClippedEx(bb, id, false);
7724     if (is_clipped)
7725         return false;
7726     //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
7727 
7728     // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
7729     if (IsMouseHoveringRect(bb.Min, bb.Max))
7730         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect;
7731     return true;
7732 }
7733 
7734 // Gets back to previous line and continue with horizontal layout
7735 //      offset_from_start_x == 0 : follow right after previous item
7736 //      offset_from_start_x != 0 : align to specified x position (relative to window/group left)
7737 //      spacing_w < 0            : use default spacing if pos_x == 0, no spacing if pos_x != 0
7738 //      spacing_w >= 0           : enforce spacing amount
SameLine(float offset_from_start_x,float spacing_w)7739 void ImGui::SameLine(float offset_from_start_x, float spacing_w)
7740 {
7741     ImGuiWindow* window = GetCurrentWindow();
7742     if (window->SkipItems)
7743         return;
7744 
7745     ImGuiContext& g = *GImGui;
7746     if (offset_from_start_x != 0.0f)
7747     {
7748         if (spacing_w < 0.0f) spacing_w = 0.0f;
7749         window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
7750         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
7751     }
7752     else
7753     {
7754         if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
7755         window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
7756         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
7757     }
7758     window->DC.CurrLineSize = window->DC.PrevLineSize;
7759     window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
7760 }
7761 
GetCursorScreenPos()7762 ImVec2 ImGui::GetCursorScreenPos()
7763 {
7764     ImGuiWindow* window = GetCurrentWindowRead();
7765     return window->DC.CursorPos;
7766 }
7767 
SetCursorScreenPos(const ImVec2 & pos)7768 void ImGui::SetCursorScreenPos(const ImVec2& pos)
7769 {
7770     ImGuiWindow* window = GetCurrentWindow();
7771     window->DC.CursorPos = pos;
7772     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
7773 }
7774 
7775 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
7776 // 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()7777 ImVec2 ImGui::GetCursorPos()
7778 {
7779     ImGuiWindow* window = GetCurrentWindowRead();
7780     return window->DC.CursorPos - window->Pos + window->Scroll;
7781 }
7782 
GetCursorPosX()7783 float ImGui::GetCursorPosX()
7784 {
7785     ImGuiWindow* window = GetCurrentWindowRead();
7786     return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
7787 }
7788 
GetCursorPosY()7789 float ImGui::GetCursorPosY()
7790 {
7791     ImGuiWindow* window = GetCurrentWindowRead();
7792     return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
7793 }
7794 
SetCursorPos(const ImVec2 & local_pos)7795 void ImGui::SetCursorPos(const ImVec2& local_pos)
7796 {
7797     ImGuiWindow* window = GetCurrentWindow();
7798     window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
7799     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
7800 }
7801 
SetCursorPosX(float x)7802 void ImGui::SetCursorPosX(float x)
7803 {
7804     ImGuiWindow* window = GetCurrentWindow();
7805     window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
7806     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
7807 }
7808 
SetCursorPosY(float y)7809 void ImGui::SetCursorPosY(float y)
7810 {
7811     ImGuiWindow* window = GetCurrentWindow();
7812     window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
7813     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
7814 }
7815 
GetCursorStartPos()7816 ImVec2 ImGui::GetCursorStartPos()
7817 {
7818     ImGuiWindow* window = GetCurrentWindowRead();
7819     return window->DC.CursorStartPos - window->Pos;
7820 }
7821 
Indent(float indent_w)7822 void ImGui::Indent(float indent_w)
7823 {
7824     ImGuiContext& g = *GImGui;
7825     ImGuiWindow* window = GetCurrentWindow();
7826     window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
7827     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
7828 }
7829 
Unindent(float indent_w)7830 void ImGui::Unindent(float indent_w)
7831 {
7832     ImGuiContext& g = *GImGui;
7833     ImGuiWindow* window = GetCurrentWindow();
7834     window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
7835     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
7836 }
7837 
7838 // Affect large frame+labels widgets only.
SetNextItemWidth(float item_width)7839 void ImGui::SetNextItemWidth(float item_width)
7840 {
7841     ImGuiContext& g = *GImGui;
7842     g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasWidth;
7843     g.NextItemData.Width = item_width;
7844 }
7845 
PushItemWidth(float item_width)7846 void ImGui::PushItemWidth(float item_width)
7847 {
7848     ImGuiContext& g = *GImGui;
7849     ImGuiWindow* window = g.CurrentWindow;
7850     window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
7851     window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
7852     g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
7853 }
7854 
PushMultiItemsWidths(int components,float w_full)7855 void ImGui::PushMultiItemsWidths(int components, float w_full)
7856 {
7857     ImGuiContext& g = *GImGui;
7858     ImGuiWindow* window = g.CurrentWindow;
7859     const ImGuiStyle& style = g.Style;
7860     const float w_item_one  = ImMax(1.0f, IM_FLOOR((w_full - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components));
7861     const float w_item_last = ImMax(1.0f, IM_FLOOR(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components - 1)));
7862     window->DC.ItemWidthStack.push_back(w_item_last);
7863     for (int i = 0; i < components - 1; i++)
7864         window->DC.ItemWidthStack.push_back(w_item_one);
7865     window->DC.ItemWidth = window->DC.ItemWidthStack.back();
7866     g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
7867 }
7868 
PopItemWidth()7869 void ImGui::PopItemWidth()
7870 {
7871     ImGuiWindow* window = GetCurrentWindow();
7872     window->DC.ItemWidthStack.pop_back();
7873     window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
7874 }
7875 
7876 // Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().
7877 // The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()
CalcItemWidth()7878 float ImGui::CalcItemWidth()
7879 {
7880     ImGuiContext& g = *GImGui;
7881     ImGuiWindow* window = g.CurrentWindow;
7882     float w;
7883     if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth)
7884         w = g.NextItemData.Width;
7885     else
7886         w = window->DC.ItemWidth;
7887     if (w < 0.0f)
7888     {
7889         float region_max_x = GetContentRegionMaxAbs().x;
7890         w = ImMax(1.0f, region_max_x - window->DC.CursorPos.x + w);
7891     }
7892     w = IM_FLOOR(w);
7893     return w;
7894 }
7895 
7896 // [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().
7897 // Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.
7898 // Note that only CalcItemWidth() is publicly exposed.
7899 // 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)7900 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)
7901 {
7902     ImGuiWindow* window = GImGui->CurrentWindow;
7903 
7904     ImVec2 region_max;
7905     if (size.x < 0.0f || size.y < 0.0f)
7906         region_max = GetContentRegionMaxAbs();
7907 
7908     if (size.x == 0.0f)
7909         size.x = default_w;
7910     else if (size.x < 0.0f)
7911         size.x = ImMax(4.0f, region_max.x - window->DC.CursorPos.x + size.x);
7912 
7913     if (size.y == 0.0f)
7914         size.y = default_h;
7915     else if (size.y < 0.0f)
7916         size.y = ImMax(4.0f, region_max.y - window->DC.CursorPos.y + size.y);
7917 
7918     return size;
7919 }
7920 
GetTextLineHeight()7921 float ImGui::GetTextLineHeight()
7922 {
7923     ImGuiContext& g = *GImGui;
7924     return g.FontSize;
7925 }
7926 
GetTextLineHeightWithSpacing()7927 float ImGui::GetTextLineHeightWithSpacing()
7928 {
7929     ImGuiContext& g = *GImGui;
7930     return g.FontSize + g.Style.ItemSpacing.y;
7931 }
7932 
GetFrameHeight()7933 float ImGui::GetFrameHeight()
7934 {
7935     ImGuiContext& g = *GImGui;
7936     return g.FontSize + g.Style.FramePadding.y * 2.0f;
7937 }
7938 
GetFrameHeightWithSpacing()7939 float ImGui::GetFrameHeightWithSpacing()
7940 {
7941     ImGuiContext& g = *GImGui;
7942     return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
7943 }
7944 
7945 // 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!
7946 
7947 // FIXME: This is in window space (not screen space!).
GetContentRegionMax()7948 ImVec2 ImGui::GetContentRegionMax()
7949 {
7950     ImGuiContext& g = *GImGui;
7951     ImGuiWindow* window = g.CurrentWindow;
7952     ImVec2 mx = window->ContentRegionRect.Max - window->Pos;
7953     if (window->DC.CurrentColumns)
7954         mx.x = window->WorkRect.Max.x - window->Pos.x;
7955     return mx;
7956 }
7957 
7958 // [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features.
GetContentRegionMaxAbs()7959 ImVec2 ImGui::GetContentRegionMaxAbs()
7960 {
7961     ImGuiContext& g = *GImGui;
7962     ImGuiWindow* window = g.CurrentWindow;
7963     ImVec2 mx = window->ContentRegionRect.Max;
7964     if (window->DC.CurrentColumns)
7965         mx.x = window->WorkRect.Max.x;
7966     return mx;
7967 }
7968 
GetContentRegionAvail()7969 ImVec2 ImGui::GetContentRegionAvail()
7970 {
7971     ImGuiWindow* window = GImGui->CurrentWindow;
7972     return GetContentRegionMaxAbs() - window->DC.CursorPos;
7973 }
7974 
7975 // In window space (not screen space!)
GetWindowContentRegionMin()7976 ImVec2 ImGui::GetWindowContentRegionMin()
7977 {
7978     ImGuiWindow* window = GImGui->CurrentWindow;
7979     return window->ContentRegionRect.Min - window->Pos;
7980 }
7981 
GetWindowContentRegionMax()7982 ImVec2 ImGui::GetWindowContentRegionMax()
7983 {
7984     ImGuiWindow* window = GImGui->CurrentWindow;
7985     return window->ContentRegionRect.Max - window->Pos;
7986 }
7987 
GetWindowContentRegionWidth()7988 float ImGui::GetWindowContentRegionWidth()
7989 {
7990     ImGuiWindow* window = GImGui->CurrentWindow;
7991     return window->ContentRegionRect.GetWidth();
7992 }
7993 
7994 // Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.)
BeginGroup()7995 void ImGui::BeginGroup()
7996 {
7997     ImGuiContext& g = *GImGui;
7998     ImGuiWindow* window = GetCurrentWindow();
7999 
8000     window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
8001     ImGuiGroupData& group_data = window->DC.GroupStack.back();
8002     group_data.BackupCursorPos = window->DC.CursorPos;
8003     group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
8004     group_data.BackupIndent = window->DC.Indent;
8005     group_data.BackupGroupOffset = window->DC.GroupOffset;
8006     group_data.BackupCurrLineSize = window->DC.CurrLineSize;
8007     group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;
8008     group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
8009     group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
8010     group_data.EmitItem = true;
8011 
8012     window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
8013     window->DC.Indent = window->DC.GroupOffset;
8014     window->DC.CursorMaxPos = window->DC.CursorPos;
8015     window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
8016     if (g.LogEnabled)
8017         g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return
8018 }
8019 
EndGroup()8020 void ImGui::EndGroup()
8021 {
8022     ImGuiContext& g = *GImGui;
8023     ImGuiWindow* window = GetCurrentWindow();
8024     IM_ASSERT(!window->DC.GroupStack.empty());  // Mismatched BeginGroup()/EndGroup() calls
8025 
8026     ImGuiGroupData& group_data = window->DC.GroupStack.back();
8027 
8028     ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos));
8029 
8030     window->DC.CursorPos = group_data.BackupCursorPos;
8031     window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
8032     window->DC.Indent = group_data.BackupIndent;
8033     window->DC.GroupOffset = group_data.BackupGroupOffset;
8034     window->DC.CurrLineSize = group_data.BackupCurrLineSize;
8035     window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;
8036     if (g.LogEnabled)
8037         g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return
8038 
8039     if (!group_data.EmitItem)
8040     {
8041         window->DC.GroupStack.pop_back();
8042         return;
8043     }
8044 
8045     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.
8046     ItemSize(group_bb.GetSize());
8047     ItemAdd(group_bb, 0);
8048 
8049     // 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.
8050     // 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.
8051     // Also if you grep for LastItemId you'll notice it is only used in that context.
8052     // (The tests not symmetrical because ActiveIdIsAlive is an ID itself, in order to be able to handle ActiveId being overwritten during the frame.)
8053     const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId;
8054     const bool group_contains_prev_active_id = !group_data.BackupActiveIdPreviousFrameIsAlive && g.ActiveIdPreviousFrameIsAlive;
8055     if (group_contains_curr_active_id)
8056         window->DC.LastItemId = g.ActiveId;
8057     else if (group_contains_prev_active_id)
8058         window->DC.LastItemId = g.ActiveIdPreviousFrame;
8059     window->DC.LastItemRect = group_bb;
8060 
8061     // Forward Edited flag
8062     if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame)
8063         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
8064 
8065     // Forward Deactivated flag
8066     window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDeactivated;
8067     if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame)
8068         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Deactivated;
8069 
8070     window->DC.GroupStack.pop_back();
8071     //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255));   // [Debug]
8072 }
8073 
8074 
8075 //-----------------------------------------------------------------------------
8076 // [SECTION] SCROLLING
8077 //-----------------------------------------------------------------------------
8078 
CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow * window)8079 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
8080 {
8081     ImVec2 scroll = window->Scroll;
8082     if (window->ScrollTarget.x < FLT_MAX)
8083     {
8084         float cr_x = window->ScrollTargetCenterRatio.x;
8085         float target_x = window->ScrollTarget.x;
8086         scroll.x = target_x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x);
8087     }
8088     if (window->ScrollTarget.y < FLT_MAX)
8089     {
8090         float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
8091         float cr_y = window->ScrollTargetCenterRatio.y;
8092         float target_y = window->ScrollTarget.y;
8093         scroll.y = target_y - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y - decoration_up_height);
8094     }
8095     scroll.x = IM_FLOOR(ImMax(scroll.x, 0.0f));
8096     scroll.y = IM_FLOOR(ImMax(scroll.y, 0.0f));
8097     if (!window->Collapsed && !window->SkipItems)
8098     {
8099         scroll.x = ImMin(scroll.x, window->ScrollMax.x);
8100         scroll.y = ImMin(scroll.y, window->ScrollMax.y);
8101     }
8102     return scroll;
8103 }
8104 
8105 // Scroll to keep newly navigated item fully into view
ScrollToBringRectIntoView(ImGuiWindow * window,const ImRect & item_rect)8106 ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect)
8107 {
8108     ImGuiContext& g = *GImGui;
8109     ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));
8110     //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
8111 
8112     ImVec2 delta_scroll;
8113     if (!window_rect.Contains(item_rect))
8114     {
8115         if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x)
8116             SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x - g.Style.ItemSpacing.x, 0.0f);
8117         else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x)
8118             SetScrollFromPosX(window, item_rect.Max.x - window->Pos.x + g.Style.ItemSpacing.x, 1.0f);
8119         if (item_rect.Min.y < window_rect.Min.y)
8120             SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y - g.Style.ItemSpacing.y, 0.0f);
8121         else if (item_rect.Max.y >= window_rect.Max.y)
8122             SetScrollFromPosY(window, item_rect.Max.y - window->Pos.y + g.Style.ItemSpacing.y, 1.0f);
8123 
8124         ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
8125         delta_scroll = next_scroll - window->Scroll;
8126     }
8127 
8128     // Also scroll parent window to keep us into view if necessary
8129     if (window->Flags & ImGuiWindowFlags_ChildWindow)
8130         delta_scroll += ScrollToBringRectIntoView(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll));
8131 
8132     return delta_scroll;
8133 }
8134 
GetScrollX()8135 float ImGui::GetScrollX()
8136 {
8137     ImGuiWindow* window = GImGui->CurrentWindow;
8138     return window->Scroll.x;
8139 }
8140 
GetScrollY()8141 float ImGui::GetScrollY()
8142 {
8143     ImGuiWindow* window = GImGui->CurrentWindow;
8144     return window->Scroll.y;
8145 }
8146 
GetScrollMaxX()8147 float ImGui::GetScrollMaxX()
8148 {
8149     ImGuiWindow* window = GImGui->CurrentWindow;
8150     return window->ScrollMax.x;
8151 }
8152 
GetScrollMaxY()8153 float ImGui::GetScrollMaxY()
8154 {
8155     ImGuiWindow* window = GImGui->CurrentWindow;
8156     return window->ScrollMax.y;
8157 }
8158 
SetScrollX(float scroll_x)8159 void ImGui::SetScrollX(float scroll_x)
8160 {
8161     ImGuiWindow* window = GetCurrentWindow();
8162     window->ScrollTarget.x = scroll_x;
8163     window->ScrollTargetCenterRatio.x = 0.0f;
8164 }
8165 
SetScrollY(float scroll_y)8166 void ImGui::SetScrollY(float scroll_y)
8167 {
8168     ImGuiWindow* window = GetCurrentWindow();
8169     window->ScrollTarget.y = scroll_y;
8170     window->ScrollTargetCenterRatio.y = 0.0f;
8171 }
8172 
SetScrollX(ImGuiWindow * window,float new_scroll_x)8173 void ImGui::SetScrollX(ImGuiWindow* window, float new_scroll_x)
8174 {
8175     window->ScrollTarget.x = new_scroll_x;
8176     window->ScrollTargetCenterRatio.x = 0.0f;
8177 }
8178 
SetScrollY(ImGuiWindow * window,float new_scroll_y)8179 void ImGui::SetScrollY(ImGuiWindow* window, float new_scroll_y)
8180 {
8181     window->ScrollTarget.y = new_scroll_y;
8182     window->ScrollTargetCenterRatio.y = 0.0f;
8183 }
8184 
8185 // Note that a local position will vary depending on initial scroll value
8186 // We store a target position so centering 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)8187 void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
8188 {
8189     IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);
8190     window->ScrollTarget.x = IM_FLOOR(local_x + window->Scroll.x);
8191     window->ScrollTargetCenterRatio.x = center_x_ratio;
8192 }
8193 
SetScrollFromPosY(ImGuiWindow * window,float local_y,float center_y_ratio)8194 void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
8195 {
8196     IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
8197     local_y -= window->TitleBarHeight() + window->MenuBarHeight(); // FIXME: Would be nice to have a more standardized access to our scrollable/client rect
8198     window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y);
8199     window->ScrollTargetCenterRatio.y = center_y_ratio;
8200 }
8201 
SetScrollFromPosX(float local_x,float center_x_ratio)8202 void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)
8203 {
8204     ImGuiContext& g = *GImGui;
8205     SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio);
8206 }
8207 
SetScrollFromPosY(float local_y,float center_y_ratio)8208 void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
8209 {
8210     ImGuiContext& g = *GImGui;
8211     SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio);
8212 }
8213 
8214 // Tweak: snap on edges when aiming at an item very close to the edge,
8215 // So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling.
8216 // When we refactor the scrolling API this may be configurable with a flag?
8217 // Note that the effect for this won't be visible on X axis with default Style settings as WindowPadding.x == ItemSpacing.x by default.
CalcScrollSnap(float target,float snap_min,float snap_max,float snap_threshold,float center_ratio)8218 static float CalcScrollSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio)
8219 {
8220     if (target <= snap_min + snap_threshold)
8221         return ImLerp(snap_min, target, center_ratio);
8222     if (target >= snap_max - snap_threshold)
8223         return ImLerp(target, snap_max, center_ratio);
8224     return target;
8225 }
8226 
8227 // 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)8228 void ImGui::SetScrollHereX(float center_x_ratio)
8229 {
8230     ImGuiContext& g = *GImGui;
8231     ImGuiWindow* window = g.CurrentWindow;
8232     float spacing_x = g.Style.ItemSpacing.x;
8233     float target_x = ImLerp(window->DC.LastItemRect.Min.x - spacing_x, window->DC.LastItemRect.Max.x + spacing_x, center_x_ratio);
8234 
8235     // Tweak: snap on edges when aiming at an item very close to the edge
8236     const float snap_x_threshold = ImMax(0.0f, window->WindowPadding.x - spacing_x);
8237     const float snap_x_min = window->DC.CursorStartPos.x - window->WindowPadding.x;
8238     const float snap_x_max = window->DC.CursorStartPos.x + window->ContentSize.x + window->WindowPadding.x;
8239     target_x = CalcScrollSnap(target_x, snap_x_min, snap_x_max, snap_x_threshold, center_x_ratio);
8240 
8241     SetScrollFromPosX(window, target_x - window->Pos.x, center_x_ratio);
8242 }
8243 
8244 // 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)8245 void ImGui::SetScrollHereY(float center_y_ratio)
8246 {
8247     ImGuiContext& g = *GImGui;
8248     ImGuiWindow* window = g.CurrentWindow;
8249     float spacing_y = g.Style.ItemSpacing.y;
8250     float target_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio);
8251 
8252     // Tweak: snap on edges when aiming at an item very close to the edge
8253     const float snap_y_threshold = ImMax(0.0f, window->WindowPadding.y - spacing_y);
8254     const float snap_y_min = window->DC.CursorStartPos.y - window->WindowPadding.y;
8255     const float snap_y_max = window->DC.CursorStartPos.y + window->ContentSize.y + window->WindowPadding.y;
8256     target_y = CalcScrollSnap(target_y, snap_y_min, snap_y_max, snap_y_threshold, center_y_ratio);
8257 
8258     SetScrollFromPosY(window, target_y - window->Pos.y, center_y_ratio);
8259 }
8260 
8261 //-----------------------------------------------------------------------------
8262 // [SECTION] TOOLTIPS
8263 //-----------------------------------------------------------------------------
8264 
BeginTooltip()8265 void ImGui::BeginTooltip()
8266 {
8267     BeginTooltipEx(ImGuiWindowFlags_None, ImGuiTooltipFlags_None);
8268 }
8269 
BeginTooltipEx(ImGuiWindowFlags extra_flags,ImGuiTooltipFlags tooltip_flags)8270 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags)
8271 {
8272     ImGuiContext& g = *GImGui;
8273 
8274     if (g.DragDropWithinSource || g.DragDropWithinTarget)
8275     {
8276         // 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)
8277         // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
8278         // 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.
8279         //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
8280         ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale);
8281         SetNextWindowPos(tooltip_pos);
8282         SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
8283         //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
8284         tooltip_flags |= ImGuiTooltipFlags_OverridePreviousTooltip;
8285     }
8286 
8287     char window_name[16];
8288     ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
8289     if (tooltip_flags & ImGuiTooltipFlags_OverridePreviousTooltip)
8290         if (ImGuiWindow* window = FindWindowByName(window_name))
8291             if (window->Active)
8292             {
8293                 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
8294                 window->Hidden = true;
8295                 window->HiddenFramesCanSkipItems = 1;
8296                 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
8297             }
8298     ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking;
8299     Begin(window_name, NULL, flags | extra_flags);
8300 }
8301 
EndTooltip()8302 void ImGui::EndTooltip()
8303 {
8304     IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip);   // Mismatched BeginTooltip()/EndTooltip() calls
8305     End();
8306 }
8307 
SetTooltipV(const char * fmt,va_list args)8308 void ImGui::SetTooltipV(const char* fmt, va_list args)
8309 {
8310     BeginTooltipEx(0, ImGuiTooltipFlags_OverridePreviousTooltip);
8311     TextV(fmt, args);
8312     EndTooltip();
8313 }
8314 
SetTooltip(const char * fmt,...)8315 void ImGui::SetTooltip(const char* fmt, ...)
8316 {
8317     va_list args;
8318     va_start(args, fmt);
8319     SetTooltipV(fmt, args);
8320     va_end(args);
8321 }
8322 
8323 //-----------------------------------------------------------------------------
8324 // [SECTION] POPUPS
8325 //-----------------------------------------------------------------------------
8326 
8327 // Supported flags: ImGuiPopupFlags_AnyPopupId, ImGuiPopupFlags_AnyPopupLevel
IsPopupOpen(ImGuiID id,ImGuiPopupFlags popup_flags)8328 bool ImGui::IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags)
8329 {
8330     ImGuiContext& g = *GImGui;
8331     if (popup_flags & ImGuiPopupFlags_AnyPopupId)
8332     {
8333         // Return true if any popup is open at the current BeginPopup() level of the popup stack
8334         // This may be used to e.g. test for another popups already opened to handle popups priorities at the same level.
8335         IM_ASSERT(id == 0);
8336         if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
8337             return g.OpenPopupStack.Size > 0;
8338         else
8339             return g.OpenPopupStack.Size > g.BeginPopupStack.Size;
8340     }
8341     else
8342     {
8343         if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
8344         {
8345             // Return true if the popup is open anywhere in the popup stack
8346             for (int n = 0; n < g.OpenPopupStack.Size; n++)
8347                 if (g.OpenPopupStack[n].PopupId == id)
8348                     return true;
8349             return false;
8350         }
8351         else
8352         {
8353             // Return true if the popup is open at the current BeginPopup() level of the popup stack (this is the most-common query)
8354             return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
8355         }
8356     }
8357 }
8358 
IsPopupOpen(const char * str_id,ImGuiPopupFlags popup_flags)8359 bool ImGui::IsPopupOpen(const char* str_id, ImGuiPopupFlags popup_flags)
8360 {
8361     ImGuiContext& g = *GImGui;
8362     ImGuiID id = (popup_flags & ImGuiPopupFlags_AnyPopupId) ? 0 : g.CurrentWindow->GetID(str_id);
8363     if ((popup_flags & ImGuiPopupFlags_AnyPopupLevel) && id != 0)
8364         IM_ASSERT(0 && "Cannot use IsPopupOpen() with a string id and ImGuiPopupFlags_AnyPopupLevel."); // But non-string version is legal and used internally
8365     return IsPopupOpen(id, popup_flags);
8366 }
8367 
GetTopMostPopupModal()8368 ImGuiWindow* ImGui::GetTopMostPopupModal()
8369 {
8370     ImGuiContext& g = *GImGui;
8371     for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
8372         if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
8373             if (popup->Flags & ImGuiWindowFlags_Modal)
8374                 return popup;
8375     return NULL;
8376 }
8377 
OpenPopup(const char * str_id,ImGuiPopupFlags popup_flags)8378 void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags)
8379 {
8380     ImGuiContext& g = *GImGui;
8381     OpenPopupEx(g.CurrentWindow->GetID(str_id), popup_flags);
8382 }
8383 
8384 // Mark popup as open (toggle toward open state).
8385 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
8386 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
8387 // 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)8388 void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags)
8389 {
8390     ImGuiContext& g = *GImGui;
8391     ImGuiWindow* parent_window = g.CurrentWindow;
8392     const int current_stack_size = g.BeginPopupStack.Size;
8393 
8394     if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup)
8395         if (IsPopupOpen(0u, ImGuiPopupFlags_AnyPopupId))
8396             return;
8397 
8398     ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
8399     popup_ref.PopupId = id;
8400     popup_ref.Window = NULL;
8401     popup_ref.SourceWindow = g.NavWindow;
8402     popup_ref.OpenFrameCount = g.FrameCount;
8403     popup_ref.OpenParentId = parent_window->IDStack.back();
8404     popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
8405     popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
8406 
8407     IMGUI_DEBUG_LOG_POPUP("OpenPopupEx(0x%08X)\n", id);
8408     if (g.OpenPopupStack.Size < current_stack_size + 1)
8409     {
8410         g.OpenPopupStack.push_back(popup_ref);
8411     }
8412     else
8413     {
8414         // 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
8415         // 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
8416         // 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.
8417         if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
8418         {
8419             g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
8420         }
8421         else
8422         {
8423             // Close child popups if any, then flag popup for open/reopen
8424             ClosePopupToLevel(current_stack_size, false);
8425             g.OpenPopupStack.push_back(popup_ref);
8426         }
8427 
8428         // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
8429         // This is equivalent to what ClosePopupToLevel() does.
8430         //if (g.OpenPopupStack[current_stack_size].PopupId == id)
8431         //    FocusWindow(parent_window);
8432     }
8433 }
8434 
8435 // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
8436 // This function closes any popups that are over 'ref_window'.
ClosePopupsOverWindow(ImGuiWindow * ref_window,bool restore_focus_to_window_under_popup)8437 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
8438 {
8439     ImGuiContext& g = *GImGui;
8440     if (g.OpenPopupStack.Size == 0)
8441         return;
8442 
8443     // Don't close our own child popup windows.
8444     int popup_count_to_keep = 0;
8445     if (ref_window)
8446     {
8447         // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
8448         for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
8449         {
8450             ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];
8451             if (!popup.Window)
8452                 continue;
8453             IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
8454             if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
8455                 continue;
8456 
8457             // Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow)
8458             // - With this stack of window, clicking/focusing Popup1 will close Popup2 and Popup3:
8459             //     Window -> Popup1 -> Popup2 -> Popup3
8460             // - Each popups may contain child windows, which is why we compare ->RootWindow!
8461             //     Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child
8462             bool ref_window_is_descendent_of_popup = false;
8463             for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++)
8464                 if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window)
8465                     if (popup_window->RootWindow == ref_window->RootWindow)
8466                     {
8467                         ref_window_is_descendent_of_popup = true;
8468                         break;
8469                     }
8470             if (!ref_window_is_descendent_of_popup)
8471                 break;
8472         }
8473     }
8474     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
8475     {
8476         IMGUI_DEBUG_LOG_POPUP("ClosePopupsOverWindow(\"%s\") -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep);
8477         ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup);
8478     }
8479 }
8480 
ClosePopupToLevel(int remaining,bool restore_focus_to_window_under_popup)8481 void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
8482 {
8483     ImGuiContext& g = *GImGui;
8484     IMGUI_DEBUG_LOG_POPUP("ClosePopupToLevel(%d), restore_focus_to_window_under_popup=%d\n", remaining, restore_focus_to_window_under_popup);
8485     IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
8486 
8487     // Trim open popup stack
8488     ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow;
8489     ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window;
8490     g.OpenPopupStack.resize(remaining);
8491 
8492     if (restore_focus_to_window_under_popup)
8493     {
8494         if (focus_window && !focus_window->WasActive && popup_window)
8495         {
8496             // Fallback
8497             FocusTopMostWindowUnderOne(popup_window, NULL);
8498         }
8499         else
8500         {
8501             if (g.NavLayer == ImGuiNavLayer_Main && focus_window)
8502                 focus_window = NavRestoreLastChildNavWindow(focus_window);
8503             FocusWindow(focus_window);
8504         }
8505     }
8506 }
8507 
8508 // Close the popup we have begin-ed into.
CloseCurrentPopup()8509 void ImGui::CloseCurrentPopup()
8510 {
8511     ImGuiContext& g = *GImGui;
8512     int popup_idx = g.BeginPopupStack.Size - 1;
8513     if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
8514         return;
8515 
8516     // Closing a menu closes its top-most parent popup (unless a modal)
8517     while (popup_idx > 0)
8518     {
8519         ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;
8520         ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
8521         bool close_parent = false;
8522         if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
8523             if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal))
8524                 close_parent = true;
8525         if (!close_parent)
8526             break;
8527         popup_idx--;
8528     }
8529     IMGUI_DEBUG_LOG_POPUP("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx);
8530     ClosePopupToLevel(popup_idx, true);
8531 
8532     // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
8533     // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
8534     // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
8535     if (ImGuiWindow* window = g.NavWindow)
8536         window->DC.NavHideHighlightOneFrame = true;
8537 }
8538 
8539 // Attention! BeginPopup() adds default flags which BeginPopupEx()!
BeginPopupEx(ImGuiID id,ImGuiWindowFlags flags)8540 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags)
8541 {
8542     ImGuiContext& g = *GImGui;
8543     if (!IsPopupOpen(id, ImGuiPopupFlags_None))
8544     {
8545         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
8546         return false;
8547     }
8548 
8549     char name[20];
8550     if (flags & ImGuiWindowFlags_ChildMenu)
8551         ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth
8552     else
8553         ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
8554 
8555     flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoDocking;
8556     bool is_open = Begin(name, NULL, flags);
8557     if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
8558         EndPopup();
8559 
8560     return is_open;
8561 }
8562 
BeginPopup(const char * str_id,ImGuiWindowFlags flags)8563 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
8564 {
8565     ImGuiContext& g = *GImGui;
8566     if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
8567     {
8568         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
8569         return false;
8570     }
8571     flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
8572     return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags);
8573 }
8574 
8575 // If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
8576 // 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)8577 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
8578 {
8579     ImGuiContext& g = *GImGui;
8580     ImGuiWindow* window = g.CurrentWindow;
8581     const ImGuiID id = window->GetID(name);
8582     if (!IsPopupOpen(id, ImGuiPopupFlags_None))
8583     {
8584         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
8585         return false;
8586     }
8587 
8588     // Center modal windows by default for increased visibility
8589     // (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves)
8590     // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
8591     if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
8592     {
8593         ImGuiViewportP* viewport = window->WasActive ? window->Viewport : (ImGuiViewportP*)GetMainViewport(); // FIXME-VIEWPORT: What may be our reference viewport?
8594         SetNextWindowPos(viewport->GetMainRect().GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
8595     }
8596 
8597     flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking;
8598     const bool is_open = Begin(name, p_open, flags);
8599     if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
8600     {
8601         EndPopup();
8602         if (is_open)
8603             ClosePopupToLevel(g.BeginPopupStack.Size, true);
8604         return false;
8605     }
8606     return is_open;
8607 }
8608 
EndPopup()8609 void ImGui::EndPopup()
8610 {
8611     ImGuiContext& g = *GImGui;
8612     ImGuiWindow* window = g.CurrentWindow;
8613     IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup);  // Mismatched BeginPopup()/EndPopup() calls
8614     IM_ASSERT(g.BeginPopupStack.Size > 0);
8615 
8616     // Make all menus and popups wrap around for now, may need to expose that policy.
8617     if (g.NavWindow == window)
8618         NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
8619 
8620     // Child-popups don't need to be laid out
8621     IM_ASSERT(g.WithinEndChild == false);
8622     if (window->Flags & ImGuiWindowFlags_ChildWindow)
8623         g.WithinEndChild = true;
8624     End();
8625     g.WithinEndChild = false;
8626 }
8627 
8628 // Open a popup if mouse button is released over the item
OpenPopupContextItem(const char * str_id,ImGuiPopupFlags popup_flags)8629 bool ImGui::OpenPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags)
8630 {
8631     ImGuiWindow* window = GImGui->CurrentWindow;
8632     int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8633     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
8634     {
8635         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!
8636         IM_ASSERT(id != 0);                                                  // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
8637         OpenPopupEx(id, popup_flags);
8638         return true;
8639     }
8640     return false;
8641 }
8642 
8643 // This is a helper to handle the simplest case of associating one named popup to one given widget.
8644 // - You can pass a NULL str_id to use the identifier of the last item.
8645 // - You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
8646 // - This is essentially the same as calling OpenPopupContextItem() + BeginPopup() but written to avoid
8647 //   computing the ID twice because BeginPopupContextXXX functions are called very frequently.
BeginPopupContextItem(const char * str_id,ImGuiPopupFlags popup_flags)8648 bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags)
8649 {
8650     ImGuiWindow* window = GImGui->CurrentWindow;
8651     if (window->SkipItems)
8652         return false;
8653     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!
8654     IM_ASSERT(id != 0);                                                  // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
8655     int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8656     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
8657         OpenPopupEx(id, popup_flags);
8658     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
8659 }
8660 
BeginPopupContextWindow(const char * str_id,ImGuiPopupFlags popup_flags)8661 bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags)
8662 {
8663     ImGuiWindow* window = GImGui->CurrentWindow;
8664     if (!str_id)
8665         str_id = "window_context";
8666     ImGuiID id = window->GetID(str_id);
8667     int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8668     if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
8669         if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered())
8670             OpenPopupEx(id, popup_flags);
8671     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
8672 }
8673 
BeginPopupContextVoid(const char * str_id,ImGuiPopupFlags popup_flags)8674 bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags)
8675 {
8676     ImGuiWindow* window = GImGui->CurrentWindow;
8677     if (!str_id)
8678         str_id = "void_context";
8679     ImGuiID id = window->GetID(str_id);
8680     int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8681     if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
8682         if (GetTopMostPopupModal() == NULL)
8683             OpenPopupEx(id, popup_flags);
8684     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
8685 }
8686 
8687 // 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.)
8688 // 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.
8689 // (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor
8690 //  information are available, it may represent the entire platform monitor from the frame of reference of the current viewport.
8691 //  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)8692 ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
8693 {
8694     ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
8695     //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
8696     //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
8697 
8698     // Combo Box policy (we want a connecting edge)
8699     if (policy == ImGuiPopupPositionPolicy_ComboBox)
8700     {
8701         const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
8702         for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
8703         {
8704             const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
8705             if (n != -1 && dir == *last_dir) // Already tried this direction?
8706                 continue;
8707             ImVec2 pos;
8708             if (dir == ImGuiDir_Down)  pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y);          // Below, Toward Right (default)
8709             if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
8710             if (dir == ImGuiDir_Left)  pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
8711             if (dir == ImGuiDir_Up)    pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
8712             if (!r_outer.Contains(ImRect(pos, pos + size)))
8713                 continue;
8714             *last_dir = dir;
8715             return pos;
8716         }
8717     }
8718 
8719     // Default popup policy
8720     const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
8721     for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
8722     {
8723         const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
8724         if (n != -1 && dir == *last_dir) // Already tried this direction?
8725             continue;
8726         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);
8727         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);
8728         if (avail_w < size.x || avail_h < size.y)
8729             continue;
8730         ImVec2 pos;
8731         pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
8732         pos.y = (dir == ImGuiDir_Up)   ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down)  ? r_avoid.Max.y : base_pos_clamped.y;
8733         *last_dir = dir;
8734         return pos;
8735     }
8736 
8737     // Fallback, try to keep within display
8738     *last_dir = ImGuiDir_None;
8739     ImVec2 pos = ref_pos;
8740     pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
8741     pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
8742     return pos;
8743 }
8744 
8745 // Note that this is used for popups, which can overlap the non work-area of individual viewports.
GetWindowAllowedExtentRect(ImGuiWindow * window)8746 ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window)
8747 {
8748     ImGuiContext& g = *GImGui;
8749     ImRect r_screen;
8750     if (window->ViewportAllowPlatformMonitorExtend >= 0)
8751     {
8752         // Extent with be in the frame of reference of the given viewport (so Min is likely to be negative here)
8753         const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->ViewportAllowPlatformMonitorExtend];
8754         r_screen.Min = monitor.WorkPos;
8755         r_screen.Max = monitor.WorkPos + monitor.WorkSize;
8756     }
8757     else
8758     {
8759         // Use the full viewport area (not work area) for popups
8760         r_screen.Min = window->Viewport->Pos;
8761         r_screen.Max = window->Viewport->Pos + window->Viewport->Size;
8762     }
8763     ImVec2 padding = g.Style.DisplaySafeAreaPadding;
8764     r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
8765     return r_screen;
8766 }
8767 
FindBestWindowPosForPopup(ImGuiWindow * window)8768 ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
8769 {
8770     ImGuiContext& g = *GImGui;
8771     if (window->Flags & ImGuiWindowFlags_ChildMenu)
8772     {
8773         // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
8774         // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
8775         ImGuiWindow* parent_window = window->ParentWindow;
8776         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).
8777         ImRect r_outer = GetWindowAllowedExtentRect(window);
8778         ImRect r_avoid;
8779         if (parent_window->DC.MenuBarAppending)
8780             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
8781         else
8782             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);
8783         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
8784     }
8785     if (window->Flags & ImGuiWindowFlags_Popup)
8786     {
8787         ImRect r_outer = GetWindowAllowedExtentRect(window);
8788         ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
8789         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
8790     }
8791     if (window->Flags & ImGuiWindowFlags_Tooltip)
8792     {
8793         // Position tooltip (always follows mouse)
8794         float sc = g.Style.MouseCursorScale;
8795         ImVec2 ref_pos = NavCalcPreferredRefPos();
8796         ImRect r_outer = GetWindowAllowedExtentRect(window);
8797         ImRect r_avoid;
8798         if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
8799             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
8800         else
8801             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.
8802         ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
8803         if (window->AutoPosLastDirection == ImGuiDir_None)
8804             pos = ref_pos + ImVec2(2, 2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
8805         return pos;
8806     }
8807     IM_ASSERT(0);
8808     return window->Pos;
8809 }
8810 
8811 //-----------------------------------------------------------------------------
8812 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
8813 //-----------------------------------------------------------------------------
8814 
8815 // FIXME-NAV: The existence of SetNavID vs SetNavIDWithRectRel vs SetFocusID is incredibly messy and confusing,
8816 // and needs some explanation or serious refactoring.
SetNavID(ImGuiID id,int nav_layer,ImGuiID focus_scope_id)8817 void ImGui::SetNavID(ImGuiID id, int nav_layer, ImGuiID focus_scope_id)
8818 {
8819     ImGuiContext& g = *GImGui;
8820     IM_ASSERT(g.NavWindow);
8821     IM_ASSERT(nav_layer == 0 || nav_layer == 1);
8822     g.NavId = id;
8823     g.NavFocusScopeId = focus_scope_id;
8824     g.NavWindow->NavLastIds[nav_layer] = id;
8825 }
8826 
SetNavIDWithRectRel(ImGuiID id,int nav_layer,ImGuiID focus_scope_id,const ImRect & rect_rel)8827 void ImGui::SetNavIDWithRectRel(ImGuiID id, int nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel)
8828 {
8829     ImGuiContext& g = *GImGui;
8830     SetNavID(id, nav_layer, focus_scope_id);
8831     g.NavWindow->NavRectRel[nav_layer] = rect_rel;
8832     g.NavMousePosDirty = true;
8833     g.NavDisableHighlight = false;
8834     g.NavDisableMouseHover = true;
8835 }
8836 
SetFocusID(ImGuiID id,ImGuiWindow * window)8837 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
8838 {
8839     ImGuiContext& g = *GImGui;
8840     IM_ASSERT(id != 0);
8841 
8842     // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and window->DC.NavFocusScopeIdCurrent are valid.
8843     // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text)
8844     const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
8845     if (g.NavWindow != window)
8846         g.NavInitRequest = false;
8847     g.NavWindow = window;
8848     g.NavId = id;
8849     g.NavLayer = nav_layer;
8850     g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
8851     window->NavLastIds[nav_layer] = id;
8852     if (window->DC.LastItemId == id)
8853         window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos);
8854 
8855     if (g.ActiveIdSource == ImGuiInputSource_Nav)
8856         g.NavDisableMouseHover = true;
8857     else
8858         g.NavDisableHighlight = true;
8859 }
8860 
ImGetDirQuadrantFromDelta(float dx,float dy)8861 ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
8862 {
8863     if (ImFabs(dx) > ImFabs(dy))
8864         return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
8865     return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
8866 }
8867 
NavScoreItemDistInterval(float a0,float a1,float b0,float b1)8868 static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
8869 {
8870     if (a1 < b0)
8871         return a1 - b0;
8872     if (b1 < a0)
8873         return a0 - b1;
8874     return 0.0f;
8875 }
8876 
NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir,ImRect & r,const ImRect & clip_rect)8877 static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect)
8878 {
8879     if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
8880     {
8881         r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y);
8882         r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y);
8883     }
8884     else
8885     {
8886         r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x);
8887         r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x);
8888     }
8889 }
8890 
8891 // Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057
NavScoreItem(ImGuiNavMoveResult * result,ImRect cand)8892 static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
8893 {
8894     ImGuiContext& g = *GImGui;
8895     ImGuiWindow* window = g.CurrentWindow;
8896     if (g.NavLayer != window->DC.NavLayerCurrent)
8897         return false;
8898 
8899     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)
8900     g.NavScoringCount++;
8901 
8902     // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
8903     if (window->ParentWindow == g.NavWindow)
8904     {
8905         IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened);
8906         if (!window->ClipRect.Overlaps(cand))
8907             return false;
8908         cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
8909     }
8910 
8911     // 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)
8912     // For example, this ensure that items in one column are not reached when moving vertically from items in another column.
8913     NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect);
8914 
8915     // Compute distance between boxes
8916     // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
8917     float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
8918     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
8919     if (dby != 0.0f && dbx != 0.0f)
8920         dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
8921     float dist_box = ImFabs(dbx) + ImFabs(dby);
8922 
8923     // 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)
8924     float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
8925     float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
8926     float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
8927 
8928     // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
8929     ImGuiDir quadrant;
8930     float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
8931     if (dbx != 0.0f || dby != 0.0f)
8932     {
8933         // For non-overlapping boxes, use distance between boxes
8934         dax = dbx;
8935         day = dby;
8936         dist_axial = dist_box;
8937         quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
8938     }
8939     else if (dcx != 0.0f || dcy != 0.0f)
8940     {
8941         // For overlapping boxes with different centers, use distance between centers
8942         dax = dcx;
8943         day = dcy;
8944         dist_axial = dist_center;
8945         quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
8946     }
8947     else
8948     {
8949         // 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)
8950         quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
8951     }
8952 
8953 #if IMGUI_DEBUG_NAV_SCORING
8954     char buf[128];
8955     if (IsMouseHoveringRect(cand.Min, cand.Max))
8956     {
8957         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]);
8958         ImDrawList* draw_list = GetForegroundDrawList(window);
8959         draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
8960         draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
8961         draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40,0,0,150));
8962         draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
8963     }
8964     else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
8965     {
8966         if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; }
8967         if (quadrant == g.NavMoveDir)
8968         {
8969             ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
8970             ImDrawList* draw_list = GetForegroundDrawList(window);
8971             draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
8972             draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
8973         }
8974     }
8975 #endif
8976 
8977     // Is it in the quadrant we're interesting in moving to?
8978     bool new_best = false;
8979     if (quadrant == g.NavMoveDir)
8980     {
8981         // Does it beat the current best candidate?
8982         if (dist_box < result->DistBox)
8983         {
8984             result->DistBox = dist_box;
8985             result->DistCenter = dist_center;
8986             return true;
8987         }
8988         if (dist_box == result->DistBox)
8989         {
8990             // Try using distance between center points to break ties
8991             if (dist_center < result->DistCenter)
8992             {
8993                 result->DistCenter = dist_center;
8994                 new_best = true;
8995             }
8996             else if (dist_center == result->DistCenter)
8997             {
8998                 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
8999                 // (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),
9000                 // 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.
9001                 if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
9002                     new_best = true;
9003             }
9004         }
9005     }
9006 
9007     // 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
9008     // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
9009     // 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.
9010     // 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.
9011     // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
9012     if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial)  // Check axial match
9013         if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
9014             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))
9015             {
9016                 result->DistAxial = dist_axial;
9017                 new_best = true;
9018             }
9019 
9020     return new_best;
9021 }
9022 
9023 // 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)9024 static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
9025 {
9026     ImGuiContext& g = *GImGui;
9027     //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.
9028     //    return;
9029 
9030     const ImGuiItemFlags item_flags = window->DC.ItemFlags;
9031     const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
9032 
9033     // Process Init Request
9034     if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
9035     {
9036         // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
9037         if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0)
9038         {
9039             g.NavInitResultId = id;
9040             g.NavInitResultRectRel = nav_bb_rel;
9041         }
9042         if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus))
9043         {
9044             g.NavInitRequest = false; // Found a match, clear request
9045             NavUpdateAnyRequestFlag();
9046         }
9047     }
9048 
9049     // Process Move Request (scoring for navigation)
9050     // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
9051     if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav)))
9052     {
9053         ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
9054 #if IMGUI_DEBUG_NAV_SCORING
9055         // [DEBUG] Score all items in NavWindow at all times
9056         if (!g.NavMoveRequest)
9057             g.NavMoveDir = g.NavMoveDirLast;
9058         bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest;
9059 #else
9060         bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb);
9061 #endif
9062         if (new_best)
9063         {
9064             result->Window = window;
9065             result->ID = id;
9066             result->FocusScopeId = window->DC.NavFocusScopeIdCurrent;
9067             result->RectRel = nav_bb_rel;
9068         }
9069 
9070         // Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
9071         const float VISIBLE_RATIO = 0.70f;
9072         if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
9073             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)
9074                 if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb))
9075                 {
9076                     result = &g.NavMoveResultLocalVisibleSet;
9077                     result->Window = window;
9078                     result->ID = id;
9079                     result->FocusScopeId = window->DC.NavFocusScopeIdCurrent;
9080                     result->RectRel = nav_bb_rel;
9081                 }
9082     }
9083 
9084     // Update window-relative bounding box of navigated item
9085     if (g.NavId == id)
9086     {
9087         g.NavWindow = window;                                           // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
9088         g.NavLayer = window->DC.NavLayerCurrent;
9089         g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
9090         g.NavIdIsAlive = true;
9091         g.NavIdTabCounter = window->DC.FocusCounterTabStop;
9092         window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel;    // Store item bounding box (relative to window position)
9093     }
9094 }
9095 
NavMoveRequestButNoResultYet()9096 bool ImGui::NavMoveRequestButNoResultYet()
9097 {
9098     ImGuiContext& g = *GImGui;
9099     return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
9100 }
9101 
NavMoveRequestCancel()9102 void ImGui::NavMoveRequestCancel()
9103 {
9104     ImGuiContext& g = *GImGui;
9105     g.NavMoveRequest = false;
9106     NavUpdateAnyRequestFlag();
9107 }
9108 
NavMoveRequestForward(ImGuiDir move_dir,ImGuiDir clip_dir,const ImRect & bb_rel,ImGuiNavMoveFlags move_flags)9109 void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags)
9110 {
9111     ImGuiContext& g = *GImGui;
9112     IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None);
9113     NavMoveRequestCancel();
9114     g.NavMoveDir = move_dir;
9115     g.NavMoveClipDir = clip_dir;
9116     g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
9117     g.NavMoveRequestFlags = move_flags;
9118     g.NavWindow->NavRectRel[g.NavLayer] = bb_rel;
9119 }
9120 
NavMoveRequestTryWrapping(ImGuiWindow * window,ImGuiNavMoveFlags move_flags)9121 void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
9122 {
9123     ImGuiContext& g = *GImGui;
9124 
9125     // Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
9126     // popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.
9127     g.NavWrapRequestWindow = window;
9128     g.NavWrapRequestFlags = move_flags;
9129 }
9130 
9131 // FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
9132 // This way we could find the last focused window among our children. It would be much less confusing this way?
NavSaveLastChildNavWindowIntoParent(ImGuiWindow * nav_window)9133 static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window)
9134 {
9135     ImGuiWindow* parent = nav_window;
9136     while (parent && parent->RootWindowDockStop != parent && (parent->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
9137         parent = parent->ParentWindow;
9138     if (parent && parent != nav_window)
9139         parent->NavLastChildNavWindow = nav_window;
9140 }
9141 
9142 // Restore the last focused child.
9143 // Call when we are expected to land on the Main Layer (0) after FocusWindow()
NavRestoreLastChildNavWindow(ImGuiWindow * window)9144 static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
9145 {
9146     if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive)
9147         return window->NavLastChildNavWindow;
9148     if (window->DockNodeAsHost && window->DockNodeAsHost->TabBar)
9149         if (ImGuiTabItem* tab = TabBarFindMostRecentlySelectedTabForActiveWindow(window->DockNodeAsHost->TabBar))
9150             return tab->Window;
9151     return window;
9152 }
9153 
NavRestoreLayer(ImGuiNavLayer layer)9154 static void NavRestoreLayer(ImGuiNavLayer layer)
9155 {
9156     ImGuiContext& g = *GImGui;
9157     g.NavLayer = layer;
9158     if (layer == 0)
9159         g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow);
9160     ImGuiWindow* window = g.NavWindow;
9161     if (window->NavLastIds[layer] != 0)
9162         ImGui::SetNavIDWithRectRel(window->NavLastIds[layer], layer, 0, g.NavWindow->NavRectRel[layer]);
9163     else
9164         ImGui::NavInitWindow(window, true);
9165 }
9166 
NavUpdateAnyRequestFlag()9167 static inline void ImGui::NavUpdateAnyRequestFlag()
9168 {
9169     ImGuiContext& g = *GImGui;
9170     g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
9171     if (g.NavAnyRequest)
9172         IM_ASSERT(g.NavWindow != NULL);
9173 }
9174 
9175 // This needs to be called before we submit any widget (aka in or before Begin)
NavInitWindow(ImGuiWindow * window,bool force_reinit)9176 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
9177 {
9178     ImGuiContext& g = *GImGui;
9179     IM_ASSERT(window == g.NavWindow);
9180     bool init_for_nav = false;
9181     if (!(window->Flags & ImGuiWindowFlags_NoNavInputs))
9182         if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
9183             init_for_nav = true;
9184     //IMGUI_DEBUG_LOG("[Nav] NavInitWindow() init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer);
9185     if (init_for_nav)
9186     {
9187         SetNavID(0, g.NavLayer, 0);
9188         g.NavInitRequest = true;
9189         g.NavInitRequestFromMove = false;
9190         g.NavInitResultId = 0;
9191         g.NavInitResultRectRel = ImRect();
9192         NavUpdateAnyRequestFlag();
9193     }
9194     else
9195     {
9196         g.NavId = window->NavLastIds[0];
9197         g.NavFocusScopeId = 0;
9198     }
9199 }
9200 
NavCalcPreferredRefPos()9201 static ImVec2 ImGui::NavCalcPreferredRefPos()
9202 {
9203     ImGuiContext& g = *GImGui;
9204     if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow)
9205     {
9206         // Mouse (we need a fallback in case the mouse becomes invalid after being used)
9207         if (IsMousePosValid(&g.IO.MousePos))
9208             return g.IO.MousePos;
9209         return g.LastValidMousePos;
9210     }
9211     else
9212     {
9213         // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item.
9214         const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
9215         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()));
9216         ImRect visible_rect = g.NavWindow->Viewport->GetMainRect();
9217         return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max));   // ImFloor() is important because non-integer mouse position application in back-end might be lossy and result in undesirable non-zero delta.
9218     }
9219 }
9220 
GetNavInputAmount(ImGuiNavInput n,ImGuiInputReadMode mode)9221 float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode)
9222 {
9223     ImGuiContext& g = *GImGui;
9224     if (mode == ImGuiInputReadMode_Down)
9225         return g.IO.NavInputs[n];                         // Instant, read analog input (0.0f..1.0f, as provided by user)
9226 
9227     const float t = g.IO.NavInputsDownDuration[n];
9228     if (t < 0.0f && mode == ImGuiInputReadMode_Released)  // Return 1.0f when just released, no repeat, ignore analog input.
9229         return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
9230     if (t < 0.0f)
9231         return 0.0f;
9232     if (mode == ImGuiInputReadMode_Pressed)               // Return 1.0f when just pressed, no repeat, ignore analog input.
9233         return (t == 0.0f) ? 1.0f : 0.0f;
9234     if (mode == ImGuiInputReadMode_Repeat)
9235         return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.80f);
9236     if (mode == ImGuiInputReadMode_RepeatSlow)
9237         return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 1.25f, g.IO.KeyRepeatRate * 2.00f);
9238     if (mode == ImGuiInputReadMode_RepeatFast)
9239         return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.30f);
9240     return 0.0f;
9241 }
9242 
GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources,ImGuiInputReadMode mode,float slow_factor,float fast_factor)9243 ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor)
9244 {
9245     ImVec2 delta(0.0f, 0.0f);
9246     if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
9247         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode)   - GetNavInputAmount(ImGuiNavInput_KeyLeft_,   mode), GetNavInputAmount(ImGuiNavInput_KeyDown_,   mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_,   mode));
9248     if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
9249         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode)   - GetNavInputAmount(ImGuiNavInput_DpadLeft,   mode), GetNavInputAmount(ImGuiNavInput_DpadDown,   mode) - GetNavInputAmount(ImGuiNavInput_DpadUp,   mode));
9250     if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
9251         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode));
9252     if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow))
9253         delta *= slow_factor;
9254     if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast))
9255         delta *= fast_factor;
9256     return delta;
9257 }
9258 
NavUpdate()9259 static void ImGui::NavUpdate()
9260 {
9261     ImGuiContext& g = *GImGui;
9262     g.IO.WantSetMousePos = false;
9263     g.NavWrapRequestWindow = NULL;
9264     g.NavWrapRequestFlags = ImGuiNavMoveFlags_None;
9265 #if 0
9266     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);
9267 #endif
9268 
9269     // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard)
9270     // (do it before we map Keyboard input!)
9271     bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
9272     bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
9273     if (nav_gamepad_active)
9274         if (g.IO.NavInputs[ImGuiNavInput_Activate] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Input] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Cancel] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Menu] > 0.0f)
9275             g.NavInputSource = ImGuiInputSource_NavGamepad;
9276 
9277     // Update Keyboard->Nav inputs mapping
9278     if (nav_keyboard_active)
9279     {
9280         #define NAV_MAP_KEY(_KEY, _NAV_INPUT)  do { if (IsKeyDown(g.IO.KeyMap[_KEY])) { g.IO.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; } } while (0)
9281         NAV_MAP_KEY(ImGuiKey_Space,     ImGuiNavInput_Activate );
9282         NAV_MAP_KEY(ImGuiKey_Enter,     ImGuiNavInput_Input    );
9283         NAV_MAP_KEY(ImGuiKey_Escape,    ImGuiNavInput_Cancel   );
9284         NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ );
9285         NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
9286         NAV_MAP_KEY(ImGuiKey_UpArrow,   ImGuiNavInput_KeyUp_   );
9287         NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
9288         if (g.IO.KeyCtrl)
9289             g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
9290         if (g.IO.KeyShift)
9291             g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
9292         if (g.IO.KeyAlt && !g.IO.KeyCtrl) // AltGR is Alt+Ctrl, also even on keyboards without AltGR we don't want Alt+Ctrl to open menu.
9293             g.IO.NavInputs[ImGuiNavInput_KeyMenu_]  = 1.0f;
9294         #undef NAV_MAP_KEY
9295     }
9296     memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration));
9297     for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++)
9298         g.IO.NavInputsDownDuration[i] = (g.IO.NavInputs[i] > 0.0f) ? (g.IO.NavInputsDownDuration[i] < 0.0f ? 0.0f : g.IO.NavInputsDownDuration[i] + g.IO.DeltaTime) : -1.0f;
9299 
9300     // Process navigation init request (select first/default focus)
9301     // 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)
9302     if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove) && g.NavWindow)
9303     {
9304         // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
9305         //IMGUI_DEBUG_LOG("[Nav] Apply NavInitRequest result: 0x%08X Layer %d in \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name);
9306         if (g.NavInitRequestFromMove)
9307             SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel);
9308         else
9309             SetNavID(g.NavInitResultId, g.NavLayer, 0);
9310         g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel;
9311     }
9312     g.NavInitRequest = false;
9313     g.NavInitRequestFromMove = false;
9314     g.NavInitResultId = 0;
9315     g.NavJustMovedToId = 0;
9316 
9317     // Process navigation move request
9318     if (g.NavMoveRequest)
9319         NavUpdateMoveResult();
9320 
9321     // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
9322     if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
9323     {
9324         IM_ASSERT(g.NavMoveRequest);
9325         if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
9326             g.NavDisableHighlight = false;
9327         g.NavMoveRequestForward = ImGuiNavForward_None;
9328     }
9329 
9330     // Apply application mouse position movement, after we had a chance to process move request result.
9331     if (g.NavMousePosDirty && g.NavIdIsAlive)
9332     {
9333         // Set mouse position given our knowledge of the navigated item position from last frame
9334         if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
9335         {
9336             if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
9337             {
9338                 g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos();
9339                 g.IO.WantSetMousePos = true;
9340             }
9341         }
9342         g.NavMousePosDirty = false;
9343     }
9344     g.NavIdIsAlive = false;
9345     g.NavJustTabbedId = 0;
9346     IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
9347 
9348     // 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
9349     if (g.NavWindow)
9350         NavSaveLastChildNavWindowIntoParent(g.NavWindow);
9351     if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main)
9352         g.NavWindow->NavLastChildNavWindow = NULL;
9353 
9354     // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
9355     NavUpdateWindowing();
9356 
9357     // Set output flags for user application
9358     g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
9359     g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL);
9360 
9361     // Process NavCancel input (to close a popup, get back to parent, clear focus)
9362     if (IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
9363     {
9364         if (g.ActiveId != 0)
9365         {
9366             if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel))
9367                 ClearActiveID();
9368         }
9369         else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow && g.NavWindow != g.NavWindow->RootWindowDockStop)
9370         {
9371             // Exit child window
9372             ImGuiWindow* child_window = g.NavWindow;
9373             ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
9374             IM_ASSERT(child_window->ChildId != 0);
9375             FocusWindow(parent_window);
9376             SetNavID(child_window->ChildId, 0, 0);
9377             // Reassigning with same value, we're being explicit here.
9378             g.NavIdIsAlive = false;     // -V1048
9379             if (g.NavDisableMouseHover)
9380                 g.NavMousePosDirty = true;
9381         }
9382         else if (g.OpenPopupStack.Size > 0)
9383         {
9384             // Close open popup/menu
9385             if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
9386                 ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
9387         }
9388         else if (g.NavLayer != ImGuiNavLayer_Main)
9389         {
9390             // Leave the "menu" layer
9391             NavRestoreLayer(ImGuiNavLayer_Main);
9392         }
9393         else
9394         {
9395             // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
9396             if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
9397                 g.NavWindow->NavLastIds[0] = 0;
9398             g.NavId = g.NavFocusScopeId = 0;
9399         }
9400     }
9401 
9402     // Process manual activation request
9403     g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
9404     if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
9405     {
9406         bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
9407         bool activate_pressed = activate_down && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
9408         if (g.ActiveId == 0 && activate_pressed)
9409             g.NavActivateId = g.NavId;
9410         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
9411             g.NavActivateDownId = g.NavId;
9412         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
9413             g.NavActivatePressedId = g.NavId;
9414         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
9415             g.NavInputId = g.NavId;
9416     }
9417     if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
9418         g.NavDisableHighlight = true;
9419     if (g.NavActivateId != 0)
9420         IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
9421     g.NavMoveRequest = false;
9422 
9423     // Process programmatic activation request
9424     if (g.NavNextActivateId != 0)
9425         g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
9426     g.NavNextActivateId = 0;
9427 
9428     // Initiate directional inputs request
9429     if (g.NavMoveRequestForward == ImGuiNavForward_None)
9430     {
9431         g.NavMoveDir = ImGuiDir_None;
9432         g.NavMoveRequestFlags = ImGuiNavMoveFlags_None;
9433         if (g.NavWindow && !g.NavWindowingTarget && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
9434         {
9435             const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat;
9436             if (!IsActiveIdUsingNavDir(ImGuiDir_Left)  && (IsNavInputTest(ImGuiNavInput_DpadLeft,  read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_,  read_mode))) { g.NavMoveDir = ImGuiDir_Left; }
9437             if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; }
9438             if (!IsActiveIdUsingNavDir(ImGuiDir_Up)    && (IsNavInputTest(ImGuiNavInput_DpadUp,    read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_,    read_mode))) { g.NavMoveDir = ImGuiDir_Up; }
9439             if (!IsActiveIdUsingNavDir(ImGuiDir_Down)  && (IsNavInputTest(ImGuiNavInput_DpadDown,  read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_,  read_mode))) { g.NavMoveDir = ImGuiDir_Down; }
9440         }
9441         g.NavMoveClipDir = g.NavMoveDir;
9442     }
9443     else
9444     {
9445         // 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)
9446         // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function)
9447         IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
9448         IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
9449         g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
9450     }
9451 
9452     // Update PageUp/PageDown/Home/End scroll
9453     // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
9454     float nav_scoring_rect_offset_y = 0.0f;
9455     if (nav_keyboard_active)
9456         nav_scoring_rect_offset_y = NavUpdatePageUpPageDown();
9457 
9458     // 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
9459     if (g.NavMoveDir != ImGuiDir_None)
9460     {
9461         g.NavMoveRequest = true;
9462         g.NavMoveRequestKeyMods = g.IO.KeyMods;
9463         g.NavMoveDirLast = g.NavMoveDir;
9464     }
9465     if (g.NavMoveRequest && g.NavId == 0)
9466     {
9467         //IMGUI_DEBUG_LOG("[Nav] NavInitRequest from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer);
9468         g.NavInitRequest = g.NavInitRequestFromMove = true;
9469         // Reassigning with same value, we're being explicit here.
9470         g.NavInitResultId = 0;     // -V1048
9471         g.NavDisableHighlight = false;
9472     }
9473     NavUpdateAnyRequestFlag();
9474 
9475     // Scrolling
9476     if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
9477     {
9478         // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
9479         ImGuiWindow* window = g.NavWindow;
9480         const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * g.IO.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.
9481         if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
9482         {
9483             if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
9484                 SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
9485             if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
9486                 SetScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
9487         }
9488 
9489         // *Normal* Manual scroll with NavScrollXXX keys
9490         // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
9491         ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f / 10.0f, 10.0f);
9492         if (scroll_dir.x != 0.0f && window->ScrollbarX)
9493         {
9494             SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
9495             g.NavMoveFromClampedRefRect = true;
9496         }
9497         if (scroll_dir.y != 0.0f)
9498         {
9499             SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
9500             g.NavMoveFromClampedRefRect = true;
9501         }
9502     }
9503 
9504     // Reset search results
9505     g.NavMoveResultLocal.Clear();
9506     g.NavMoveResultLocalVisibleSet.Clear();
9507     g.NavMoveResultOther.Clear();
9508 
9509     // When we have manually scrolled (without using navigation) and NavId becomes out of bounds, we project its bounding box to the visible area to restart navigation within visible items
9510     if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == ImGuiNavLayer_Main)
9511     {
9512         ImGuiWindow* window = g.NavWindow;
9513         ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1));
9514         if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
9515         {
9516             float pad = window->CalcFontSize() * 0.5f;
9517             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
9518             window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel);
9519             g.NavId = g.NavFocusScopeId = 0;
9520         }
9521         g.NavMoveFromClampedRefRect = false;
9522     }
9523 
9524     // 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)
9525     ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);
9526     g.NavScoringRect = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : ImRect(0, 0, 0, 0);
9527     g.NavScoringRect.TranslateY(nav_scoring_rect_offset_y);
9528     g.NavScoringRect.Min.x = ImMin(g.NavScoringRect.Min.x + 1.0f, g.NavScoringRect.Max.x);
9529     g.NavScoringRect.Max.x = g.NavScoringRect.Min.x;
9530     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().
9531     //GetForegroundDrawList()->AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
9532     g.NavScoringCount = 0;
9533 #if IMGUI_DEBUG_NAV_RECTS
9534     if (g.NavWindow)
9535     {
9536         ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow);
9537         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]
9538         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); }
9539     }
9540 #endif
9541 }
9542 
9543 // Apply result from previous frame navigation directional move request
NavUpdateMoveResult()9544 static void ImGui::NavUpdateMoveResult()
9545 {
9546     ImGuiContext& g = *GImGui;
9547     if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
9548     {
9549         // 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)
9550         if (g.NavId != 0)
9551         {
9552             g.NavDisableHighlight = false;
9553             g.NavDisableMouseHover = true;
9554         }
9555         return;
9556     }
9557 
9558     // Select which result to use
9559     ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
9560 
9561     // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
9562     if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
9563         if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId)
9564             result = &g.NavMoveResultLocalVisibleSet;
9565 
9566     // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
9567     if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
9568         if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
9569             result = &g.NavMoveResultOther;
9570     IM_ASSERT(g.NavWindow && result->Window);
9571 
9572     // Scroll to keep newly navigated item fully into view.
9573     if (g.NavLayer == ImGuiNavLayer_Main)
9574     {
9575         ImVec2 delta_scroll;
9576         if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_ScrollToEdge)
9577         {
9578             float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
9579             delta_scroll.y = result->Window->Scroll.y - scroll_target;
9580             SetScrollY(result->Window, scroll_target);
9581         }
9582         else
9583         {
9584             ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos);
9585             delta_scroll = ScrollToBringRectIntoView(result->Window, rect_abs);
9586         }
9587 
9588         // Offset our result position so mouse position can be applied immediately after in NavUpdate()
9589         result->RectRel.TranslateX(-delta_scroll.x);
9590         result->RectRel.TranslateY(-delta_scroll.y);
9591     }
9592 
9593     ClearActiveID();
9594     g.NavWindow = result->Window;
9595     if (g.NavId != result->ID)
9596     {
9597         // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
9598         g.NavJustMovedToId = result->ID;
9599         g.NavJustMovedToFocusScopeId = result->FocusScopeId;
9600         g.NavJustMovedToKeyMods = g.NavMoveRequestKeyMods;
9601     }
9602     SetNavIDWithRectRel(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
9603     g.NavMoveFromClampedRefRect = false;
9604 }
9605 
9606 // Handle PageUp/PageDown/Home/End keys
NavUpdatePageUpPageDown()9607 static float ImGui::NavUpdatePageUpPageDown()
9608 {
9609     ImGuiContext& g = *GImGui;
9610     if (g.NavMoveDir != ImGuiDir_None || g.NavWindow == NULL)
9611         return 0.0f;
9612     if ((g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != ImGuiNavLayer_Main)
9613         return 0.0f;
9614 
9615     ImGuiWindow* window = g.NavWindow;
9616     const bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp);
9617     const bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown);
9618     const bool home_pressed = IsKeyPressed(g.IO.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home);
9619     const bool end_pressed = IsKeyPressed(g.IO.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End);
9620     if (page_up_held != page_down_held || home_pressed != end_pressed) // If either (not both) are pressed
9621     {
9622         if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll)
9623         {
9624             // Fallback manual-scroll when window has no navigable item
9625             if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
9626                 SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
9627             else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
9628                 SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
9629             else if (home_pressed)
9630                 SetScrollY(window, 0.0f);
9631             else if (end_pressed)
9632                 SetScrollY(window, window->ScrollMax.y);
9633         }
9634         else
9635         {
9636             ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
9637             const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
9638             float nav_scoring_rect_offset_y = 0.0f;
9639             if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
9640             {
9641                 nav_scoring_rect_offset_y = -page_offset_y;
9642                 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)
9643                 g.NavMoveClipDir = ImGuiDir_Up;
9644                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
9645             }
9646             else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
9647             {
9648                 nav_scoring_rect_offset_y = +page_offset_y;
9649                 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)
9650                 g.NavMoveClipDir = ImGuiDir_Down;
9651                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
9652             }
9653             else if (home_pressed)
9654             {
9655                 // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
9656                 // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result.
9657                 // Preserve current horizontal position if we have any.
9658                 nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y;
9659                 if (nav_rect_rel.IsInverted())
9660                     nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
9661                 g.NavMoveDir = ImGuiDir_Down;
9662                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
9663             }
9664             else if (end_pressed)
9665             {
9666                 nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y;
9667                 if (nav_rect_rel.IsInverted())
9668                     nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
9669                 g.NavMoveDir = ImGuiDir_Up;
9670                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
9671             }
9672             return nav_scoring_rect_offset_y;
9673         }
9674     }
9675     return 0.0f;
9676 }
9677 
NavEndFrame()9678 static void ImGui::NavEndFrame()
9679 {
9680     ImGuiContext& g = *GImGui;
9681 
9682     // Show CTRL+TAB list window
9683     if (g.NavWindowingTarget != NULL)
9684         NavUpdateWindowingOverlay();
9685 
9686     // Perform wrap-around in menus
9687     ImGuiWindow* window = g.NavWrapRequestWindow;
9688     ImGuiNavMoveFlags move_flags = g.NavWrapRequestFlags;
9689     if (window != NULL && g.NavWindow == window && NavMoveRequestButNoResultYet() && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == ImGuiNavLayer_Main)
9690     {
9691         IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
9692         ImRect bb_rel = window->NavRectRel[0];
9693 
9694         ImGuiDir clip_dir = g.NavMoveDir;
9695         if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
9696         {
9697             bb_rel.Min.x = bb_rel.Max.x =
9698                 ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x;
9699             if (move_flags & ImGuiNavMoveFlags_WrapX)
9700             {
9701                 bb_rel.TranslateY(-bb_rel.GetHeight());
9702                 clip_dir = ImGuiDir_Up;
9703             }
9704             NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
9705         }
9706         if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
9707         {
9708             bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
9709             if (move_flags & ImGuiNavMoveFlags_WrapX)
9710             {
9711                 bb_rel.TranslateY(+bb_rel.GetHeight());
9712                 clip_dir = ImGuiDir_Down;
9713             }
9714             NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
9715         }
9716         if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
9717         {
9718             bb_rel.Min.y = bb_rel.Max.y =
9719                 ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y;
9720             if (move_flags & ImGuiNavMoveFlags_WrapY)
9721             {
9722                 bb_rel.TranslateX(-bb_rel.GetWidth());
9723                 clip_dir = ImGuiDir_Left;
9724             }
9725             NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
9726         }
9727         if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
9728         {
9729             bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
9730             if (move_flags & ImGuiNavMoveFlags_WrapY)
9731             {
9732                 bb_rel.TranslateX(+bb_rel.GetWidth());
9733                 clip_dir = ImGuiDir_Right;
9734             }
9735             NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
9736         }
9737     }
9738 }
9739 
FindWindowFocusIndex(ImGuiWindow * window)9740 static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N)
9741 {
9742     ImGuiContext& g = *GImGui;
9743     for (int i = g.WindowsFocusOrder.Size - 1; i >= 0; i--)
9744         if (g.WindowsFocusOrder[i] == window)
9745             return i;
9746     return -1;
9747 }
9748 
FindWindowNavFocusable(int i_start,int i_stop,int dir)9749 static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
9750 {
9751     ImGuiContext& g = *GImGui;
9752     for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
9753         if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
9754             return g.WindowsFocusOrder[i];
9755     return NULL;
9756 }
9757 
NavUpdateWindowingHighlightWindow(int focus_change_dir)9758 static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
9759 {
9760     ImGuiContext& g = *GImGui;
9761     IM_ASSERT(g.NavWindowingTarget);
9762     if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
9763         return;
9764 
9765     const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget);
9766     ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
9767     if (!window_target)
9768         window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
9769     if (window_target) // Don't reset windowing target if there's a single window in the list
9770         g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
9771     g.NavWindowingToggleLayer = false;
9772 }
9773 
9774 // Windowing management mode
9775 // Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer)
9776 // Gamepad:  Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)
NavUpdateWindowing()9777 static void ImGui::NavUpdateWindowing()
9778 {
9779     ImGuiContext& g = *GImGui;
9780     ImGuiWindow* apply_focus_window = NULL;
9781     bool apply_toggle_layer = false;
9782 
9783     ImGuiWindow* modal_window = GetTopMostPopupModal();
9784     if (modal_window != NULL)
9785     {
9786         g.NavWindowingTarget = NULL;
9787         return;
9788     }
9789 
9790     // Fade out
9791     if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
9792     {
9793         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f);
9794         if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
9795             g.NavWindowingTargetAnim = NULL;
9796     }
9797 
9798     // Start CTRL-TAB or Square+L/R window selection
9799     bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
9800     bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
9801     if (start_windowing_with_gamepad || start_windowing_with_keyboard)
9802         if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
9803         {
9804             g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindowDockStop;
9805             g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
9806             g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true;
9807             g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad;
9808         }
9809 
9810     // Gamepad update
9811     g.NavWindowingTimer += g.IO.DeltaTime;
9812     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad)
9813     {
9814         // 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
9815         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
9816 
9817         // Select window to focus
9818         const int focus_change_dir = (int)IsNavInputTest(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputTest(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow);
9819         if (focus_change_dir != 0)
9820         {
9821             NavUpdateWindowingHighlightWindow(focus_change_dir);
9822             g.NavWindowingHighlightAlpha = 1.0f;
9823         }
9824 
9825         // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)
9826         if (!IsNavInputDown(ImGuiNavInput_Menu))
9827         {
9828             g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
9829             if (g.NavWindowingToggleLayer && g.NavWindow)
9830                 apply_toggle_layer = true;
9831             else if (!g.NavWindowingToggleLayer)
9832                 apply_focus_window = g.NavWindowingTarget;
9833             g.NavWindowingTarget = NULL;
9834         }
9835     }
9836 
9837     // Keyboard: Focus
9838     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard)
9839     {
9840         // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
9841         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
9842         if (IsKeyPressedMap(ImGuiKey_Tab, true))
9843             NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1);
9844         if (!g.IO.KeyCtrl)
9845             apply_focus_window = g.NavWindowingTarget;
9846     }
9847 
9848     // Keyboard: Press and Release ALT to toggle menu layer
9849     // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of back-end clearing releases all keys on ALT-TAB
9850     if (IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Pressed))
9851         g.NavWindowingToggleLayer = true;
9852     if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && g.NavWindowingToggleLayer && IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released))
9853         if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev))
9854             apply_toggle_layer = true;
9855 
9856     // Move window
9857     if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
9858     {
9859         ImVec2 move_delta;
9860         if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift)
9861             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
9862         if (g.NavInputSource == ImGuiInputSource_NavGamepad)
9863             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down);
9864         if (move_delta.x != 0.0f || move_delta.y != 0.0f)
9865         {
9866             const float NAV_MOVE_SPEED = 800.0f;
9867             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
9868             ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow;
9869             SetWindowPos(moving_window, moving_window->Pos + move_delta * move_speed, ImGuiCond_Always);
9870             MarkIniSettingsDirty(moving_window);
9871             g.NavDisableMouseHover = true;
9872         }
9873     }
9874 
9875     // Apply final focus
9876     if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindowDockStop))
9877     {
9878         ImGuiViewport* previous_viewport = g.NavWindow ? g.NavWindow->Viewport : NULL;
9879         ClearActiveID();
9880         g.NavDisableHighlight = false;
9881         g.NavDisableMouseHover = true;
9882         apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
9883         ClosePopupsOverWindow(apply_focus_window, false);
9884         FocusWindow(apply_focus_window);
9885         if (apply_focus_window->NavLastIds[0] == 0)
9886             NavInitWindow(apply_focus_window, false);
9887 
9888         // If the window only has a menu layer, select it directly
9889         if (apply_focus_window->DC.NavLayerActiveMask == (1 << ImGuiNavLayer_Menu))
9890             g.NavLayer = ImGuiNavLayer_Menu;
9891 
9892         // Request OS level focus
9893         if (apply_focus_window->Viewport != previous_viewport && g.PlatformIO.Platform_SetWindowFocus)
9894             g.PlatformIO.Platform_SetWindowFocus(apply_focus_window->Viewport);
9895     }
9896     if (apply_focus_window)
9897         g.NavWindowingTarget = NULL;
9898 
9899     // Apply menu/layer toggle
9900     if (apply_toggle_layer && g.NavWindow)
9901     {
9902         // Move to parent menu if necessary
9903         ImGuiWindow* new_nav_window = g.NavWindow;
9904         while (new_nav_window->ParentWindow
9905             && (new_nav_window->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) == 0
9906             && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
9907             && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
9908             new_nav_window = new_nav_window->ParentWindow;
9909         if (new_nav_window != g.NavWindow)
9910         {
9911             ImGuiWindow* old_nav_window = g.NavWindow;
9912             FocusWindow(new_nav_window);
9913             new_nav_window->NavLastChildNavWindow = old_nav_window;
9914         }
9915         g.NavDisableHighlight = false;
9916         g.NavDisableMouseHover = true;
9917 
9918         // When entering a regular menu bar with the Alt key, we always reinitialize the navigation ID. It however persist on docking tab tabs.
9919         const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;
9920         const bool preserve_layer_1_nav_id = (new_nav_window->DockNodeAsHost != NULL);
9921         if (new_nav_layer == ImGuiNavLayer_Menu && !preserve_layer_1_nav_id)
9922             g.NavWindow->NavLastIds[ImGuiNavLayer_Menu] = 0;
9923         NavRestoreLayer(new_nav_layer);
9924     }
9925 }
9926 
9927 // Window has already passed the IsWindowNavFocusable()
GetFallbackWindowNameForWindowingList(ImGuiWindow * window)9928 static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
9929 {
9930     if (window->Flags & ImGuiWindowFlags_Popup)
9931         return "(Popup)";
9932     if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
9933         return "(Main menu bar)";
9934     if (window->DockNodeAsHost)
9935         return "(Dock node)";
9936     return "(Untitled)";
9937 }
9938 
9939 // Overlay displayed when using CTRL+TAB. Called by EndFrame().
NavUpdateWindowingOverlay()9940 void ImGui::NavUpdateWindowingOverlay()
9941 {
9942     ImGuiContext& g = *GImGui;
9943     IM_ASSERT(g.NavWindowingTarget != NULL);
9944 
9945     if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
9946         return;
9947 
9948     if (g.NavWindowingListWindow == NULL)
9949         g.NavWindowingListWindow = FindWindowByName("###NavWindowingList");
9950     ImGuiViewportP* viewport = /*g.NavWindow ? g.NavWindow->Viewport :*/ (ImGuiViewportP*)GetMainViewport();
9951     SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
9952     SetNextWindowPos(viewport->Pos + viewport->Size * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
9953     PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
9954     Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
9955     for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
9956     {
9957         ImGuiWindow* window = g.WindowsFocusOrder[n];
9958         if (!IsWindowNavFocusable(window))
9959             continue;
9960         const char* label = window->Name;
9961         if (label == FindRenderedTextEnd(label))
9962             label = GetFallbackWindowNameForWindowingList(window);
9963         Selectable(label, g.NavWindowingTarget == window);
9964     }
9965     End();
9966     PopStyleVar();
9967 }
9968 
9969 
9970 //-----------------------------------------------------------------------------
9971 // [SECTION] DRAG AND DROP
9972 //-----------------------------------------------------------------------------
9973 
ClearDragDrop()9974 void ImGui::ClearDragDrop()
9975 {
9976     ImGuiContext& g = *GImGui;
9977     g.DragDropActive = false;
9978     g.DragDropPayload.Clear();
9979     g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
9980     g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
9981     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
9982     g.DragDropAcceptFrameCount = -1;
9983 
9984     g.DragDropPayloadBufHeap.clear();
9985     memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
9986 }
9987 
9988 // Call when current ID is active.
9989 // When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
BeginDragDropSource(ImGuiDragDropFlags flags)9990 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
9991 {
9992     ImGuiContext& g = *GImGui;
9993     ImGuiWindow* window = g.CurrentWindow;
9994 
9995     bool source_drag_active = false;
9996     ImGuiID source_id = 0;
9997     ImGuiID source_parent_id = 0;
9998     ImGuiMouseButton mouse_button = ImGuiMouseButton_Left;
9999     if (!(flags & ImGuiDragDropFlags_SourceExtern))
10000     {
10001         source_id = window->DC.LastItemId;
10002         if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case
10003             return false;
10004         if (g.IO.MouseDown[mouse_button] == false)
10005             return false;
10006 
10007         if (source_id == 0)
10008         {
10009             // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
10010             // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
10011             if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
10012             {
10013                 IM_ASSERT(0);
10014                 return false;
10015             }
10016 
10017             // Early out
10018             if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
10019                 return false;
10020 
10021             // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
10022             // We build a throwaway ID based on current ID stack + relative AABB of items in window.
10023             // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
10024             // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
10025             source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect);
10026             bool is_hovered = ItemHoverable(window->DC.LastItemRect, source_id);
10027             if (is_hovered && g.IO.MouseClicked[mouse_button])
10028             {
10029                 SetActiveID(source_id, window);
10030                 FocusWindow(window);
10031             }
10032             if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
10033                 g.ActiveIdAllowOverlap = is_hovered;
10034         }
10035         else
10036         {
10037             g.ActiveIdAllowOverlap = false;
10038         }
10039         if (g.ActiveId != source_id)
10040             return false;
10041         source_parent_id = window->IDStack.back();
10042         source_drag_active = IsMouseDragging(mouse_button);
10043 
10044         // Disable navigation and key inputs while dragging
10045         g.ActiveIdUsingNavDirMask = ~(ImU32)0;
10046         g.ActiveIdUsingNavInputMask = ~(ImU32)0;
10047         g.ActiveIdUsingKeyInputMask = ~(ImU64)0;
10048     }
10049     else
10050     {
10051         window = NULL;
10052         source_id = ImHashStr("#SourceExtern");
10053         source_drag_active = true;
10054     }
10055 
10056     if (source_drag_active)
10057     {
10058         if (!g.DragDropActive)
10059         {
10060             IM_ASSERT(source_id != 0);
10061             ClearDragDrop();
10062             ImGuiPayload& payload = g.DragDropPayload;
10063             payload.SourceId = source_id;
10064             payload.SourceParentId = source_parent_id;
10065             g.DragDropActive = true;
10066             g.DragDropSourceFlags = flags;
10067             g.DragDropMouseButton = mouse_button;
10068         }
10069         g.DragDropSourceFrameCount = g.FrameCount;
10070         g.DragDropWithinSource = true;
10071 
10072         if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
10073         {
10074             // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
10075             // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
10076             BeginTooltip();
10077             if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
10078             {
10079                 ImGuiWindow* tooltip_window = g.CurrentWindow;
10080                 tooltip_window->SkipItems = true;
10081                 tooltip_window->HiddenFramesCanSkipItems = 1;
10082             }
10083         }
10084 
10085         if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
10086             window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
10087 
10088         return true;
10089     }
10090     return false;
10091 }
10092 
EndDragDropSource()10093 void ImGui::EndDragDropSource()
10094 {
10095     ImGuiContext& g = *GImGui;
10096     IM_ASSERT(g.DragDropActive);
10097     IM_ASSERT(g.DragDropWithinSource && "Not after a BeginDragDropSource()?");
10098 
10099     if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
10100         EndTooltip();
10101 
10102     // Discard the drag if have not called SetDragDropPayload()
10103     if (g.DragDropPayload.DataFrameCount == -1)
10104         ClearDragDrop();
10105     g.DragDropWithinSource = false;
10106 }
10107 
10108 // 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)10109 bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
10110 {
10111     ImGuiContext& g = *GImGui;
10112     ImGuiPayload& payload = g.DragDropPayload;
10113     if (cond == 0)
10114         cond = ImGuiCond_Always;
10115 
10116     IM_ASSERT(type != NULL);
10117     IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
10118     IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
10119     IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
10120     IM_ASSERT(payload.SourceId != 0);                               // Not called between BeginDragDropSource() and EndDragDropSource()
10121 
10122     if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
10123     {
10124         // Copy payload
10125         ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
10126         g.DragDropPayloadBufHeap.resize(0);
10127         if (data_size > sizeof(g.DragDropPayloadBufLocal))
10128         {
10129             // Store in heap
10130             g.DragDropPayloadBufHeap.resize((int)data_size);
10131             payload.Data = g.DragDropPayloadBufHeap.Data;
10132             memcpy(payload.Data, data, data_size);
10133         }
10134         else if (data_size > 0)
10135         {
10136             // Store locally
10137             memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
10138             payload.Data = g.DragDropPayloadBufLocal;
10139             memcpy(payload.Data, data, data_size);
10140         }
10141         else
10142         {
10143             payload.Data = NULL;
10144         }
10145         payload.DataSize = (int)data_size;
10146     }
10147     payload.DataFrameCount = g.FrameCount;
10148 
10149     return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
10150 }
10151 
BeginDragDropTargetCustom(const ImRect & bb,ImGuiID id)10152 bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
10153 {
10154     ImGuiContext& g = *GImGui;
10155     if (!g.DragDropActive)
10156         return false;
10157 
10158     ImGuiWindow* window = g.CurrentWindow;
10159     ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
10160     if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow)
10161         return false;
10162     IM_ASSERT(id != 0);
10163     if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
10164         return false;
10165     if (window->SkipItems)
10166         return false;
10167 
10168     IM_ASSERT(g.DragDropWithinTarget == false);
10169     g.DragDropTargetRect = bb;
10170     g.DragDropTargetId = id;
10171     g.DragDropWithinTarget = true;
10172     return true;
10173 }
10174 
10175 // We don't use BeginDragDropTargetCustom() and duplicate its code because:
10176 // 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
10177 // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
10178 // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
BeginDragDropTarget()10179 bool ImGui::BeginDragDropTarget()
10180 {
10181     ImGuiContext& g = *GImGui;
10182     if (!g.DragDropActive)
10183         return false;
10184 
10185     ImGuiWindow* window = g.CurrentWindow;
10186     if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
10187         return false;
10188     ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
10189     if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow)
10190         return false;
10191 
10192     const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect;
10193     ImGuiID id = window->DC.LastItemId;
10194     if (id == 0)
10195         id = window->GetIDFromRectangle(display_rect);
10196     if (g.DragDropPayload.SourceId == id)
10197         return false;
10198 
10199     IM_ASSERT(g.DragDropWithinTarget == false);
10200     g.DragDropTargetRect = display_rect;
10201     g.DragDropTargetId = id;
10202     g.DragDropWithinTarget = true;
10203     return true;
10204 }
10205 
IsDragDropPayloadBeingAccepted()10206 bool ImGui::IsDragDropPayloadBeingAccepted()
10207 {
10208     ImGuiContext& g = *GImGui;
10209     return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
10210 }
10211 
AcceptDragDropPayload(const char * type,ImGuiDragDropFlags flags)10212 const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
10213 {
10214     ImGuiContext& g = *GImGui;
10215     ImGuiWindow* window = g.CurrentWindow;
10216     ImGuiPayload& payload = g.DragDropPayload;
10217     IM_ASSERT(g.DragDropActive);                        // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
10218     IM_ASSERT(payload.DataFrameCount != -1);            // Forgot to call EndDragDropTarget() ?
10219     if (type != NULL && !payload.IsDataType(type))
10220         return NULL;
10221 
10222     // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
10223     // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
10224     const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
10225     ImRect r = g.DragDropTargetRect;
10226     float r_surface = r.GetWidth() * r.GetHeight();
10227     if (r_surface < g.DragDropAcceptIdCurrRectSurface)
10228     {
10229         g.DragDropAcceptFlags = flags;
10230         g.DragDropAcceptIdCurr = g.DragDropTargetId;
10231         g.DragDropAcceptIdCurrRectSurface = r_surface;
10232     }
10233 
10234     // Render default drop visuals
10235     payload.Preview = was_accepted_previously;
10236     flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
10237     if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
10238     {
10239         // FIXME-DRAG: Settle on a proper default visuals for drop target.
10240         r.Expand(3.5f);
10241         bool push_clip_rect = !window->ClipRect.Contains(r);
10242         if (push_clip_rect) window->DrawList->PushClipRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1));
10243         window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f);
10244         if (push_clip_rect) window->DrawList->PopClipRect();
10245     }
10246 
10247     g.DragDropAcceptFrameCount = g.FrameCount;
10248     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()
10249     if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
10250         return NULL;
10251 
10252     return &payload;
10253 }
10254 
GetDragDropPayload()10255 const ImGuiPayload* ImGui::GetDragDropPayload()
10256 {
10257     ImGuiContext& g = *GImGui;
10258     return g.DragDropActive ? &g.DragDropPayload : NULL;
10259 }
10260 
10261 // We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
EndDragDropTarget()10262 void ImGui::EndDragDropTarget()
10263 {
10264     ImGuiContext& g = *GImGui;
10265     IM_ASSERT(g.DragDropActive);
10266     IM_ASSERT(g.DragDropWithinTarget);
10267     g.DragDropWithinTarget = false;
10268 }
10269 
10270 //-----------------------------------------------------------------------------
10271 // [SECTION] LOGGING/CAPTURING
10272 //-----------------------------------------------------------------------------
10273 // All text output from the interface can be captured into tty/file/clipboard.
10274 // By default, tree nodes are automatically opened during logging.
10275 //-----------------------------------------------------------------------------
10276 
10277 // Pass text data straight to log (without being displayed)
LogText(const char * fmt,...)10278 void ImGui::LogText(const char* fmt, ...)
10279 {
10280     ImGuiContext& g = *GImGui;
10281     if (!g.LogEnabled)
10282         return;
10283 
10284     va_list args;
10285     va_start(args, fmt);
10286     if (g.LogFile)
10287     {
10288         g.LogBuffer.Buf.resize(0);
10289         g.LogBuffer.appendfv(fmt, args);
10290         ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile);
10291     }
10292     else
10293     {
10294         g.LogBuffer.appendfv(fmt, args);
10295     }
10296     va_end(args);
10297 }
10298 
10299 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
10300 // We split text into individual lines to add current tree level padding
LogRenderedText(const ImVec2 * ref_pos,const char * text,const char * text_end)10301 void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
10302 {
10303     ImGuiContext& g = *GImGui;
10304     ImGuiWindow* window = g.CurrentWindow;
10305 
10306     if (!text_end)
10307         text_end = FindRenderedTextEnd(text, text_end);
10308 
10309     const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + 1);
10310     if (ref_pos)
10311         g.LogLinePosY = ref_pos->y;
10312     if (log_new_line)
10313         g.LogLineFirstItem = true;
10314 
10315     const char* text_remaining = text;
10316     if (g.LogDepthRef > window->DC.TreeDepth)  // Re-adjust padding if we have popped out of our starting depth
10317         g.LogDepthRef = window->DC.TreeDepth;
10318     const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);
10319     for (;;)
10320     {
10321         // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
10322         // We don't add a trailing \n to allow a subsequent item on the same line to be captured.
10323         const char* line_start = text_remaining;
10324         const char* line_end = ImStreolRange(line_start, text_end);
10325         const bool is_first_line = (line_start == text);
10326         const bool is_last_line = (line_end == text_end);
10327         if (!is_last_line || (line_start != line_end))
10328         {
10329             const int char_count = (int)(line_end - line_start);
10330             if (log_new_line || !is_first_line)
10331                 LogText(IM_NEWLINE "%*s%.*s", tree_depth * 4, "", char_count, line_start);
10332             else if (g.LogLineFirstItem)
10333                 LogText("%*s%.*s", tree_depth * 4, "", char_count, line_start);
10334             else
10335                 LogText(" %.*s", char_count, line_start);
10336             g.LogLineFirstItem = false;
10337         }
10338         else if (log_new_line)
10339         {
10340             // An empty "" string at a different Y position should output a carriage return.
10341             LogText(IM_NEWLINE);
10342             break;
10343         }
10344 
10345         if (is_last_line)
10346             break;
10347         text_remaining = line_end + 1;
10348     }
10349 }
10350 
10351 // Start logging/capturing text output
LogBegin(ImGuiLogType type,int auto_open_depth)10352 void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth)
10353 {
10354     ImGuiContext& g = *GImGui;
10355     ImGuiWindow* window = g.CurrentWindow;
10356     IM_ASSERT(g.LogEnabled == false);
10357     IM_ASSERT(g.LogFile == NULL);
10358     IM_ASSERT(g.LogBuffer.empty());
10359     g.LogEnabled = true;
10360     g.LogType = type;
10361     g.LogDepthRef = window->DC.TreeDepth;
10362     g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);
10363     g.LogLinePosY = FLT_MAX;
10364     g.LogLineFirstItem = true;
10365 }
10366 
LogToTTY(int auto_open_depth)10367 void ImGui::LogToTTY(int auto_open_depth)
10368 {
10369     ImGuiContext& g = *GImGui;
10370     if (g.LogEnabled)
10371         return;
10372     IM_UNUSED(auto_open_depth);
10373 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
10374     LogBegin(ImGuiLogType_TTY, auto_open_depth);
10375     g.LogFile = stdout;
10376 #endif
10377 }
10378 
10379 // Start logging/capturing text output to given file
LogToFile(int auto_open_depth,const char * filename)10380 void ImGui::LogToFile(int auto_open_depth, const char* filename)
10381 {
10382     ImGuiContext& g = *GImGui;
10383     if (g.LogEnabled)
10384         return;
10385 
10386     // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still
10387     // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE.
10388     // By opening the file in binary mode "ab" we have consistent output everywhere.
10389     if (!filename)
10390         filename = g.IO.LogFilename;
10391     if (!filename || !filename[0])
10392         return;
10393     ImFileHandle f = ImFileOpen(filename, "ab");
10394     if (!f)
10395     {
10396         IM_ASSERT(0);
10397         return;
10398     }
10399 
10400     LogBegin(ImGuiLogType_File, auto_open_depth);
10401     g.LogFile = f;
10402 }
10403 
10404 // Start logging/capturing text output to clipboard
LogToClipboard(int auto_open_depth)10405 void ImGui::LogToClipboard(int auto_open_depth)
10406 {
10407     ImGuiContext& g = *GImGui;
10408     if (g.LogEnabled)
10409         return;
10410     LogBegin(ImGuiLogType_Clipboard, auto_open_depth);
10411 }
10412 
LogToBuffer(int auto_open_depth)10413 void ImGui::LogToBuffer(int auto_open_depth)
10414 {
10415     ImGuiContext& g = *GImGui;
10416     if (g.LogEnabled)
10417         return;
10418     LogBegin(ImGuiLogType_Buffer, auto_open_depth);
10419 }
10420 
LogFinish()10421 void ImGui::LogFinish()
10422 {
10423     ImGuiContext& g = *GImGui;
10424     if (!g.LogEnabled)
10425         return;
10426 
10427     LogText(IM_NEWLINE);
10428     switch (g.LogType)
10429     {
10430     case ImGuiLogType_TTY:
10431 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
10432         fflush(g.LogFile);
10433 #endif
10434         break;
10435     case ImGuiLogType_File:
10436         ImFileClose(g.LogFile);
10437         break;
10438     case ImGuiLogType_Buffer:
10439         break;
10440     case ImGuiLogType_Clipboard:
10441         if (!g.LogBuffer.empty())
10442             SetClipboardText(g.LogBuffer.begin());
10443         break;
10444     case ImGuiLogType_None:
10445         IM_ASSERT(0);
10446         break;
10447     }
10448 
10449     g.LogEnabled = false;
10450     g.LogType = ImGuiLogType_None;
10451     g.LogFile = NULL;
10452     g.LogBuffer.clear();
10453 }
10454 
10455 // Helper to display logging buttons
10456 // FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!)
LogButtons()10457 void ImGui::LogButtons()
10458 {
10459     ImGuiContext& g = *GImGui;
10460 
10461     PushID("LogButtons");
10462 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
10463     const bool log_to_tty = Button("Log To TTY"); SameLine();
10464 #else
10465     const bool log_to_tty = false;
10466 #endif
10467     const bool log_to_file = Button("Log To File"); SameLine();
10468     const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
10469     PushAllowKeyboardFocus(false);
10470     SetNextItemWidth(80.0f);
10471     SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL);
10472     PopAllowKeyboardFocus();
10473     PopID();
10474 
10475     // Start logging at the end of the function so that the buttons don't appear in the log
10476     if (log_to_tty)
10477         LogToTTY();
10478     if (log_to_file)
10479         LogToFile();
10480     if (log_to_clipboard)
10481         LogToClipboard();
10482 }
10483 
10484 
10485 //-----------------------------------------------------------------------------
10486 // [SECTION] SETTINGS
10487 //-----------------------------------------------------------------------------
10488 // - UpdateSettings() [Internal]
10489 // - MarkIniSettingsDirty() [Internal]
10490 // - CreateNewWindowSettings() [Internal]
10491 // - FindWindowSettings() [Internal]
10492 // - FindOrCreateWindowSettings() [Internal]
10493 // - FindSettingsHandler() [Internal]
10494 // - ClearIniSettings() [Internal]
10495 // - LoadIniSettingsFromDisk()
10496 // - LoadIniSettingsFromMemory()
10497 // - SaveIniSettingsToDisk()
10498 // - SaveIniSettingsToMemory()
10499 // - WindowSettingsHandler_***() [Internal]
10500 //-----------------------------------------------------------------------------
10501 
10502 // Called by NewFrame()
UpdateSettings()10503 void ImGui::UpdateSettings()
10504 {
10505     // Load settings on first frame (if not explicitly loaded manually before)
10506     ImGuiContext& g = *GImGui;
10507     if (!g.SettingsLoaded)
10508     {
10509         IM_ASSERT(g.SettingsWindows.empty());
10510         if (g.IO.IniFilename)
10511             LoadIniSettingsFromDisk(g.IO.IniFilename);
10512         g.SettingsLoaded = true;
10513     }
10514 
10515     // Save settings (with a delay after the last modification, so we don't spam disk too much)
10516     if (g.SettingsDirtyTimer > 0.0f)
10517     {
10518         g.SettingsDirtyTimer -= g.IO.DeltaTime;
10519         if (g.SettingsDirtyTimer <= 0.0f)
10520         {
10521             if (g.IO.IniFilename != NULL)
10522                 SaveIniSettingsToDisk(g.IO.IniFilename);
10523             else
10524                 g.IO.WantSaveIniSettings = true;  // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
10525             g.SettingsDirtyTimer = 0.0f;
10526         }
10527     }
10528 }
10529 
MarkIniSettingsDirty()10530 void ImGui::MarkIniSettingsDirty()
10531 {
10532     ImGuiContext& g = *GImGui;
10533     if (g.SettingsDirtyTimer <= 0.0f)
10534         g.SettingsDirtyTimer = g.IO.IniSavingRate;
10535 }
10536 
MarkIniSettingsDirty(ImGuiWindow * window)10537 void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
10538 {
10539     ImGuiContext& g = *GImGui;
10540     if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
10541         if (g.SettingsDirtyTimer <= 0.0f)
10542             g.SettingsDirtyTimer = g.IO.IniSavingRate;
10543 }
10544 
CreateNewWindowSettings(const char * name)10545 ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
10546 {
10547     ImGuiContext& g = *GImGui;
10548 
10549 #if !IMGUI_DEBUG_INI_SETTINGS
10550     // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
10551     // Preserve the full string when IMGUI_DEBUG_INI_SETTINGS is set to make .ini inspection easier.
10552     if (const char* p = strstr(name, "###"))
10553         name = p;
10554 #endif
10555     const size_t name_len = strlen(name);
10556 
10557     // Allocate chunk
10558     const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1;
10559     ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size);
10560     IM_PLACEMENT_NEW(settings) ImGuiWindowSettings();
10561     settings->ID = ImHashStr(name, name_len);
10562     memcpy(settings->GetName(), name, name_len + 1);   // Store with zero terminator
10563 
10564     return settings;
10565 }
10566 
FindWindowSettings(ImGuiID id)10567 ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
10568 {
10569     ImGuiContext& g = *GImGui;
10570     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
10571         if (settings->ID == id)
10572             return settings;
10573     return NULL;
10574 }
10575 
FindOrCreateWindowSettings(const char * name)10576 ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name)
10577 {
10578     if (ImGuiWindowSettings* settings = FindWindowSettings(ImHashStr(name)))
10579         return settings;
10580     return CreateNewWindowSettings(name);
10581 }
10582 
FindSettingsHandler(const char * type_name)10583 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
10584 {
10585     ImGuiContext& g = *GImGui;
10586     const ImGuiID type_hash = ImHashStr(type_name);
10587     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10588         if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
10589             return &g.SettingsHandlers[handler_n];
10590     return NULL;
10591 }
10592 
ClearIniSettings()10593 void ImGui::ClearIniSettings()
10594 {
10595     ImGuiContext& g = *GImGui;
10596     g.SettingsIniData.clear();
10597     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10598         if (g.SettingsHandlers[handler_n].ClearAllFn)
10599             g.SettingsHandlers[handler_n].ClearAllFn(&g, &g.SettingsHandlers[handler_n]);
10600 }
10601 
LoadIniSettingsFromDisk(const char * ini_filename)10602 void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
10603 {
10604     size_t file_data_size = 0;
10605     char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
10606     if (!file_data)
10607         return;
10608     LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
10609     IM_FREE(file_data);
10610 }
10611 
10612 // Zero-tolerance, no error reporting, cheap .ini parsing
LoadIniSettingsFromMemory(const char * ini_data,size_t ini_size)10613 void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
10614 {
10615     ImGuiContext& g = *GImGui;
10616     IM_ASSERT(g.Initialized);
10617     //IM_ASSERT(!g.WithinFrameScope && "Cannot be called between NewFrame() and EndFrame()");
10618     //IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
10619 
10620     // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
10621     // 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..
10622     if (ini_size == 0)
10623         ini_size = strlen(ini_data);
10624     g.SettingsIniData.Buf.resize((int)ini_size + 1);
10625     char* const buf = g.SettingsIniData.Buf.Data;
10626     char* const buf_end = buf + ini_size;
10627     memcpy(buf, ini_data, ini_size);
10628     buf_end[0] = 0;
10629 
10630     // Call pre-read handlers
10631     // Some types will clear their data (e.g. dock information) some types will allow merge/override (window)
10632     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10633         if (g.SettingsHandlers[handler_n].ReadInitFn)
10634             g.SettingsHandlers[handler_n].ReadInitFn(&g, &g.SettingsHandlers[handler_n]);
10635 
10636     void* entry_data = NULL;
10637     ImGuiSettingsHandler* entry_handler = NULL;
10638 
10639     char* line_end = NULL;
10640     for (char* line = buf; line < buf_end; line = line_end + 1)
10641     {
10642         // Skip new lines markers, then find end of the line
10643         while (*line == '\n' || *line == '\r')
10644             line++;
10645         line_end = line;
10646         while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
10647             line_end++;
10648         line_end[0] = 0;
10649         if (line[0] == ';')
10650             continue;
10651         if (line[0] == '[' && line_end > line && line_end[-1] == ']')
10652         {
10653             // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
10654             line_end[-1] = 0;
10655             const char* name_end = line_end - 1;
10656             const char* type_start = line + 1;
10657             char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']');
10658             const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
10659             if (!type_end || !name_start)
10660                 continue;
10661             *type_end = 0; // Overwrite first ']'
10662             name_start++;  // Skip second '['
10663             entry_handler = FindSettingsHandler(type_start);
10664             entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
10665         }
10666         else if (entry_handler != NULL && entry_data != NULL)
10667         {
10668             // Let type handler parse the line
10669             entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
10670         }
10671     }
10672     g.SettingsLoaded = true;
10673 
10674     // [DEBUG] Restore untouched copy so it can be browsed in Metrics (not strictly necessary)
10675     memcpy(buf, ini_data, ini_size);
10676 
10677     // Call post-read handlers
10678     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10679         if (g.SettingsHandlers[handler_n].ApplyAllFn)
10680             g.SettingsHandlers[handler_n].ApplyAllFn(&g, &g.SettingsHandlers[handler_n]);
10681 }
10682 
SaveIniSettingsToDisk(const char * ini_filename)10683 void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
10684 {
10685     ImGuiContext& g = *GImGui;
10686     g.SettingsDirtyTimer = 0.0f;
10687     if (!ini_filename)
10688         return;
10689 
10690     size_t ini_data_size = 0;
10691     const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
10692     ImFileHandle f = ImFileOpen(ini_filename, "wt");
10693     if (!f)
10694         return;
10695     ImFileWrite(ini_data, sizeof(char), ini_data_size, f);
10696     ImFileClose(f);
10697 }
10698 
10699 // Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
SaveIniSettingsToMemory(size_t * out_size)10700 const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
10701 {
10702     ImGuiContext& g = *GImGui;
10703     g.SettingsDirtyTimer = 0.0f;
10704     g.SettingsIniData.Buf.resize(0);
10705     g.SettingsIniData.Buf.push_back(0);
10706     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10707     {
10708         ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
10709         handler->WriteAllFn(&g, handler, &g.SettingsIniData);
10710     }
10711     if (out_size)
10712         *out_size = (size_t)g.SettingsIniData.size();
10713     return g.SettingsIniData.c_str();
10714 }
10715 
WindowSettingsHandler_ClearAll(ImGuiContext * ctx,ImGuiSettingsHandler *)10716 static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
10717 {
10718     ImGuiContext& g = *ctx;
10719     for (int i = 0; i != g.Windows.Size; i++)
10720         g.Windows[i]->SettingsOffset = -1;
10721     g.SettingsWindows.clear();
10722 }
10723 
WindowSettingsHandler_ReadOpen(ImGuiContext *,ImGuiSettingsHandler *,const char * name)10724 static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
10725 {
10726     ImGuiWindowSettings* settings = ImGui::FindOrCreateWindowSettings(name);
10727     ImGuiID id = settings->ID;
10728     *settings = ImGuiWindowSettings(); // Clear existing if recycling previous entry
10729     settings->ID = id;
10730     settings->WantApply = true;
10731     return (void*)settings;
10732 }
10733 
WindowSettingsHandler_ReadLine(ImGuiContext *,ImGuiSettingsHandler *,void * entry,const char * line)10734 static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
10735 {
10736     ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
10737     int x, y;
10738     int i;
10739     ImU32 u1;
10740     if (sscanf(line, "Pos=%i,%i", &x, &y) == 2)             { settings->Pos = ImVec2ih((short)x, (short)y); }
10741     else if (sscanf(line, "Size=%i,%i", &x, &y) == 2)       { settings->Size = ImVec2ih((short)x, (short)y); }
10742     else if (sscanf(line, "ViewportId=0x%08X", &u1) == 1)   { settings->ViewportId = u1; }
10743     else if (sscanf(line, "ViewportPos=%i,%i", &x, &y) == 2){ settings->ViewportPos = ImVec2ih((short)x, (short)y); }
10744     else if (sscanf(line, "Collapsed=%d", &i) == 1)         { settings->Collapsed = (i != 0); }
10745     else if (sscanf(line, "DockId=0x%X,%d", &u1, &i) == 2)  { settings->DockId = u1; settings->DockOrder = (short)i; }
10746     else if (sscanf(line, "DockId=0x%X", &u1) == 1)         { settings->DockId = u1; settings->DockOrder = -1; }
10747     else if (sscanf(line, "ClassId=0x%X", &u1) == 1)        { settings->ClassId = u1; }
10748 }
10749 
10750 // Apply to existing windows (if any)
WindowSettingsHandler_ApplyAll(ImGuiContext * ctx,ImGuiSettingsHandler *)10751 static void WindowSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
10752 {
10753     ImGuiContext& g = *ctx;
10754     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
10755         if (settings->WantApply)
10756         {
10757             if (ImGuiWindow* window = ImGui::FindWindowByID(settings->ID))
10758                 ApplyWindowSettings(window, settings);
10759             settings->WantApply = false;
10760         }
10761 }
10762 
WindowSettingsHandler_WriteAll(ImGuiContext * ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)10763 static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
10764 {
10765     // Gather data from windows that were active during this session
10766     // (if a window wasn't opened in this session we preserve its settings)
10767     ImGuiContext& g = *ctx;
10768     for (int i = 0; i != g.Windows.Size; i++)
10769     {
10770         ImGuiWindow* window = g.Windows[i];
10771         if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
10772             continue;
10773 
10774         ImGuiWindowSettings* settings = (window->SettingsOffset != -1) ? g.SettingsWindows.ptr_from_offset(window->SettingsOffset) : ImGui::FindWindowSettings(window->ID);
10775         if (!settings)
10776         {
10777             settings = ImGui::CreateNewWindowSettings(window->Name);
10778             window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
10779         }
10780         IM_ASSERT(settings->ID == window->ID);
10781         settings->Pos = ImVec2ih(window->Pos - window->ViewportPos);
10782         settings->Size = ImVec2ih(window->SizeFull);
10783         settings->ViewportId = window->ViewportId;
10784         settings->ViewportPos = ImVec2ih(window->ViewportPos);
10785         IM_ASSERT(window->DockNode == NULL || window->DockNode->ID == window->DockId);
10786         settings->DockId = window->DockId;
10787         settings->ClassId = window->WindowClass.ClassId;
10788         settings->DockOrder = window->DockOrder;
10789         settings->Collapsed = window->Collapsed;
10790     }
10791 
10792     // Write to text buffer
10793     buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve
10794     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
10795     {
10796         const char* settings_name = settings->GetName();
10797         buf->appendf("[%s][%s]\n", handler->TypeName, settings_name);
10798         if (settings->ViewportId != 0 && settings->ViewportId != ImGui::IMGUI_VIEWPORT_DEFAULT_ID)
10799         {
10800             buf->appendf("ViewportPos=%d,%d\n", settings->ViewportPos.x, settings->ViewportPos.y);
10801             buf->appendf("ViewportId=0x%08X\n", settings->ViewportId);
10802         }
10803         if (settings->Pos.x != 0 || settings->Pos.y != 0 || settings->ViewportId == ImGui::IMGUI_VIEWPORT_DEFAULT_ID)
10804             buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
10805         if (settings->Size.x != 0 || settings->Size.y != 0)
10806             buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
10807         buf->appendf("Collapsed=%d\n", settings->Collapsed);
10808         if (settings->DockId != 0)
10809         {
10810             // Write DockId as 4 digits if possible. Automatic DockId are small numbers, but full explicit DockSpace() are full ImGuiID range.
10811             if (settings->DockOrder == -1)
10812                 buf->appendf("DockId=0x%08X\n", settings->DockId);
10813             else
10814                 buf->appendf("DockId=0x%08X,%d\n", settings->DockId, settings->DockOrder);
10815             if (settings->ClassId != 0)
10816                 buf->appendf("ClassId=0x%08X\n", settings->ClassId);
10817         }
10818         buf->append("\n");
10819     }
10820 }
10821 
10822 
10823 //-----------------------------------------------------------------------------
10824 // [SECTION] VIEWPORTS, PLATFORM WINDOWS
10825 //-----------------------------------------------------------------------------
10826 // - GetMainViewport()
10827 // - FindViewportByID()
10828 // - FindViewportByPlatformHandle()
10829 // - SetCurrentViewport() [Internal]
10830 // - SetWindowViewport() [Internal]
10831 // - GetWindowAlwaysWantOwnViewport() [Internal]
10832 // - UpdateTryMergeWindowIntoHostViewport() [Internal]
10833 // - UpdateTryMergeWindowIntoHostViewports() [Internal]
10834 // - TranslateWindowsInViewport() [Internal]
10835 // - ScaleWindowsInViewport() [Internal]
10836 // - FindHoveredViewportFromPlatformWindowStack() [Internal]
10837 // - UpdateViewportsNewFrame() [Internal]
10838 // - UpdateViewportsEndFrame() [Internal]
10839 // - AddUpdateViewport() [Internal]
10840 // - UpdateSelectWindowViewport() [Internal]
10841 // - UpdatePlatformWindows()
10842 // - RenderPlatformWindowsDefault()
10843 // - FindPlatformMonitorForPos() [Internal]
10844 // - FindPlatformMonitorForRect() [Internal]
10845 // - UpdateViewportPlatformMonitor() [Internal]
10846 // - DestroyPlatformWindow() [Internal]
10847 // - DestroyPlatformWindows()
10848 //-----------------------------------------------------------------------------
10849 
GetMainViewport()10850 ImGuiViewport* ImGui::GetMainViewport()
10851 {
10852     ImGuiContext& g = *GImGui;
10853     return g.Viewports[0];
10854 }
10855 
FindViewportByID(ImGuiID id)10856 ImGuiViewport* ImGui::FindViewportByID(ImGuiID id)
10857 {
10858     ImGuiContext& g = *GImGui;
10859     for (int n = 0; n < g.Viewports.Size; n++)
10860         if (g.Viewports[n]->ID == id)
10861             return g.Viewports[n];
10862     return NULL;
10863 }
10864 
FindViewportByPlatformHandle(void * platform_handle)10865 ImGuiViewport* ImGui::FindViewportByPlatformHandle(void* platform_handle)
10866 {
10867     ImGuiContext& g = *GImGui;
10868     for (int i = 0; i != g.Viewports.Size; i++)
10869         if (g.Viewports[i]->PlatformHandle == platform_handle)
10870             return g.Viewports[i];
10871     return NULL;
10872 }
10873 
SetCurrentViewport(ImGuiWindow * current_window,ImGuiViewportP * viewport)10874 void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* viewport)
10875 {
10876     ImGuiContext& g = *GImGui;
10877     (void)current_window;
10878 
10879     if (viewport)
10880         viewport->LastFrameActive = g.FrameCount;
10881     if (g.CurrentViewport == viewport)
10882         return;
10883     g.CurrentDpiScale = viewport ? viewport->DpiScale : 1.0f;
10884     g.CurrentViewport = viewport;
10885     //IMGUI_DEBUG_LOG_VIEWPORT("SetCurrentViewport changed '%s' 0x%08X\n", current_window ? current_window->Name : NULL, viewport ? viewport->ID : 0);
10886 
10887     // Notify platform layer of viewport changes
10888     // FIXME-DPI: This is only currently used for experimenting with handling of multiple DPI
10889     if (g.CurrentViewport && g.PlatformIO.Platform_OnChangedViewport)
10890         g.PlatformIO.Platform_OnChangedViewport(g.CurrentViewport);
10891 }
10892 
SetWindowViewport(ImGuiWindow * window,ImGuiViewportP * viewport)10893 static void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport)
10894 {
10895     window->Viewport = viewport;
10896     window->ViewportId = viewport->ID;
10897     window->ViewportOwned = (viewport->Window == window);
10898 }
10899 
GetWindowAlwaysWantOwnViewport(ImGuiWindow * window)10900 static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window)
10901 {
10902     // 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.
10903     ImGuiContext& g = *GImGui;
10904     if (g.IO.ConfigViewportsNoAutoMerge || (window->WindowClass.ViewportFlagsOverrideSet & ImGuiViewportFlags_NoAutoMerge))
10905         if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)
10906             if (!window->DockIsActive)
10907                 if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip)) == 0)
10908                     if ((window->Flags & ImGuiWindowFlags_Popup) == 0 || (window->Flags & ImGuiWindowFlags_Modal) != 0)
10909                         return true;
10910     return false;
10911 }
10912 
UpdateTryMergeWindowIntoHostViewport(ImGuiWindow * window,ImGuiViewportP * viewport)10913 static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport)
10914 {
10915     ImGuiContext& g = *GImGui;
10916     if (window->Viewport == viewport)
10917         return false;
10918     if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) == 0)
10919         return false;
10920     if ((viewport->Flags & ImGuiViewportFlags_Minimized) != 0)
10921         return false;
10922     if (!viewport->GetMainRect().Contains(window->Rect()))
10923         return false;
10924     if (GetWindowAlwaysWantOwnViewport(window))
10925         return false;
10926 
10927     for (int n = 0; n < g.Windows.Size; n++)
10928     {
10929         ImGuiWindow* window_behind = g.Windows[n];
10930         if (window_behind == window)
10931             break;
10932         if (window_behind->WasActive && window_behind->ViewportOwned && !(window_behind->Flags & ImGuiWindowFlags_ChildWindow))
10933             if (window_behind->Viewport->GetMainRect().Overlaps(window->Rect()))
10934                 return false;
10935     }
10936 
10937     // Move to the existing viewport, Move child/hosted windows as well (FIXME-OPT: iterate child)
10938     ImGuiViewportP* old_viewport = window->Viewport;
10939     if (window->ViewportOwned)
10940         for (int n = 0; n < g.Windows.Size; n++)
10941             if (g.Windows[n]->Viewport == old_viewport)
10942                 SetWindowViewport(g.Windows[n], viewport);
10943     SetWindowViewport(window, viewport);
10944     BringWindowToDisplayFront(window);
10945 
10946     return true;
10947 }
10948 
UpdateTryMergeWindowIntoHostViewports(ImGuiWindow * window)10949 static bool ImGui::UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window)
10950 {
10951     ImGuiContext& g = *GImGui;
10952     return UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]);
10953 }
10954 
10955 // Translate imgui windows when a Host Viewport has been moved
10956 // (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!)
TranslateWindowsInViewport(ImGuiViewportP * viewport,const ImVec2 & old_pos,const ImVec2 & new_pos)10957 void ImGui::TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos)
10958 {
10959     ImGuiContext& g = *GImGui;
10960     IM_ASSERT(viewport->Window == NULL && (viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows));
10961 
10962     // 1) We test if ImGuiConfigFlags_ViewportsEnable was just toggled, which allows us to conveniently
10963     // translate imgui windows from OS-window-local to absolute coordinates or vice-versa.
10964     // 2) If it's not going to fit into the new size, keep it at same absolute position.
10965     // One problem with this is that most Win32 applications doesn't update their render while dragging,
10966     // and so the window will appear to teleport when releasing the mouse.
10967     const bool translate_all_windows = (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) != (g.ConfigFlagsLastFrame & ImGuiConfigFlags_ViewportsEnable);
10968     ImRect test_still_fit_rect(old_pos, old_pos + viewport->Size);
10969     ImVec2 delta_pos = new_pos - old_pos;
10970     for (int window_n = 0; window_n < g.Windows.Size; window_n++) // FIXME-OPT
10971         if (translate_all_windows || (g.Windows[window_n]->Viewport == viewport && test_still_fit_rect.Contains(g.Windows[window_n]->Rect())))
10972             TranslateWindow(g.Windows[window_n], delta_pos);
10973 }
10974 
10975 // Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!)
ScaleWindowsInViewport(ImGuiViewportP * viewport,float scale)10976 void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale)
10977 {
10978     ImGuiContext& g = *GImGui;
10979     if (viewport->Window)
10980     {
10981         ScaleWindow(viewport->Window, scale);
10982     }
10983     else
10984     {
10985         for (int i = 0; i != g.Windows.Size; i++)
10986             if (g.Windows[i]->Viewport == viewport)
10987                 ScaleWindow(g.Windows[i], scale);
10988     }
10989 }
10990 
10991 // If the back-end doesn't set MouseLastHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs, we do a search ourselves.
10992 // A) It won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window.
10993 // B) It requires Platform_GetWindowFocus to be implemented by back-end.
FindHoveredViewportFromPlatformWindowStack(const ImVec2 mouse_platform_pos)10994 static ImGuiViewportP* FindHoveredViewportFromPlatformWindowStack(const ImVec2 mouse_platform_pos)
10995 {
10996     ImGuiContext& g = *GImGui;
10997     ImGuiViewportP* best_candidate = NULL;
10998     for (int n = 0; n < g.Viewports.Size; n++)
10999     {
11000         ImGuiViewportP* viewport = g.Viewports[n];
11001         if (!(viewport->Flags & (ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_Minimized)) && viewport->GetMainRect().Contains(mouse_platform_pos))
11002             if (best_candidate == NULL || best_candidate->LastFrontMostStampCount < viewport->LastFrontMostStampCount)
11003                 best_candidate = viewport;
11004     }
11005     return best_candidate;
11006 }
11007 
11008 // Update viewports and monitor infos
11009 // 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()11010 static void ImGui::UpdateViewportsNewFrame()
11011 {
11012     ImGuiContext& g = *GImGui;
11013     IM_ASSERT(g.PlatformIO.Viewports.Size <= g.Viewports.Size);
11014 
11015     // Update Minimized status (we need it first in order to decide if we'll apply Pos/Size of the main viewport)
11016     const bool viewports_enabled = (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) != 0;
11017     if (viewports_enabled)
11018     {
11019         for (int n = 0; n < g.Viewports.Size; n++)
11020         {
11021             ImGuiViewportP* viewport = g.Viewports[n];
11022             const bool platform_funcs_available = viewport->PlatformWindowCreated;
11023             if (g.PlatformIO.Platform_GetWindowMinimized && platform_funcs_available)
11024             {
11025                 bool minimized = g.PlatformIO.Platform_GetWindowMinimized(viewport);
11026                 if (minimized)
11027                     viewport->Flags |= ImGuiViewportFlags_Minimized;
11028                 else
11029                     viewport->Flags &= ~ImGuiViewportFlags_Minimized;
11030             }
11031         }
11032     }
11033 
11034     // Create/update main viewport with current platform position.
11035     // FIXME-VIEWPORT: Size is driven by back-end/user code for backward-compatibility but we should aim to make this more consistent.
11036     ImGuiViewportP* main_viewport = g.Viewports[0];
11037     IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID);
11038     IM_ASSERT(main_viewport->Window == NULL);
11039     ImVec2 main_viewport_pos = viewports_enabled ? g.PlatformIO.Platform_GetWindowPos(main_viewport) : ImVec2(0.0f, 0.0f);
11040     ImVec2 main_viewport_size = g.IO.DisplaySize;
11041     if (viewports_enabled && (main_viewport->Flags & ImGuiViewportFlags_Minimized))
11042     {
11043         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)
11044         main_viewport_size = main_viewport->Size;
11045     }
11046     AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_pos, main_viewport_size, ImGuiViewportFlags_CanHostOtherWindows);
11047 
11048     g.CurrentDpiScale = 0.0f;
11049     g.CurrentViewport = NULL;
11050     g.MouseViewport = NULL;
11051     for (int n = 0; n < g.Viewports.Size; n++)
11052     {
11053         ImGuiViewportP* viewport = g.Viewports[n];
11054         viewport->Idx = n;
11055 
11056         // Erase unused viewports
11057         if (n > 0 && viewport->LastFrameActive < g.FrameCount - 2)
11058         {
11059             // Clear references to this viewport in windows (window->ViewportId becomes the master data)
11060             for (int window_n = 0; window_n < g.Windows.Size; window_n++)
11061                 if (g.Windows[window_n]->Viewport == viewport)
11062                 {
11063                     g.Windows[window_n]->Viewport = NULL;
11064                     g.Windows[window_n]->ViewportOwned = false;
11065                 }
11066             if (viewport == g.MouseLastHoveredViewport)
11067                 g.MouseLastHoveredViewport = NULL;
11068             g.Viewports.erase(g.Viewports.Data + n);
11069 
11070             // Destroy
11071             IMGUI_DEBUG_LOG_VIEWPORT("Delete Viewport %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a");
11072             DestroyPlatformWindow(viewport); // In most circumstances the platform window will already be destroyed here.
11073             IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false);
11074             IM_DELETE(viewport);
11075             n--;
11076             continue;
11077         }
11078 
11079         const bool platform_funcs_available = viewport->PlatformWindowCreated;
11080         if (viewports_enabled)
11081         {
11082             // Update Position and Size (from Platform Window to ImGui) if requested.
11083             // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities.
11084             if (!(viewport->Flags & ImGuiViewportFlags_Minimized) && platform_funcs_available)
11085             {
11086                 if (viewport->PlatformRequestMove)
11087                     viewport->Pos = viewport->LastPlatformPos = g.PlatformIO.Platform_GetWindowPos(viewport);
11088                 if (viewport->PlatformRequestResize)
11089                     viewport->Size = viewport->LastPlatformSize = g.PlatformIO.Platform_GetWindowSize(viewport);
11090             }
11091         }
11092 
11093         // Update/copy monitor info
11094         UpdateViewportPlatformMonitor(viewport);
11095 
11096         // Lock down space taken by menu bars and status bars, reset the offset for fucntions like BeginMainMenuBar() to alter them again.
11097         viewport->WorkOffsetMin = viewport->CurrWorkOffsetMin;
11098         viewport->WorkOffsetMax = viewport->CurrWorkOffsetMax;
11099         viewport->CurrWorkOffsetMin = viewport->CurrWorkOffsetMax = ImVec2(0.0f, 0.0f);
11100 
11101         // Reset alpha every frame. Users of transparency (docking) needs to request a lower alpha back.
11102         viewport->Alpha = 1.0f;
11103 
11104         // Translate imgui windows when a Host Viewport has been moved
11105         // (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!)
11106         const ImVec2 viewport_delta_pos = viewport->Pos - viewport->LastPos;
11107         if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (viewport_delta_pos.x != 0.0f || viewport_delta_pos.y != 0.0f))
11108             TranslateWindowsInViewport(viewport, viewport->LastPos, viewport->Pos);
11109 
11110         // Update DPI scale
11111         float new_dpi_scale;
11112         if (g.PlatformIO.Platform_GetWindowDpiScale && platform_funcs_available)
11113             new_dpi_scale = g.PlatformIO.Platform_GetWindowDpiScale(viewport);
11114         else if (viewport->PlatformMonitor != -1)
11115             new_dpi_scale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale;
11116         else
11117             new_dpi_scale = (viewport->DpiScale != 0.0f) ? viewport->DpiScale : 1.0f;
11118         if (viewport->DpiScale != 0.0f && new_dpi_scale != viewport->DpiScale)
11119         {
11120             float scale_factor = new_dpi_scale / viewport->DpiScale;
11121             if (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports)
11122                 ScaleWindowsInViewport(viewport, scale_factor);
11123             //if (viewport == GetMainViewport())
11124             //    g.PlatformInterface.SetWindowSize(viewport, viewport->Size * scale_factor);
11125 
11126             // Scale our window moving pivot so that the window will rescale roughly around the mouse position.
11127             // FIXME-VIEWPORT: This currently creates a resizing feedback loop when a window is straddling a DPI transition border.
11128             // (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.)
11129             //if (g.MovingWindow != NULL && g.MovingWindow->Viewport == viewport)
11130             //    g.ActiveIdClickOffset = ImFloor(g.ActiveIdClickOffset * scale_factor);
11131         }
11132         viewport->DpiScale = new_dpi_scale;
11133     }
11134 
11135     if (!viewports_enabled)
11136     {
11137         g.MouseViewport = main_viewport;
11138         return;
11139     }
11140 
11141     // Mouse handling: decide on the actual mouse viewport for this frame between the active/focused viewport and the hovered viewport.
11142     // Note that 'viewport_hovered' should skip over any viewport that has the ImGuiViewportFlags_NoInputs flags set.
11143     ImGuiViewportP* viewport_hovered = NULL;
11144     if (g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport)
11145     {
11146         viewport_hovered = g.IO.MouseHoveredViewport ? (ImGuiViewportP*)FindViewportByID(g.IO.MouseHoveredViewport) : NULL;
11147         if (viewport_hovered && (viewport_hovered->Flags & ImGuiViewportFlags_NoInputs))
11148         {
11149             // Back-end failed at honoring its contract if it returned a viewport with the _NoInputs flag.
11150             IM_ASSERT(0);
11151             viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos);
11152         }
11153     }
11154     else
11155     {
11156         // If the back-end doesn't know how to honor ImGuiViewportFlags_NoInputs, we do a search ourselves. Note that this search:
11157         // A) won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window.
11158         // 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)
11159         viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos);
11160     }
11161     if (viewport_hovered != NULL)
11162         g.MouseLastHoveredViewport = viewport_hovered;
11163     else if (g.MouseLastHoveredViewport == NULL)
11164         g.MouseLastHoveredViewport = g.Viewports[0];
11165 
11166     // Update mouse reference viewport
11167     // (when moving a window we aim at its viewport, but this will be overwritten below if we go in drag and drop mode)
11168     if (g.MovingWindow)
11169         g.MouseViewport = g.MovingWindow->Viewport;
11170     else
11171         g.MouseViewport = g.MouseLastHoveredViewport;
11172 
11173     // When dragging something, always refer to the last hovered viewport.
11174     // - when releasing a moving window we will revert to aiming behind (at viewport_hovered)
11175     // - 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)
11176     // - 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.
11177     const bool is_mouse_dragging_with_an_expected_destination = g.DragDropActive;
11178     if (is_mouse_dragging_with_an_expected_destination && viewport_hovered == NULL)
11179         viewport_hovered = g.MouseLastHoveredViewport;
11180     if (is_mouse_dragging_with_an_expected_destination || g.ActiveId == 0 || !IsAnyMouseDown())
11181         if (viewport_hovered != NULL && viewport_hovered != g.MouseViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs))
11182             g.MouseViewport = viewport_hovered;
11183 
11184     IM_ASSERT(g.MouseViewport != NULL);
11185 }
11186 
11187 // Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some)
UpdateViewportsEndFrame()11188 static void ImGui::UpdateViewportsEndFrame()
11189 {
11190     ImGuiContext& g = *GImGui;
11191     g.PlatformIO.MainViewport = g.Viewports[0];
11192     g.PlatformIO.Viewports.resize(0);
11193     for (int i = 0; i < g.Viewports.Size; i++)
11194     {
11195         ImGuiViewportP* viewport = g.Viewports[i];
11196         viewport->LastPos = viewport->Pos;
11197         if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0.0f || viewport->Size.y <= 0.0f)
11198             if (i > 0) // Always include main viewport in the list
11199                 continue;
11200         if (viewport->Window && !IsWindowActiveAndVisible(viewport->Window))
11201             continue;
11202         if (i > 0)
11203             IM_ASSERT(viewport->Window != NULL);
11204         g.PlatformIO.Viewports.push_back(viewport);
11205     }
11206     g.Viewports[0]->ClearRequestFlags(); // Clear main viewport flags because UpdatePlatformWindows() won't do it and may not even be called
11207 }
11208 
11209 // 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)11210 ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& pos, const ImVec2& size, ImGuiViewportFlags flags)
11211 {
11212     ImGuiContext& g = *GImGui;
11213     IM_ASSERT(id != 0);
11214 
11215     if (window != NULL)
11216     {
11217         if (g.MovingWindow && g.MovingWindow->RootWindow == window)
11218             flags |= ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_NoFocusOnAppearing;
11219         if ((window->Flags & ImGuiWindowFlags_NoMouseInputs) && (window->Flags & ImGuiWindowFlags_NoNavInputs))
11220             flags |= ImGuiViewportFlags_NoInputs;
11221         if (window->Flags & ImGuiWindowFlags_NoFocusOnAppearing)
11222             flags |= ImGuiViewportFlags_NoFocusOnAppearing;
11223     }
11224 
11225     ImGuiViewportP* viewport = (ImGuiViewportP*)FindViewportByID(id);
11226     if (viewport)
11227     {
11228         if (!viewport->PlatformRequestMove)
11229             viewport->Pos = pos;
11230         if (!viewport->PlatformRequestResize)
11231             viewport->Size = size;
11232         viewport->Flags = flags | (viewport->Flags & ImGuiViewportFlags_Minimized); // Preserve existing flags
11233     }
11234     else
11235     {
11236         // New viewport
11237         viewport = IM_NEW(ImGuiViewportP)();
11238         viewport->ID = id;
11239         viewport->Idx = g.Viewports.Size;
11240         viewport->Pos = viewport->LastPos = pos;
11241         viewport->Size = size;
11242         viewport->Flags = flags;
11243         UpdateViewportPlatformMonitor(viewport);
11244         g.Viewports.push_back(viewport);
11245         IMGUI_DEBUG_LOG_VIEWPORT("Add Viewport %08X (%s)\n", id, window->Name);
11246 
11247         // We normally setup for all viewports in NewFrame() but here need to handle the mid-frame creation of a new viewport.
11248         // We need to extend the fullscreen clip rect so the OverlayDrawList clip is correct for that the first frame
11249         g.DrawListSharedData.ClipRectFullscreen.x = ImMin(g.DrawListSharedData.ClipRectFullscreen.x, viewport->Pos.x);
11250         g.DrawListSharedData.ClipRectFullscreen.y = ImMin(g.DrawListSharedData.ClipRectFullscreen.y, viewport->Pos.y);
11251         g.DrawListSharedData.ClipRectFullscreen.z = ImMax(g.DrawListSharedData.ClipRectFullscreen.z, viewport->Pos.x + viewport->Size.x);
11252         g.DrawListSharedData.ClipRectFullscreen.w = ImMax(g.DrawListSharedData.ClipRectFullscreen.w, viewport->Pos.y + viewport->Size.y);
11253 
11254         // Store initial DpiScale before the OS platform window creation, based on expected monitor data.
11255         // This is so we can select an appropriate font size on the first frame of our window lifetime
11256         if (viewport->PlatformMonitor != -1)
11257             viewport->DpiScale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale;
11258     }
11259 
11260     viewport->Window = window;
11261     viewport->LastFrameActive = g.FrameCount;
11262     IM_ASSERT(window == NULL || viewport->ID == window->ID);
11263 
11264     if (window != NULL)
11265         window->ViewportOwned = true;
11266 
11267     return viewport;
11268 }
11269 
11270 // FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten.
UpdateSelectWindowViewport(ImGuiWindow * window)11271 static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window)
11272 {
11273     ImGuiContext& g = *GImGui;
11274     ImGuiWindowFlags flags = window->Flags;
11275     window->ViewportAllowPlatformMonitorExtend = -1;
11276 
11277     // Restore main viewport if multi-viewport is not supported by the back-end
11278     ImGuiViewportP* main_viewport = g.Viewports[0];
11279     if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable))
11280     {
11281         SetWindowViewport(window, main_viewport);
11282         return;
11283     }
11284     window->ViewportOwned = false;
11285 
11286     // Appearing popups reset their viewport so they can inherit again
11287     if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && window->Appearing)
11288     {
11289         window->Viewport = NULL;
11290         window->ViewportId = 0;
11291     }
11292 
11293     if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasViewport) == 0)
11294     {
11295         // By default inherit from parent window
11296         if (window->Viewport == NULL && window->ParentWindow && !window->ParentWindow->IsFallbackWindow)
11297             window->Viewport = window->ParentWindow->Viewport;
11298 
11299         // 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
11300         if (window->Viewport == NULL && window->ViewportId != 0)
11301         {
11302             window->Viewport = (ImGuiViewportP*)FindViewportByID(window->ViewportId);
11303             if (window->Viewport == NULL && window->ViewportPos.x != FLT_MAX && window->ViewportPos.y != FLT_MAX)
11304                 window->Viewport = AddUpdateViewport(window, window->ID, window->ViewportPos, window->Size, ImGuiViewportFlags_None);
11305         }
11306     }
11307 
11308     bool lock_viewport = false;
11309     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasViewport)
11310     {
11311         // Code explicitly request a viewport
11312         window->Viewport = (ImGuiViewportP*)FindViewportByID(g.NextWindowData.ViewportId);
11313         window->ViewportId = g.NextWindowData.ViewportId; // Store ID even if Viewport isn't resolved yet.
11314         lock_viewport = true;
11315     }
11316     else if ((flags & ImGuiWindowFlags_ChildWindow) || (flags & ImGuiWindowFlags_ChildMenu))
11317     {
11318         // Always inherit viewport from parent window
11319         window->Viewport = window->ParentWindow->Viewport;
11320     }
11321     else if (flags & ImGuiWindowFlags_Tooltip)
11322     {
11323         window->Viewport = g.MouseViewport;
11324     }
11325     else if (GetWindowAlwaysWantOwnViewport(window))
11326     {
11327         window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None);
11328     }
11329     else if (g.MovingWindow && g.MovingWindow->RootWindow == window && IsMousePosValid())
11330     {
11331         if (window->Viewport != NULL && window->Viewport->Window == window)
11332             window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None);
11333     }
11334     else
11335     {
11336         // Merge into host viewport?
11337         // We cannot test window->ViewportOwned as it set lower in the function.
11338         bool try_to_merge_into_host_viewport = (window->Viewport && window == window->Viewport->Window && g.ActiveId == 0);
11339         if (try_to_merge_into_host_viewport)
11340             UpdateTryMergeWindowIntoHostViewports(window);
11341     }
11342 
11343     // Fallback to default viewport
11344     if (window->Viewport == NULL)
11345         window->Viewport = main_viewport;
11346 
11347     // Mark window as allowed to protrude outside of its viewport and into the current monitor
11348     if (!lock_viewport)
11349     {
11350         if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
11351         {
11352             // We need to take account of the possibility that mouse may become invalid.
11353             // Popups/Tooltip always set ViewportAllowPlatformMonitorExtend so GetWindowAllowedExtentRect() will return full monitor bounds.
11354             ImVec2 mouse_ref = (flags & ImGuiWindowFlags_Tooltip) ? g.IO.MousePos : g.BeginPopupStack.back().OpenMousePos;
11355             bool use_mouse_ref = (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow);
11356             bool mouse_valid = IsMousePosValid(&mouse_ref);
11357             if ((window->Appearing || (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_ChildMenu))) && (!use_mouse_ref || mouse_valid))
11358                 window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos((use_mouse_ref && mouse_valid) ? mouse_ref : NavCalcPreferredRefPos());
11359             else
11360                 window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor;
11361         }
11362         else if (window->Viewport && window != window->Viewport->Window && window->Viewport->Window && !(flags & ImGuiWindowFlags_ChildWindow))
11363         {
11364             // When called from Begin() we don't have access to a proper version of the Hidden flag yet, so we replicate this code.
11365             const bool will_be_visible = (window->DockIsActive && !window->DockTabIsVisible) ? false : true;
11366             if ((window->Flags & ImGuiWindowFlags_DockNodeHost) && window->Viewport->LastFrameActive < g.FrameCount && will_be_visible)
11367             {
11368                 // Steal/transfer ownership
11369                 IMGUI_DEBUG_LOG_VIEWPORT("Window '%s' steal Viewport %08X from Window '%s'\n", window->Name, window->Viewport->ID, window->Viewport->Window->Name);
11370                 window->Viewport->Window = window;
11371                 window->Viewport->ID = window->ID;
11372                 window->Viewport->LastNameHash = 0;
11373             }
11374             else if (!UpdateTryMergeWindowIntoHostViewports(window)) // Merge?
11375             {
11376                 // New viewport
11377                 window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing);
11378             }
11379         }
11380         else if (window->ViewportAllowPlatformMonitorExtend < 0 && (flags & ImGuiWindowFlags_ChildWindow) == 0)
11381         {
11382             // Regular (non-child, non-popup) windows by default are also allowed to protrude
11383             // Child windows are kept contained within their parent.
11384             window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor;
11385         }
11386     }
11387 
11388     // Update flags
11389     window->ViewportOwned = (window == window->Viewport->Window);
11390     window->ViewportId = window->Viewport->ID;
11391 
11392     // If the OS window has a title bar, hide our imgui title bar
11393     //if (window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration))
11394     //    window->Flags |= ImGuiWindowFlags_NoTitleBar;
11395 }
11396 
11397 // Called by user at the end of the main loop, after EndFrame()
11398 // This will handle the creation/update of all OS windows via function defined in the ImGuiPlatformIO api.
UpdatePlatformWindows()11399 void ImGui::UpdatePlatformWindows()
11400 {
11401     ImGuiContext& g = *GImGui;
11402     IM_ASSERT(g.FrameCountEnded == g.FrameCount && "Forgot to call Render() or EndFrame() before UpdatePlatformWindows()?");
11403     IM_ASSERT(g.FrameCountPlatformEnded < g.FrameCount);
11404     g.FrameCountPlatformEnded = g.FrameCount;
11405     if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable))
11406         return;
11407 
11408     // Create/resize/destroy platform windows to match each active viewport.
11409     // Skip the main viewport (index 0), which is always fully handled by the application!
11410     for (int i = 1; i < g.Viewports.Size; i++)
11411     {
11412         ImGuiViewportP* viewport = g.Viewports[i];
11413 
11414         // Destroy platform window if the viewport hasn't been submitted or if it is hosting a hidden window
11415         // (the implicit/fallback Debug##Default window will be registering its viewport then be disabled, causing a dummy DestroyPlatformWindow to be made each frame)
11416         bool destroy_platform_window = false;
11417         destroy_platform_window |= (viewport->LastFrameActive < g.FrameCount - 1);
11418         destroy_platform_window |= (viewport->Window && !IsWindowActiveAndVisible(viewport->Window));
11419         if (destroy_platform_window)
11420         {
11421             DestroyPlatformWindow(viewport);
11422             continue;
11423         }
11424 
11425         // New windows that appears directly in a new viewport won't always have a size on their first frame
11426         if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0 || viewport->Size.y <= 0)
11427             continue;
11428 
11429         // Create window
11430         bool is_new_platform_window = (viewport->PlatformWindowCreated == false);
11431         if (is_new_platform_window)
11432         {
11433             IMGUI_DEBUG_LOG_VIEWPORT("Create Platform Window %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a");
11434             g.PlatformIO.Platform_CreateWindow(viewport);
11435             if (g.PlatformIO.Renderer_CreateWindow != NULL)
11436                 g.PlatformIO.Renderer_CreateWindow(viewport);
11437             viewport->LastNameHash = 0;
11438             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?)
11439             viewport->LastRendererSize = viewport->Size;                                       // We don't need to call Renderer_SetWindowSize() as it is expected Renderer_CreateWindow() already did it.
11440             viewport->PlatformWindowCreated = true;
11441         }
11442 
11443         // Apply Position and Size (from ImGui to Platform/Renderer back-ends)
11444         if ((viewport->LastPlatformPos.x != viewport->Pos.x || viewport->LastPlatformPos.y != viewport->Pos.y) && !viewport->PlatformRequestMove)
11445             g.PlatformIO.Platform_SetWindowPos(viewport, viewport->Pos);
11446         if ((viewport->LastPlatformSize.x != viewport->Size.x || viewport->LastPlatformSize.y != viewport->Size.y) && !viewport->PlatformRequestResize)
11447             g.PlatformIO.Platform_SetWindowSize(viewport, viewport->Size);
11448         if ((viewport->LastRendererSize.x != viewport->Size.x || viewport->LastRendererSize.y != viewport->Size.y) && g.PlatformIO.Renderer_SetWindowSize)
11449             g.PlatformIO.Renderer_SetWindowSize(viewport, viewport->Size);
11450         viewport->LastPlatformPos = viewport->Pos;
11451         viewport->LastPlatformSize = viewport->LastRendererSize = viewport->Size;
11452 
11453         // Update title bar (if it changed)
11454         if (ImGuiWindow* window_for_title = GetWindowForTitleDisplay(viewport->Window))
11455         {
11456             const char* title_begin = window_for_title->Name;
11457             char* title_end = (char*)(intptr_t)FindRenderedTextEnd(title_begin);
11458             const ImGuiID title_hash = ImHashStr(title_begin, title_end - title_begin);
11459             if (viewport->LastNameHash != title_hash)
11460             {
11461                 char title_end_backup_c = *title_end;
11462                 *title_end = 0; // Cut existing buffer short instead of doing an alloc/free, no small gain.
11463                 g.PlatformIO.Platform_SetWindowTitle(viewport, title_begin);
11464                 *title_end = title_end_backup_c;
11465                 viewport->LastNameHash = title_hash;
11466             }
11467         }
11468 
11469         // Update alpha (if it changed)
11470         if (viewport->LastAlpha != viewport->Alpha && g.PlatformIO.Platform_SetWindowAlpha)
11471             g.PlatformIO.Platform_SetWindowAlpha(viewport, viewport->Alpha);
11472         viewport->LastAlpha = viewport->Alpha;
11473 
11474         // Optional, general purpose call to allow the back-end to perform general book-keeping even if things haven't changed.
11475         if (g.PlatformIO.Platform_UpdateWindow)
11476             g.PlatformIO.Platform_UpdateWindow(viewport);
11477 
11478         if (is_new_platform_window)
11479         {
11480             // 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)
11481             if (g.FrameCount < 3)
11482                 viewport->Flags |= ImGuiViewportFlags_NoFocusOnAppearing;
11483 
11484             // Show window
11485             g.PlatformIO.Platform_ShowWindow(viewport);
11486 
11487             // Even without focus, we assume the window becomes front-most.
11488             // This is useful for our platform z-order heuristic when io.MouseHoveredViewport is not available.
11489             if (viewport->LastFrontMostStampCount != g.ViewportFrontMostStampCount)
11490                 viewport->LastFrontMostStampCount = ++g.ViewportFrontMostStampCount;
11491         }
11492 
11493         // Clear request flags
11494         viewport->ClearRequestFlags();
11495     }
11496 
11497     // Update our implicit z-order knowledge of platform windows, which is used when the back-end cannot provide io.MouseHoveredViewport.
11498     // When setting Platform_GetWindowFocus, it is expected that the platform back-end can handle calls without crashing if it doesn't have data stored.
11499     if (g.PlatformIO.Platform_GetWindowFocus != NULL)
11500     {
11501         ImGuiViewportP* focused_viewport = NULL;
11502         for (int n = 0; n < g.Viewports.Size && focused_viewport == NULL; n++)
11503         {
11504             ImGuiViewportP* viewport = g.Viewports[n];
11505             if (viewport->PlatformWindowCreated)
11506                 if (g.PlatformIO.Platform_GetWindowFocus(viewport))
11507                     focused_viewport = viewport;
11508         }
11509         if (focused_viewport && g.PlatformLastFocusedViewport != focused_viewport->ID)
11510         {
11511             if (focused_viewport->LastFrontMostStampCount != g.ViewportFrontMostStampCount)
11512                 focused_viewport->LastFrontMostStampCount = ++g.ViewportFrontMostStampCount;
11513             g.PlatformLastFocusedViewport = focused_viewport->ID;
11514         }
11515     }
11516 }
11517 
11518 // This is a default/basic function for performing the rendering/swap of multiple Platform Windows.
11519 // Custom renderers may prefer to not call this function at all, and instead iterate the publicly exposed platform data and handle rendering/sync themselves.
11520 // The Render/Swap functions stored in ImGuiPlatformIO are merely here to allow for this helper to exist, but you can do it yourself:
11521 //
11522 //    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
11523 //    for (int i = 1; i < platform_io.Viewports.Size; i++)
11524 //        if ((platform_io.Viewports[i]->Flags & ImGuiViewportFlags_Minimized) == 0)
11525 //            MyRenderFunction(platform_io.Viewports[i], my_args);
11526 //    for (int i = 1; i < platform_io.Viewports.Size; i++)
11527 //        if ((platform_io.Viewports[i]->Flags & ImGuiViewportFlags_Minimized) == 0)
11528 //            MySwapBufferFunction(platform_io.Viewports[i], my_args);
11529 //
RenderPlatformWindowsDefault(void * platform_render_arg,void * renderer_render_arg)11530 void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* renderer_render_arg)
11531 {
11532     // Skip the main viewport (index 0), which is always fully handled by the application!
11533     ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
11534     for (int i = 1; i < platform_io.Viewports.Size; i++)
11535     {
11536         ImGuiViewport* viewport = platform_io.Viewports[i];
11537         if (viewport->Flags & ImGuiViewportFlags_Minimized)
11538             continue;
11539         if (platform_io.Platform_RenderWindow) platform_io.Platform_RenderWindow(viewport, platform_render_arg);
11540         if (platform_io.Renderer_RenderWindow) platform_io.Renderer_RenderWindow(viewport, renderer_render_arg);
11541     }
11542     for (int i = 1; i < platform_io.Viewports.Size; i++)
11543     {
11544         ImGuiViewport* viewport = platform_io.Viewports[i];
11545         if (viewport->Flags & ImGuiViewportFlags_Minimized)
11546             continue;
11547         if (platform_io.Platform_SwapBuffers) platform_io.Platform_SwapBuffers(viewport, platform_render_arg);
11548         if (platform_io.Renderer_SwapBuffers) platform_io.Renderer_SwapBuffers(viewport, renderer_render_arg);
11549     }
11550 }
11551 
FindPlatformMonitorForPos(const ImVec2 & pos)11552 static int ImGui::FindPlatformMonitorForPos(const ImVec2& pos)
11553 {
11554     ImGuiContext& g = *GImGui;
11555     for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++)
11556     {
11557         const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n];
11558         if (ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize).Contains(pos))
11559             return monitor_n;
11560     }
11561     return -1;
11562 }
11563 
11564 // Search for the monitor with the largest intersection area with the given rectangle
11565 // We generally try to avoid searching loops but the monitor count should be very small here
11566 // FIXME-OPT: We could test the last monitor used for that viewport first, and early
FindPlatformMonitorForRect(const ImRect & rect)11567 static int ImGui::FindPlatformMonitorForRect(const ImRect& rect)
11568 {
11569     ImGuiContext& g = *GImGui;
11570 
11571     const int monitor_count = g.PlatformIO.Monitors.Size;
11572     if (monitor_count <= 1)
11573         return monitor_count - 1;
11574 
11575     // 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.
11576     // This is necessary for tooltips which always resize down to zero at first.
11577     const float surface_threshold = ImMax(rect.GetWidth() * rect.GetHeight() * 0.5f, 1.0f);
11578     int best_monitor_n = -1;
11579     float best_monitor_surface = 0.001f;
11580 
11581     for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size && best_monitor_surface < surface_threshold; monitor_n++)
11582     {
11583         const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n];
11584         const ImRect monitor_rect = ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize);
11585         if (monitor_rect.Contains(rect))
11586             return monitor_n;
11587         ImRect overlapping_rect = rect;
11588         overlapping_rect.ClipWithFull(monitor_rect);
11589         float overlapping_surface = overlapping_rect.GetWidth() * overlapping_rect.GetHeight();
11590         if (overlapping_surface < best_monitor_surface)
11591             continue;
11592         best_monitor_surface = overlapping_surface;
11593         best_monitor_n = monitor_n;
11594     }
11595     return best_monitor_n;
11596 }
11597 
11598 // Update monitor from viewport rectangle (we'll use this info to clamp windows and save windows lost in a removed monitor)
UpdateViewportPlatformMonitor(ImGuiViewportP * viewport)11599 static void ImGui::UpdateViewportPlatformMonitor(ImGuiViewportP* viewport)
11600 {
11601     viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetMainRect());
11602 }
11603 
DestroyPlatformWindow(ImGuiViewportP * viewport)11604 void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport)
11605 {
11606     ImGuiContext& g = *GImGui;
11607     if (viewport->PlatformWindowCreated)
11608     {
11609         if (g.PlatformIO.Renderer_DestroyWindow)
11610             g.PlatformIO.Renderer_DestroyWindow(viewport);
11611         if (g.PlatformIO.Platform_DestroyWindow)
11612             g.PlatformIO.Platform_DestroyWindow(viewport);
11613         IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL);
11614 
11615         // Don't clear PlatformWindowCreated for the main viewport, as we initially set that up to true in Initialize()
11616         // The right-er way may be to leave it to the back-end to set this flag all-together, and made the flag public.
11617         if (viewport->ID != IMGUI_VIEWPORT_DEFAULT_ID)
11618             viewport->PlatformWindowCreated = false;
11619     }
11620     else
11621     {
11622         IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL);
11623     }
11624     viewport->RendererUserData = viewport->PlatformUserData = viewport->PlatformHandle = NULL;
11625     viewport->ClearRequestFlags();
11626 }
11627 
DestroyPlatformWindows()11628 void ImGui::DestroyPlatformWindows()
11629 {
11630     // We call the destroy window on every viewport (including the main viewport, index 0) to give a chance to the back-end
11631     // to clear any data they may have stored in e.g. PlatformUserData, RendererUserData.
11632     // It is convenient for the platform back-end code to store something in the main viewport, in order for e.g. the mouse handling
11633     // code to operator a consistent manner.
11634     // It is expected that the back-end can handle calls to Renderer_DestroyWindow/Platform_DestroyWindow without
11635     // crashing if it doesn't have data stored.
11636     ImGuiContext& g = *GImGui;
11637     for (int i = 0; i < g.Viewports.Size; i++)
11638         DestroyPlatformWindow(g.Viewports[i]);
11639 }
11640 
11641 
11642 //-----------------------------------------------------------------------------
11643 // [SECTION] DOCKING
11644 //-----------------------------------------------------------------------------
11645 // Docking: Internal Types
11646 // Docking: Forward Declarations
11647 // Docking: ImGuiDockContext
11648 // Docking: ImGuiDockContext Docking/Undocking functions
11649 // Docking: ImGuiDockNode
11650 // Docking: ImGuiDockNode Tree manipulation functions
11651 // Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport)
11652 // Docking: Builder Functions
11653 // Docking: Begin/End Support Functions (called from Begin/End)
11654 // Docking: Settings
11655 //-----------------------------------------------------------------------------
11656 
11657 //-----------------------------------------------------------------------------
11658 // Docking: Internal Types
11659 //-----------------------------------------------------------------------------
11660 // - ImGuiDockRequestType
11661 // - ImGuiDockRequest
11662 // - ImGuiDockPreviewData
11663 // - ImGuiDockNodeSettings
11664 // - ImGuiDockContext
11665 //-----------------------------------------------------------------------------
11666 
11667 enum ImGuiDockRequestType
11668 {
11669     ImGuiDockRequestType_None = 0,
11670     ImGuiDockRequestType_Dock,
11671     ImGuiDockRequestType_Undock,
11672     ImGuiDockRequestType_Split                  // Split is the same as Dock but without a DockPayload
11673 };
11674 
11675 struct ImGuiDockRequest
11676 {
11677     ImGuiDockRequestType    Type;
11678     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)
11679     ImGuiDockNode*          DockTargetNode;     // Destination/Target Node to dock into
11680     ImGuiWindow*            DockPayload;        // Source/Payload window to dock (may be a loose window or a DockNode), [Optional]
11681     ImGuiDir                DockSplitDir;
11682     float                   DockSplitRatio;
11683     bool                    DockSplitOuter;
11684     ImGuiWindow*            UndockTargetWindow;
11685     ImGuiDockNode*          UndockTargetNode;
11686 
ImGuiDockRequestImGuiDockRequest11687     ImGuiDockRequest()
11688     {
11689         Type = ImGuiDockRequestType_None;
11690         DockTargetWindow = DockPayload = UndockTargetWindow = NULL;
11691         DockTargetNode = UndockTargetNode = NULL;
11692         DockSplitDir = ImGuiDir_None;
11693         DockSplitRatio = 0.5f;
11694         DockSplitOuter = false;
11695     }
11696 };
11697 
11698 struct ImGuiDockPreviewData
11699 {
11700     ImGuiDockNode   FutureNode;
11701     bool            IsDropAllowed;
11702     bool            IsCenterAvailable;
11703     bool            IsSidesAvailable;           // Hold your breath, grammar freaks..
11704     bool            IsSplitDirExplicit;         // Set when hovered the drop rect (vs. implicit SplitDir==None when hovered the window)
11705     ImGuiDockNode*  SplitNode;
11706     ImGuiDir        SplitDir;
11707     float           SplitRatio;
11708     ImRect          DropRectsDraw[ImGuiDir_COUNT + 1];  // May be slightly different from hit-testing drop rects used in DockNodeCalcDropRects()
11709 
ImGuiDockPreviewDataImGuiDockPreviewData11710     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); }
11711 };
11712 
11713 // Persistent Settings data, stored contiguously in SettingsNodes (sizeof() ~32 bytes)
11714 struct ImGuiDockNodeSettings
11715 {
11716     ImGuiID             ID;
11717     ImGuiID             ParentNodeId;
11718     ImGuiID             ParentWindowId;
11719     ImGuiID             SelectedWindowId;
11720     signed char         SplitAxis;
11721     char                Depth;
11722     ImGuiDockNodeFlags  Flags;                  // NB: We save individual flags one by one in ascii format (ImGuiDockNodeFlags_SavedFlagsMask_)
11723     ImVec2ih            Pos;
11724     ImVec2ih            Size;
11725     ImVec2ih            SizeRef;
ImGuiDockNodeSettingsImGuiDockNodeSettings11726     ImGuiDockNodeSettings() { ID = ParentNodeId = ParentWindowId = SelectedWindowId = 0; SplitAxis = ImGuiAxis_None; Depth = 0; Flags = ImGuiDockNodeFlags_None; }
11727 };
11728 
11729 //-----------------------------------------------------------------------------
11730 // Docking: Forward Declarations
11731 //-----------------------------------------------------------------------------
11732 
11733 namespace ImGui
11734 {
11735     // ImGuiDockContext
11736     static ImGuiDockNode*   DockContextAddNode(ImGuiContext* ctx, ImGuiID id);
11737     static void             DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node);
11738     static void             DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node);
11739     static void             DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req);
11740     static void             DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window, bool clear_persistent_docking_ref = true);
11741     static void             DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node);
11742     static void             DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx);
11743     static ImGuiDockNode*   DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id);
11744     static ImGuiDockNode*   DockContextBindNodeToWindow(ImGuiContext* ctx, ImGuiWindow* window);
11745     static void             DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count);
11746     static void             DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id);                            // Use root_id==0 to add all
11747 
11748     // ImGuiDockNode
11749     static void             DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, bool add_to_tab_bar);
11750     static void             DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node);
11751     static void             DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode* src_node);
11752     static ImGuiWindow*     DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id);
11753     static void             DockNodeApplyPosSizeToWindows(ImGuiDockNode* node);
11754     static void             DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id);
11755     static void             DockNodeHideHostWindow(ImGuiDockNode* node);
11756     static void             DockNodeUpdate(ImGuiDockNode* node);
11757     static void             DockNodeUpdateForRootNode(ImGuiDockNode* node);
11758     static void             DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node);
11759     static void             DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window);
11760     static void             DockNodeAddTabBar(ImGuiDockNode* node);
11761     static void             DockNodeRemoveTabBar(ImGuiDockNode* node);
11762     static ImGuiID          DockNodeUpdateWindowMenu(ImGuiDockNode* node, ImGuiTabBar* tab_bar);
11763     static void             DockNodeUpdateVisibleFlag(ImGuiDockNode* node);
11764     static void             DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window);
11765     static bool             DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* payload_window);
11766     static void             DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockPreviewData* preview_data, bool is_explicit_target, bool is_outer_docking);
11767     static void             DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, const ImGuiDockPreviewData* preview_data);
11768     static void             DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos);
11769     static void             DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired);
11770     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)11771     static const char*      DockNodeGetHostWindowTitle(ImGuiDockNode* node, char* buf, int buf_size) { ImFormatString(buf, buf_size, "##DockNode_%02X", node->ID); return buf; }
11772     static int              DockNodeGetTabOrder(ImGuiWindow* window);
11773 
11774     // ImGuiDockNode tree manipulations
11775     static void             DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_first_child, float split_ratio, ImGuiDockNode* new_node);
11776     static void             DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child);
11777     static void             DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, bool only_write_to_marked_nodes = false);
11778     static void             DockNodeTreeUpdateSplitter(ImGuiDockNode* node);
11779     static ImGuiDockNode*   DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos);
11780     static ImGuiDockNode*   DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node);
11781 
11782     // Settings
11783     static void             DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id);
11784     static void             DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ids_count);
11785     static ImGuiDockNodeSettings*   DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID node_id);
11786     static void             DockSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
11787     static void             DockSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);
11788     static void*            DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
11789     static void             DockSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
11790     static void             DockSettingsHandler_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf);
11791 }
11792 
11793 //-----------------------------------------------------------------------------
11794 // Docking: ImGuiDockContext
11795 //-----------------------------------------------------------------------------
11796 // The lifetime model is different from the one of regular windows: we always create a ImGuiDockNode for each ImGuiDockNodeSettings,
11797 // 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.
11798 // At boot time only, we run a simple GC to remove nodes that have no references.
11799 // Because dock node settings (which are small, contiguous structures) are always mirrored by their corresponding dock nodes (more complete structures),
11800 // we can also very easily recreate the nodes from scratch given the settings data (this is what DockContextRebuild() does).
11801 // This is convenient as docking reconfiguration can be implemented by mostly poking at the simpler settings data.
11802 //-----------------------------------------------------------------------------
11803 // - DockContextInitialize()
11804 // - DockContextShutdown()
11805 // - DockContextClearNodes()
11806 // - DockContextRebuildNodes()
11807 // - DockContextUpdateUndocking()
11808 // - DockContextUpdateDocking()
11809 // - DockContextFindNodeByID()
11810 // - DockContextBindNodeToWindow()
11811 // - DockContextGenNodeID()
11812 // - DockContextAddNode()
11813 // - DockContextRemoveNode()
11814 // - ImGuiDockContextPruneNodeData
11815 // - DockContextPruneUnusedSettingsNodes()
11816 // - DockContextBuildNodesFromSettings()
11817 // - DockContextBuildAddWindowsToNodes()
11818 //-----------------------------------------------------------------------------
11819 
DockContextInitialize(ImGuiContext * ctx)11820 void ImGui::DockContextInitialize(ImGuiContext* ctx)
11821 {
11822     ImGuiContext& g = *ctx;
11823 
11824     // Add .ini handle for persistent docking data
11825     ImGuiSettingsHandler ini_handler;
11826     ini_handler.TypeName = "Docking";
11827     ini_handler.TypeHash = ImHashStr("Docking");
11828     ini_handler.ClearAllFn = DockSettingsHandler_ClearAll;
11829     ini_handler.ReadInitFn = DockSettingsHandler_ClearAll; // Also clear on read
11830     ini_handler.ReadOpenFn = DockSettingsHandler_ReadOpen;
11831     ini_handler.ReadLineFn = DockSettingsHandler_ReadLine;
11832     ini_handler.ApplyAllFn = DockSettingsHandler_ApplyAll;
11833     ini_handler.WriteAllFn = DockSettingsHandler_WriteAll;
11834     g.SettingsHandlers.push_back(ini_handler);
11835 }
11836 
DockContextShutdown(ImGuiContext * ctx)11837 void ImGui::DockContextShutdown(ImGuiContext* ctx)
11838 {
11839     ImGuiDockContext* dc  = &ctx->DockContext;
11840     for (int n = 0; n < dc->Nodes.Data.Size; n++)
11841         if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
11842             IM_DELETE(node);
11843 }
11844 
DockContextClearNodes(ImGuiContext * ctx,ImGuiID root_id,bool clear_settings_refs)11845 void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_settings_refs)
11846 {
11847     IM_UNUSED(ctx);
11848     IM_ASSERT(ctx == GImGui);
11849     DockBuilderRemoveNodeDockedWindows(root_id, clear_settings_refs);
11850     DockBuilderRemoveNodeChildNodes(root_id);
11851 }
11852 
11853 // [DEBUG] This function also acts as a defacto test to make sure we can rebuild from scratch without a glitch
11854 // (Different from DockSettingsHandler_ClearAll() + DockSettingsHandler_ApplyAll() because this reuses current settings!)
DockContextRebuildNodes(ImGuiContext * ctx)11855 void ImGui::DockContextRebuildNodes(ImGuiContext* ctx)
11856 {
11857     IMGUI_DEBUG_LOG_DOCKING("DockContextRebuild()\n");
11858     ImGuiDockContext* dc  = &ctx->DockContext;
11859     SaveIniSettingsToMemory();
11860     ImGuiID root_id = 0; // Rebuild all
11861     DockContextClearNodes(ctx, root_id, false);
11862     DockContextBuildNodesFromSettings(ctx, dc->NodesSettings.Data, dc->NodesSettings.Size);
11863     DockContextBuildAddWindowsToNodes(ctx, root_id);
11864 }
11865 
11866 // Docking context update function, called by NewFrame()
DockContextUpdateUndocking(ImGuiContext * ctx)11867 void ImGui::DockContextUpdateUndocking(ImGuiContext* ctx)
11868 {
11869     ImGuiContext& g = *ctx;
11870     ImGuiDockContext* dc  = &ctx->DockContext;
11871     if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
11872     {
11873         if (dc->Nodes.Data.Size > 0 || dc->Requests.Size > 0)
11874             DockContextClearNodes(ctx, 0, true);
11875         return;
11876     }
11877 
11878     // Setting NoSplit at runtime merges all nodes
11879     if (g.IO.ConfigDockingNoSplit)
11880         for (int n = 0; n < dc->Nodes.Data.Size; n++)
11881             if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
11882                 if (node->IsRootNode() && node->IsSplitNode())
11883                 {
11884                     DockBuilderRemoveNodeChildNodes(node->ID);
11885                     //dc->WantFullRebuild = true;
11886                 }
11887 
11888     // Process full rebuild
11889 #if 0
11890     if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C)))
11891         dc->WantFullRebuild = true;
11892 #endif
11893     if (dc->WantFullRebuild)
11894     {
11895         DockContextRebuildNodes(ctx);
11896         dc->WantFullRebuild = false;
11897     }
11898 
11899     // Process Undocking requests (we need to process them _before_ the UpdateMouseMovingWindowNewFrame call in NewFrame)
11900     for (int n = 0; n < dc->Requests.Size; n++)
11901     {
11902         ImGuiDockRequest* req = &dc->Requests[n];
11903         if (req->Type == ImGuiDockRequestType_Undock && req->UndockTargetWindow)
11904             DockContextProcessUndockWindow(ctx, req->UndockTargetWindow);
11905         else if (req->Type == ImGuiDockRequestType_Undock && req->UndockTargetNode)
11906             DockContextProcessUndockNode(ctx, req->UndockTargetNode);
11907     }
11908 }
11909 
11910 // Docking context update function, called by NewFrame()
DockContextUpdateDocking(ImGuiContext * ctx)11911 void ImGui::DockContextUpdateDocking(ImGuiContext* ctx)
11912 {
11913     ImGuiContext& g = *ctx;
11914     ImGuiDockContext* dc  = &ctx->DockContext;
11915     if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
11916         return;
11917 
11918     // Store hovered dock node. We could in theory use DockNodeTreeFindVisibleNodeByPos() on the root host dock node, but using ->DockNode is a good shortcut.
11919     g.HoveredDockNode = NULL;
11920     if (ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow)
11921     {
11922         if (hovered_window->DockNodeAsHost)
11923             g.HoveredDockNode = DockNodeTreeFindVisibleNodeByPos(hovered_window->DockNodeAsHost, g.IO.MousePos);
11924         else if (hovered_window->RootWindowDockStop->DockNode)
11925             g.HoveredDockNode = hovered_window->RootWindowDockStop->DockNode;
11926     }
11927 
11928     // Process Docking requests
11929     for (int n = 0; n < dc->Requests.Size; n++)
11930         if (dc->Requests[n].Type == ImGuiDockRequestType_Dock)
11931             DockContextProcessDock(ctx, &dc->Requests[n]);
11932     dc->Requests.resize(0);
11933 
11934     // Create windows for each automatic docking nodes
11935     // 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)
11936     for (int n = 0; n < dc->Nodes.Data.Size; n++)
11937         if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
11938             if (node->IsFloatingNode())
11939                 DockNodeUpdate(node);
11940 }
11941 
DockContextFindNodeByID(ImGuiContext * ctx,ImGuiID id)11942 static ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id)
11943 {
11944     return (ImGuiDockNode*)ctx->DockContext.Nodes.GetVoidPtr(id);
11945 }
11946 
DockContextGenNodeID(ImGuiContext * ctx)11947 ImGuiID ImGui::DockContextGenNodeID(ImGuiContext* ctx)
11948 {
11949     // Generate an ID for new node (the exact ID value doesn't matter as long as it is not already used)
11950     // FIXME-OPT FIXME-DOCK: This is suboptimal, even if the node count is small enough not to be a worry. We should poke in ctx->Nodes to find a suitable ID faster.
11951     ImGuiID id = 0x0001;
11952     while (DockContextFindNodeByID(ctx, id) != NULL)
11953         id++;
11954     return id;
11955 }
11956 
DockContextAddNode(ImGuiContext * ctx,ImGuiID id)11957 static ImGuiDockNode* ImGui::DockContextAddNode(ImGuiContext* ctx, ImGuiID id)
11958 {
11959     // 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.
11960     if (id == 0)
11961         id = DockContextGenNodeID(ctx);
11962     else
11963         IM_ASSERT(DockContextFindNodeByID(ctx, id) == NULL);
11964 
11965     // We don't set node->LastFrameAlive on construction. Nodes are always created at all time to reflect .ini settings!
11966     IMGUI_DEBUG_LOG_DOCKING("DockContextAddNode 0x%08X\n", id);
11967     ImGuiDockNode* node = IM_NEW(ImGuiDockNode)(id);
11968     ctx->DockContext.Nodes.SetVoidPtr(node->ID, node);
11969     return node;
11970 }
11971 
DockContextRemoveNode(ImGuiContext * ctx,ImGuiDockNode * node,bool merge_sibling_into_parent_node)11972 static void ImGui::DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node)
11973 {
11974     ImGuiContext& g = *ctx;
11975     ImGuiDockContext* dc  = &ctx->DockContext;
11976 
11977     IMGUI_DEBUG_LOG_DOCKING("DockContextRemoveNode 0x%08X\n", node->ID);
11978     IM_ASSERT(DockContextFindNodeByID(ctx, node->ID) == node);
11979     IM_ASSERT(node->ChildNodes[0] == NULL && node->ChildNodes[1] == NULL);
11980     IM_ASSERT(node->Windows.Size == 0);
11981 
11982     if (node->HostWindow)
11983         node->HostWindow->DockNodeAsHost = NULL;
11984 
11985     ImGuiDockNode* parent_node = node->ParentNode;
11986     const bool merge = (merge_sibling_into_parent_node && parent_node != NULL);
11987     if (merge)
11988     {
11989         IM_ASSERT(parent_node->ChildNodes[0] == node || parent_node->ChildNodes[1] == node);
11990         ImGuiDockNode* sibling_node = (parent_node->ChildNodes[0] == node ? parent_node->ChildNodes[1] : parent_node->ChildNodes[0]);
11991         DockNodeTreeMerge(&g, parent_node, sibling_node);
11992     }
11993     else
11994     {
11995         for (int n = 0; parent_node && n < IM_ARRAYSIZE(parent_node->ChildNodes); n++)
11996             if (parent_node->ChildNodes[n] == node)
11997                 node->ParentNode->ChildNodes[n] = NULL;
11998         dc->Nodes.SetVoidPtr(node->ID, NULL);
11999         IM_DELETE(node);
12000     }
12001 }
12002 
DockNodeComparerDepthMostFirst(const void * lhs,const void * rhs)12003 static int IMGUI_CDECL DockNodeComparerDepthMostFirst(const void* lhs, const void* rhs)
12004 {
12005     const ImGuiDockNode* a = *(const ImGuiDockNode* const*)lhs;
12006     const ImGuiDockNode* b = *(const ImGuiDockNode* const*)rhs;
12007     return ImGui::DockNodeGetDepth(b) - ImGui::DockNodeGetDepth(a);
12008 }
12009 
12010 // Pre C++0x doesn't allow us to use a function-local type (without linkage) as template parameter, so we moved this here.
12011 struct ImGuiDockContextPruneNodeData
12012 {
12013     int         CountWindows, CountChildWindows, CountChildNodes;
12014     ImGuiID     RootId;
ImGuiDockContextPruneNodeDataImGuiDockContextPruneNodeData12015     ImGuiDockContextPruneNodeData() { CountWindows = CountChildWindows = CountChildNodes = 0; RootId = 0; }
12016 };
12017 
12018 // Garbage collect unused nodes (run once at init time)
DockContextPruneUnusedSettingsNodes(ImGuiContext * ctx)12019 static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx)
12020 {
12021     ImGuiContext& g = *ctx;
12022     ImGuiDockContext* dc  = &ctx->DockContext;
12023     IM_ASSERT(g.Windows.Size == 0);
12024 
12025     ImPool<ImGuiDockContextPruneNodeData> pool;
12026     pool.Reserve(dc->NodesSettings.Size);
12027 
12028     // Count child nodes and compute RootID
12029     for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++)
12030     {
12031         ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n];
12032         ImGuiDockContextPruneNodeData* parent_data = settings->ParentNodeId ? pool.GetByKey(settings->ParentNodeId) : 0;
12033         pool.GetOrAddByKey(settings->ID)->RootId = parent_data ? parent_data->RootId : settings->ID;
12034         if (settings->ParentNodeId)
12035             pool.GetOrAddByKey(settings->ParentNodeId)->CountChildNodes++;
12036     }
12037 
12038     // Count reference to dock ids from dockspaces
12039     // We track the 'auto-DockNode <- manual-Window <- manual-DockSpace' in order to avoid 'auto-DockNode' being ditched by DockContextPruneUnusedSettingsNodes()
12040     for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++)
12041     {
12042         ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n];
12043         if (settings->ParentWindowId != 0)
12044             if (ImGuiWindowSettings* window_settings = FindWindowSettings(settings->ParentWindowId))
12045                 if (window_settings->DockId)
12046                     if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(window_settings->DockId))
12047                         data->CountChildNodes++;
12048     }
12049 
12050     // Count reference to dock ids from window settings
12051     // We guard against the possibility of an invalid .ini file (RootID may point to a missing node)
12052     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
12053         if (ImGuiID dock_id = settings->DockId)
12054             if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(dock_id))
12055             {
12056                 data->CountWindows++;
12057                 if (ImGuiDockContextPruneNodeData* data_root = (data->RootId == dock_id) ? data : pool.GetByKey(data->RootId))
12058                     data_root->CountChildWindows++;
12059             }
12060 
12061     // Prune
12062     for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++)
12063     {
12064         ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n];
12065         ImGuiDockContextPruneNodeData* data = pool.GetByKey(settings->ID);
12066         if (data->CountWindows > 1)
12067             continue;
12068         ImGuiDockContextPruneNodeData* data_root = (data->RootId == settings->ID) ? data : pool.GetByKey(data->RootId);
12069 
12070         bool remove = false;
12071         remove |= (data->CountWindows == 1 && settings->ParentNodeId == 0 && data->CountChildNodes == 0 && !(settings->Flags & ImGuiDockNodeFlags_CentralNode));  // Floating root node with only 1 window
12072         remove |= (data->CountWindows == 0 && settings->ParentNodeId == 0 && data->CountChildNodes == 0); // Leaf nodes with 0 window
12073         remove |= (data_root->CountChildWindows == 0);
12074         if (remove)
12075         {
12076             IMGUI_DEBUG_LOG_DOCKING("DockContextPruneUnusedSettingsNodes: Prune 0x%08X\n", settings->ID);
12077             DockSettingsRemoveNodeReferences(&settings->ID, 1);
12078             settings->ID = 0;
12079         }
12080     }
12081 }
12082 
DockContextBuildNodesFromSettings(ImGuiContext * ctx,ImGuiDockNodeSettings * node_settings_array,int node_settings_count)12083 static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count)
12084 {
12085     // Build nodes
12086     for (int node_n = 0; node_n < node_settings_count; node_n++)
12087     {
12088         ImGuiDockNodeSettings* settings = &node_settings_array[node_n];
12089         if (settings->ID == 0)
12090             continue;
12091         ImGuiDockNode* node = DockContextAddNode(ctx, settings->ID);
12092         node->ParentNode = settings->ParentNodeId ? DockContextFindNodeByID(ctx, settings->ParentNodeId) : NULL;
12093         node->Pos = ImVec2(settings->Pos.x, settings->Pos.y);
12094         node->Size = ImVec2(settings->Size.x, settings->Size.y);
12095         node->SizeRef = ImVec2(settings->SizeRef.x, settings->SizeRef.y);
12096         node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_DockNode;
12097         if (node->ParentNode && node->ParentNode->ChildNodes[0] == NULL)
12098             node->ParentNode->ChildNodes[0] = node;
12099         else if (node->ParentNode && node->ParentNode->ChildNodes[1] == NULL)
12100             node->ParentNode->ChildNodes[1] = node;
12101         node->SelectedTabId = settings->SelectedWindowId;
12102         node->SplitAxis = (ImGuiAxis)settings->SplitAxis;
12103         node->LocalFlags |= (settings->Flags & ImGuiDockNodeFlags_SavedFlagsMask_);
12104 
12105         // Bind host window immediately if it already exist (in case of a rebuild)
12106         // This is useful as the RootWindowForTitleBarHighlight links necessary to highlight the currently focused node requires node->HostWindow to be set.
12107         char host_window_title[20];
12108         ImGuiDockNode* root_node = DockNodeGetRootNode(node);
12109         node->HostWindow = FindWindowByName(DockNodeGetHostWindowTitle(root_node, host_window_title, IM_ARRAYSIZE(host_window_title)));
12110     }
12111 }
12112 
DockContextBuildAddWindowsToNodes(ImGuiContext * ctx,ImGuiID root_id)12113 void ImGui::DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id)
12114 {
12115     // Rebind all windows to nodes (they can also lazily rebind but we'll have a visible glitch during the first frame)
12116     ImGuiContext& g = *ctx;
12117     for (int n = 0; n < g.Windows.Size; n++)
12118     {
12119         ImGuiWindow* window = g.Windows[n];
12120         if (window->DockId == 0 || window->LastFrameActive < g.FrameCount - 1)
12121             continue;
12122         if (window->DockNode != NULL)
12123             continue;
12124 
12125         ImGuiDockNode* node = DockContextFindNodeByID(ctx, window->DockId);
12126         IM_ASSERT(node != NULL);   // This should have been called after DockContextBuildNodesFromSettings()
12127         if (root_id == 0 || DockNodeGetRootNode(node)->ID == root_id)
12128             DockNodeAddWindow(node, window, true);
12129     }
12130 }
12131 
12132 //-----------------------------------------------------------------------------
12133 // Docking: ImGuiDockContext Docking/Undocking functions
12134 //-----------------------------------------------------------------------------
12135 // - DockContextQueueDock()
12136 // - DockContextQueueUndockWindow()
12137 // - DockContextQueueUndockNode()
12138 // - DockContextQueueNotifyRemovedNode()
12139 // - DockContextProcessDock()
12140 // - DockContextProcessUndockWindow()
12141 // - DockContextProcessUndockNode()
12142 // - DockContextCalcDropPosForDocking()
12143 //-----------------------------------------------------------------------------
12144 
DockContextQueueDock(ImGuiContext * ctx,ImGuiWindow * target,ImGuiDockNode * target_node,ImGuiWindow * payload,ImGuiDir split_dir,float split_ratio,bool split_outer)12145 void ImGui::DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer)
12146 {
12147     IM_ASSERT(target != payload);
12148     ImGuiDockRequest req;
12149     req.Type = ImGuiDockRequestType_Dock;
12150     req.DockTargetWindow = target;
12151     req.DockTargetNode = target_node;
12152     req.DockPayload = payload;
12153     req.DockSplitDir = split_dir;
12154     req.DockSplitRatio = split_ratio;
12155     req.DockSplitOuter = split_outer;
12156     ctx->DockContext.Requests.push_back(req);
12157 }
12158 
DockContextQueueUndockWindow(ImGuiContext * ctx,ImGuiWindow * window)12159 void ImGui::DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window)
12160 {
12161     ImGuiDockRequest req;
12162     req.Type = ImGuiDockRequestType_Undock;
12163     req.UndockTargetWindow = window;
12164     ctx->DockContext.Requests.push_back(req);
12165 }
12166 
DockContextQueueUndockNode(ImGuiContext * ctx,ImGuiDockNode * node)12167 void ImGui::DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node)
12168 {
12169     ImGuiDockRequest req;
12170     req.Type = ImGuiDockRequestType_Undock;
12171     req.UndockTargetNode = node;
12172     ctx->DockContext.Requests.push_back(req);
12173 }
12174 
DockContextQueueNotifyRemovedNode(ImGuiContext * ctx,ImGuiDockNode * node)12175 void ImGui::DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node)
12176 {
12177     ImGuiDockContext* dc  = &ctx->DockContext;
12178     for (int n = 0; n < dc->Requests.Size; n++)
12179         if (dc->Requests[n].DockTargetNode == node)
12180             dc->Requests[n].Type = ImGuiDockRequestType_None;
12181 }
12182 
DockContextProcessDock(ImGuiContext * ctx,ImGuiDockRequest * req)12183 void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req)
12184 {
12185     IM_ASSERT((req->Type == ImGuiDockRequestType_Dock && req->DockPayload != NULL) || (req->Type == ImGuiDockRequestType_Split && req->DockPayload == NULL));
12186     IM_ASSERT(req->DockTargetWindow != NULL || req->DockTargetNode != NULL);
12187 
12188     ImGuiContext& g = *ctx;
12189     IM_UNUSED(g);
12190 
12191     ImGuiWindow* payload_window = req->DockPayload;     // Optional
12192     ImGuiWindow* target_window = req->DockTargetWindow;
12193     ImGuiDockNode* node = req->DockTargetNode;
12194     if (payload_window)
12195         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);
12196     else
12197         IMGUI_DEBUG_LOG_DOCKING("DockContextProcessDock node 0x%08X, split_dir %d\n", node ? node->ID : 0, req->DockSplitDir);
12198 
12199     // Decide which Tab will be selected at the end of the operation
12200     ImGuiID next_selected_id = 0;
12201     ImGuiDockNode* payload_node = NULL;
12202     if (payload_window)
12203     {
12204         payload_node = payload_window->DockNodeAsHost;
12205         payload_window->DockNodeAsHost = NULL; // Important to clear this as the node will have its life as a child which might be merged/deleted later.
12206         if (payload_node && payload_node->IsLeafNode())
12207             next_selected_id = payload_node->TabBar->NextSelectedTabId ? payload_node->TabBar->NextSelectedTabId : payload_node->TabBar->SelectedTabId;
12208         if (payload_node == NULL)
12209             next_selected_id = payload_window->ID;
12210     }
12211 
12212     // FIXME-DOCK: When we are trying to dock an existing single-window node into a loose window, transfer Node ID as well
12213     // When processing an interactive split, usually LastFrameAlive will be < g.FrameCount. But DockBuilder operations can make it ==.
12214     if (node)
12215         IM_ASSERT(node->LastFrameAlive <= g.FrameCount);
12216     if (node && target_window && node == target_window->DockNodeAsHost)
12217         IM_ASSERT(node->Windows.Size > 0 || node->IsSplitNode() || node->IsCentralNode());
12218 
12219     // Create new node and add existing window to it
12220     if (node == NULL)
12221     {
12222         node = DockContextAddNode(ctx, 0);
12223         node->Pos = target_window->Pos;
12224         node->Size = target_window->Size;
12225         if (target_window->DockNodeAsHost == NULL)
12226         {
12227             DockNodeAddWindow(node, target_window, true);
12228             node->TabBar->Tabs[0].Flags &= ~ImGuiTabItemFlags_Unsorted;
12229             target_window->DockIsActive = true;
12230         }
12231     }
12232 
12233     ImGuiDir split_dir = req->DockSplitDir;
12234     if (split_dir != ImGuiDir_None)
12235     {
12236         // Split into one, one side will be our payload node unless we are dropping a loose window
12237         const ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
12238         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
12239         const float split_ratio = req->DockSplitRatio;
12240         DockNodeTreeSplit(ctx, node, split_axis, split_inheritor_child_idx, split_ratio, payload_node);  // payload_node may be NULL here!
12241         ImGuiDockNode* new_node = node->ChildNodes[split_inheritor_child_idx ^ 1];
12242         new_node->HostWindow = node->HostWindow;
12243         node = new_node;
12244     }
12245     node->LocalFlags &= ~ImGuiDockNodeFlags_HiddenTabBar;
12246 
12247     if (node != payload_node)
12248     {
12249         // 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!)
12250         if (node->Windows.Size > 0 && node->TabBar == NULL)
12251         {
12252             DockNodeAddTabBar(node);
12253             for (int n = 0; n < node->Windows.Size; n++)
12254                 TabBarAddTab(node->TabBar, ImGuiTabItemFlags_None, node->Windows[n]);
12255         }
12256 
12257         if (payload_node != NULL)
12258         {
12259             // Transfer full payload node (with 1+ child windows or child nodes)
12260             if (payload_node->IsSplitNode())
12261             {
12262                 if (node->Windows.Size > 0)
12263                 {
12264                     // 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.
12265                     // In this situation, we move the windows of the target node into the currently visible node of the payload.
12266                     // This allows us to preserve some of the underlying dock tree settings nicely.
12267                     IM_ASSERT(payload_node->OnlyNodeWithWindows != NULL); // The docking should have been blocked by DockNodePreviewDockSetup() early on and never submitted.
12268                     ImGuiDockNode* visible_node = payload_node->OnlyNodeWithWindows;
12269                     if (visible_node->TabBar)
12270                         IM_ASSERT(visible_node->TabBar->Tabs.Size > 0);
12271                     DockNodeMoveWindows(node, visible_node);
12272                     DockNodeMoveWindows(visible_node, node);
12273                     DockSettingsRenameNodeReferences(node->ID, visible_node->ID);
12274                 }
12275                 if (node->IsCentralNode())
12276                 {
12277                     // Central node property needs to be moved to a leaf node, pick the last focused one.
12278                     // FIXME-DOCK: If we had to transfer other flags here, what would the policy be?
12279                     ImGuiDockNode* last_focused_node = DockContextFindNodeByID(ctx, payload_node->LastFocusedNodeId);
12280                     IM_ASSERT(last_focused_node != NULL);
12281                     ImGuiDockNode* last_focused_root_node = DockNodeGetRootNode(last_focused_node);
12282                     IM_ASSERT(last_focused_root_node == DockNodeGetRootNode(payload_node));
12283                     last_focused_node->LocalFlags |= ImGuiDockNodeFlags_CentralNode;
12284                     node->LocalFlags &= ~ImGuiDockNodeFlags_CentralNode;
12285                     last_focused_root_node->CentralNode = last_focused_node;
12286                 }
12287 
12288                 IM_ASSERT(node->Windows.Size == 0);
12289                 DockNodeMoveChildNodes(node, payload_node);
12290             }
12291             else
12292             {
12293                 const ImGuiID payload_dock_id = payload_node->ID;
12294                 DockNodeMoveWindows(node, payload_node);
12295                 DockSettingsRenameNodeReferences(payload_dock_id, node->ID);
12296             }
12297             DockContextRemoveNode(ctx, payload_node, true);
12298         }
12299         else if (payload_window)
12300         {
12301             // Transfer single window
12302             const ImGuiID payload_dock_id = payload_window->DockId;
12303             node->VisibleWindow = payload_window;
12304             DockNodeAddWindow(node, payload_window, true);
12305             if (payload_dock_id != 0)
12306                 DockSettingsRenameNodeReferences(payload_dock_id, node->ID);
12307         }
12308     }
12309     else
12310     {
12311         // When docking a floating single window node we want to reevaluate auto-hiding of the tab bar
12312         node->WantHiddenTabBarUpdate = true;
12313     }
12314 
12315     // Update selection immediately
12316     if (ImGuiTabBar* tab_bar = node->TabBar)
12317         tab_bar->NextSelectedTabId = next_selected_id;
12318     MarkIniSettingsDirty();
12319 }
12320 
DockContextProcessUndockWindow(ImGuiContext * ctx,ImGuiWindow * window,bool clear_persistent_docking_ref)12321 void ImGui::DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window, bool clear_persistent_docking_ref)
12322 {
12323     IMGUI_DEBUG_LOG_DOCKING("DockContextProcessUndockWindow window '%s', clear_persistent_docking_ref = %d\n", window->Name, clear_persistent_docking_ref);
12324     IM_UNUSED(ctx);
12325     if (window->DockNode)
12326         DockNodeRemoveWindow(window->DockNode, window, clear_persistent_docking_ref ? 0 : window->DockId);
12327     else
12328         window->DockId = 0;
12329     window->Collapsed = false;
12330     window->DockIsActive = false;
12331     window->DockTabIsVisible = false;
12332     MarkIniSettingsDirty();
12333 }
12334 
DockContextProcessUndockNode(ImGuiContext * ctx,ImGuiDockNode * node)12335 void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node)
12336 {
12337     IMGUI_DEBUG_LOG_DOCKING("DockContextProcessUndockNode node %08X\n", node->ID);
12338     IM_ASSERT(node->IsLeafNode());
12339     IM_ASSERT(node->Windows.Size >= 1);
12340 
12341     if (node->IsRootNode() || node->IsCentralNode())
12342     {
12343         // 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.
12344         ImGuiDockNode* new_node = DockContextAddNode(ctx, 0);
12345         DockNodeMoveWindows(new_node, node);
12346         DockSettingsRenameNodeReferences(node->ID, new_node->ID);
12347         for (int n = 0; n < new_node->Windows.Size; n++)
12348             UpdateWindowParentAndRootLinks(new_node->Windows[n], new_node->Windows[n]->Flags, NULL);
12349         node = new_node;
12350     }
12351     else
12352     {
12353         // Otherwise extract our node and merging our sibling back into the parent node.
12354         IM_ASSERT(node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node);
12355         int index_in_parent = (node->ParentNode->ChildNodes[0] == node) ? 0 : 1;
12356         node->ParentNode->ChildNodes[index_in_parent] = NULL;
12357         DockNodeTreeMerge(ctx, node->ParentNode, node->ParentNode->ChildNodes[index_in_parent ^ 1]);
12358         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
12359         node->ParentNode = NULL;
12360     }
12361     node->AuthorityForPos = node->AuthorityForSize = ImGuiDataAuthority_Window;
12362     node->WantMouseMove = true;
12363     MarkIniSettingsDirty();
12364 }
12365 
12366 // This is mostly used for automation.
DockContextCalcDropPosForDocking(ImGuiWindow * target,ImGuiDockNode * target_node,ImGuiWindow * payload,ImGuiDir split_dir,bool split_outer,ImVec2 * out_pos)12367 bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos)
12368 {
12369     if (split_outer)
12370     {
12371         IM_ASSERT(0);
12372     }
12373     else
12374     {
12375         ImGuiDockPreviewData split_data;
12376         DockNodePreviewDockSetup(target, target_node, payload, &split_data, false, split_outer);
12377         if (split_data.DropRectsDraw[split_dir+1].IsInverted())
12378             return false;
12379         *out_pos = split_data.DropRectsDraw[split_dir+1].GetCenter();
12380         return true;
12381     }
12382     return false;
12383 }
12384 
12385 //-----------------------------------------------------------------------------
12386 // Docking: ImGuiDockNode
12387 //-----------------------------------------------------------------------------
12388 // - DockNodeGetTabOrder()
12389 // - DockNodeAddWindow()
12390 // - DockNodeRemoveWindow()
12391 // - DockNodeMoveChildNodes()
12392 // - DockNodeMoveWindows()
12393 // - DockNodeApplyPosSizeToWindows()
12394 // - DockNodeHideHostWindow()
12395 // - ImGuiDockNodeFindInfoResults
12396 // - DockNodeFindInfo()
12397 // - DockNodeFindWindowByID()
12398 // - DockNodeUpdateVisibleFlagAndInactiveChilds()
12399 // - DockNodeUpdateVisibleFlag()
12400 // - DockNodeStartMouseMovingWindow()
12401 // - DockNodeUpdate()
12402 // - DockNodeUpdateWindowMenu()
12403 // - DockNodeUpdateTabBar()
12404 // - DockNodeAddTabBar()
12405 // - DockNodeRemoveTabBar()
12406 // - DockNodeIsDropAllowedOne()
12407 // - DockNodeIsDropAllowed()
12408 // - DockNodeCalcTabBarLayout()
12409 // - DockNodeCalcSplitRects()
12410 // - DockNodeCalcDropRectsAndTestMousePos()
12411 // - DockNodePreviewDockSetup()
12412 // - DockNodePreviewDockRender()
12413 //-----------------------------------------------------------------------------
12414 
ImGuiDockNode(ImGuiID id)12415 ImGuiDockNode::ImGuiDockNode(ImGuiID id)
12416 {
12417     ID = id;
12418     SharedFlags = LocalFlags = ImGuiDockNodeFlags_None;
12419     ParentNode = ChildNodes[0] = ChildNodes[1] = NULL;
12420     TabBar = NULL;
12421     SplitAxis = ImGuiAxis_None;
12422 
12423     State = ImGuiDockNodeState_Unknown;
12424     HostWindow = VisibleWindow = NULL;
12425     CentralNode = OnlyNodeWithWindows = NULL;
12426     LastFrameAlive = LastFrameActive = LastFrameFocused = -1;
12427     LastFocusedNodeId = 0;
12428     SelectedTabId = 0;
12429     WantCloseTabId = 0;
12430     AuthorityForPos = AuthorityForSize = ImGuiDataAuthority_DockNode;
12431     AuthorityForViewport = ImGuiDataAuthority_Auto;
12432     IsVisible = true;
12433     IsFocused = HasCloseButton = HasWindowMenuButton = EnableCloseButton = false;
12434     WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false;
12435     MarkedForPosSizeWrite = false;
12436 }
12437 
~ImGuiDockNode()12438 ImGuiDockNode::~ImGuiDockNode()
12439 {
12440     IM_DELETE(TabBar);
12441     TabBar = NULL;
12442     ChildNodes[0] = ChildNodes[1] = NULL;
12443 }
12444 
DockNodeGetTabOrder(ImGuiWindow * window)12445 int ImGui::DockNodeGetTabOrder(ImGuiWindow* window)
12446 {
12447     ImGuiTabBar* tab_bar = window->DockNode->TabBar;
12448     if (tab_bar == NULL)
12449         return -1;
12450     ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, window->ID);
12451     return tab ? tab_bar->GetTabOrder(tab) : -1;
12452 }
12453 
DockNodeAddWindow(ImGuiDockNode * node,ImGuiWindow * window,bool add_to_tab_bar)12454 static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, bool add_to_tab_bar)
12455 {
12456     ImGuiContext& g = *GImGui; (void)g;
12457     if (window->DockNode)
12458     {
12459         // Can overwrite an existing window->DockNode (e.g. pointing to a disabled DockSpace node)
12460         IM_ASSERT(window->DockNode->ID != node->ID);
12461         DockNodeRemoveWindow(window->DockNode, window, 0);
12462     }
12463     IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL);
12464     IMGUI_DEBUG_LOG_DOCKING("DockNodeAddWindow node 0x%08X window '%s'\n", node->ID, window->Name);
12465 
12466     node->Windows.push_back(window);
12467     node->WantHiddenTabBarUpdate = true;
12468     window->DockNode = node;
12469     window->DockId = node->ID;
12470     window->DockIsActive = (node->Windows.Size > 1);
12471     window->DockTabWantClose = false;
12472 
12473     // If more than 2 windows appeared on the same frame, we'll create a new hosting DockNode from the point of the second window submission.
12474     // Then we need to hide the first window (after its been output) otherwise it would be visible as a standalone window for one frame.
12475     if (node->HostWindow == NULL && node->Windows.Size == 2 && node->Windows[0]->WasActive == false)
12476     {
12477         node->Windows[0]->Hidden = true;
12478         node->Windows[0]->HiddenFramesCanSkipItems = 1;
12479     }
12480 
12481     // When reactivating a node with one or two loose window, the window pos/size/viewport are authoritative over the node storage.
12482     // In particular it is important we init the viewport from the first window so we don't create two viewports and drop one.
12483     if (node->HostWindow == NULL && node->IsFloatingNode())
12484     {
12485         if (node->AuthorityForPos == ImGuiDataAuthority_Auto)
12486             node->AuthorityForPos = ImGuiDataAuthority_Window;
12487         if (node->AuthorityForSize == ImGuiDataAuthority_Auto)
12488             node->AuthorityForSize = ImGuiDataAuthority_Window;
12489         if (node->AuthorityForViewport == ImGuiDataAuthority_Auto)
12490             node->AuthorityForViewport = ImGuiDataAuthority_Window;
12491     }
12492 
12493     // Add to tab bar if requested
12494     if (add_to_tab_bar)
12495     {
12496         if (node->TabBar == NULL)
12497         {
12498             DockNodeAddTabBar(node);
12499             node->TabBar->SelectedTabId = node->TabBar->NextSelectedTabId = node->SelectedTabId;
12500 
12501             // Add existing windows
12502             for (int n = 0; n < node->Windows.Size - 1; n++)
12503                 TabBarAddTab(node->TabBar, ImGuiTabItemFlags_None, node->Windows[n]);
12504         }
12505         TabBarAddTab(node->TabBar, ImGuiTabItemFlags_Unsorted, window);
12506     }
12507 
12508     DockNodeUpdateVisibleFlag(node);
12509 
12510     // 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.
12511     if (node->HostWindow)
12512         UpdateWindowParentAndRootLinks(window, window->Flags | ImGuiWindowFlags_ChildWindow, node->HostWindow);
12513 }
12514 
DockNodeRemoveWindow(ImGuiDockNode * node,ImGuiWindow * window,ImGuiID save_dock_id)12515 static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id)
12516 {
12517     ImGuiContext& g = *GImGui;
12518     IM_ASSERT(window->DockNode == node);
12519     //IM_ASSERT(window->RootWindow == node->HostWindow);
12520     //IM_ASSERT(window->LastFrameActive < g.FrameCount);    // We may call this from Begin()
12521     IM_ASSERT(save_dock_id == 0 || save_dock_id == node->ID);
12522     IMGUI_DEBUG_LOG_DOCKING("DockNodeRemoveWindow node 0x%08X window '%s'\n", node->ID, window->Name);
12523 
12524     window->DockNode = NULL;
12525     window->DockIsActive = window->DockTabWantClose = false;
12526     window->DockId = save_dock_id;
12527     UpdateWindowParentAndRootLinks(window, window->Flags & ~ImGuiWindowFlags_ChildWindow, NULL); // Update immediately
12528 
12529     // Remove window
12530     bool erased = false;
12531     IM_UNUSED(erased); // [Bruno Levy] silence a warning
12532     for (int n = 0; n < node->Windows.Size; n++)
12533         if (node->Windows[n] == window)
12534         {
12535             node->Windows.erase(node->Windows.Data + n);
12536             erased = true;
12537             break;
12538         }
12539     IM_ASSERT(erased);
12540     if (node->VisibleWindow == window)
12541         node->VisibleWindow = NULL;
12542 
12543     // Remove tab and possibly tab bar
12544     node->WantHiddenTabBarUpdate = true;
12545     if (node->TabBar)
12546     {
12547         TabBarRemoveTab(node->TabBar, window->ID);
12548         const int tab_count_threshold_for_tab_bar = node->IsCentralNode() ? 1 : 2;
12549         if (node->Windows.Size < tab_count_threshold_for_tab_bar)
12550             DockNodeRemoveTabBar(node);
12551     }
12552 
12553     if (node->Windows.Size == 0 && !node->IsCentralNode() && !node->IsDockSpace() && window->DockId != node->ID)
12554     {
12555         // Automatic dock node delete themselves if they are not holding at least one tab
12556         DockContextRemoveNode(&g, node, true);
12557         return;
12558     }
12559 
12560     if (node->Windows.Size == 1 && !node->IsCentralNode() && node->HostWindow)
12561     {
12562         ImGuiWindow* remaining_window = node->Windows[0];
12563         if (node->HostWindow->ViewportOwned && node->IsRootNode())
12564         {
12565             // Transfer viewport back to the remaining loose window
12566             IM_ASSERT(node->HostWindow->Viewport->Window == node->HostWindow);
12567             node->HostWindow->Viewport->Window = remaining_window;
12568             node->HostWindow->Viewport->ID = remaining_window->ID;
12569         }
12570         remaining_window->Collapsed = node->HostWindow->Collapsed;
12571     }
12572 
12573     // Update visibility immediately is required so the DockNodeUpdateRemoveInactiveChilds() processing can reflect changes up the tree
12574     DockNodeUpdateVisibleFlag(node);
12575 }
12576 
DockNodeMoveChildNodes(ImGuiDockNode * dst_node,ImGuiDockNode * src_node)12577 static void ImGui::DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode* src_node)
12578 {
12579     IM_ASSERT(dst_node->Windows.Size == 0);
12580     dst_node->ChildNodes[0] = src_node->ChildNodes[0];
12581     dst_node->ChildNodes[1] = src_node->ChildNodes[1];
12582     if (dst_node->ChildNodes[0])
12583         dst_node->ChildNodes[0]->ParentNode = dst_node;
12584     if (dst_node->ChildNodes[1])
12585         dst_node->ChildNodes[1]->ParentNode = dst_node;
12586     dst_node->SplitAxis = src_node->SplitAxis;
12587     dst_node->SizeRef = src_node->SizeRef;
12588     src_node->ChildNodes[0] = src_node->ChildNodes[1] = NULL;
12589 }
12590 
DockNodeMoveWindows(ImGuiDockNode * dst_node,ImGuiDockNode * src_node)12591 static void ImGui::DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node)
12592 {
12593     // Insert tabs in the same orders as currently ordered (node->Windows isn't ordered)
12594     IM_ASSERT(src_node && dst_node && dst_node != src_node);
12595     ImGuiTabBar* src_tab_bar = src_node->TabBar;
12596     if (src_tab_bar != NULL)
12597         IM_ASSERT(src_node->Windows.Size == src_node->TabBar->Tabs.Size);
12598 
12599     // If the dst_node is empty we can just move the entire tab bar (to preserve selection, scrolling, etc.)
12600     bool move_tab_bar = (src_tab_bar != NULL) && (dst_node->TabBar == NULL);
12601     if (move_tab_bar)
12602     {
12603         dst_node->TabBar = src_node->TabBar;
12604         src_node->TabBar = NULL;
12605     }
12606 
12607     for (int n = 0; n < src_node->Windows.Size; n++)
12608     {
12609         ImGuiWindow* window = src_tab_bar ? src_tab_bar->Tabs[n].Window : src_node->Windows[n];
12610         window->DockNode = NULL;
12611         window->DockIsActive = false;
12612         DockNodeAddWindow(dst_node, window, move_tab_bar ? false : true);
12613     }
12614     src_node->Windows.clear();
12615 
12616     if (!move_tab_bar && src_node->TabBar)
12617     {
12618         if (dst_node->TabBar)
12619             dst_node->TabBar->SelectedTabId = src_node->TabBar->SelectedTabId;
12620         DockNodeRemoveTabBar(src_node);
12621     }
12622 }
12623 
DockNodeApplyPosSizeToWindows(ImGuiDockNode * node)12624 static void ImGui::DockNodeApplyPosSizeToWindows(ImGuiDockNode* node)
12625 {
12626     for (int n = 0; n < node->Windows.Size; n++)
12627     {
12628         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
12629         SetWindowSize(node->Windows[n], node->Size, ImGuiCond_Always);
12630     }
12631 }
12632 
DockNodeHideHostWindow(ImGuiDockNode * node)12633 static void ImGui::DockNodeHideHostWindow(ImGuiDockNode* node)
12634 {
12635     if (node->HostWindow)
12636     {
12637         if (node->HostWindow->DockNodeAsHost == node)
12638             node->HostWindow->DockNodeAsHost = NULL;
12639         node->HostWindow = NULL;
12640     }
12641 
12642     if (node->Windows.Size == 1)
12643     {
12644         node->VisibleWindow = node->Windows[0];
12645         node->Windows[0]->DockIsActive = false;
12646     }
12647 
12648     if (node->TabBar)
12649         DockNodeRemoveTabBar(node);
12650 }
12651 
12652 // Search function called once by root node in DockNodeUpdate()
12653 struct ImGuiDockNodeFindInfoResults
12654 {
12655     ImGuiDockNode*      CentralNode;
12656     ImGuiDockNode*      FirstNodeWithWindows;
12657     int                 CountNodesWithWindows;
12658     //ImGuiWindowClass  WindowClassForMerges;
12659 
ImGuiDockNodeFindInfoResultsImGuiDockNodeFindInfoResults12660     ImGuiDockNodeFindInfoResults() { CentralNode = FirstNodeWithWindows = NULL; CountNodesWithWindows = 0; }
12661 };
12662 
DockNodeFindInfo(ImGuiDockNode * node,ImGuiDockNodeFindInfoResults * results)12663 static void DockNodeFindInfo(ImGuiDockNode* node, ImGuiDockNodeFindInfoResults* results)
12664 {
12665     if (node->Windows.Size > 0)
12666     {
12667         if (results->FirstNodeWithWindows == NULL)
12668             results->FirstNodeWithWindows = node;
12669         results->CountNodesWithWindows++;
12670     }
12671     if (node->IsCentralNode())
12672     {
12673         IM_ASSERT(results->CentralNode == NULL); // Should be only one
12674         IM_ASSERT(node->IsLeafNode() && "If you get this assert: please submit .ini file + repro of actions leading to this.");
12675         results->CentralNode = node;
12676     }
12677     if (results->CountNodesWithWindows > 1 && results->CentralNode != NULL)
12678         return;
12679     if (node->ChildNodes[0])
12680         DockNodeFindInfo(node->ChildNodes[0], results);
12681     if (node->ChildNodes[1])
12682         DockNodeFindInfo(node->ChildNodes[1], results);
12683 }
12684 
DockNodeFindWindowByID(ImGuiDockNode * node,ImGuiID id)12685 static ImGuiWindow* ImGui::DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id)
12686 {
12687     IM_ASSERT(id != 0);
12688     for (int n = 0; n < node->Windows.Size; n++)
12689         if (node->Windows[n]->ID == id)
12690             return node->Windows[n];
12691     return NULL;
12692 }
12693 
12694 // - Remove inactive windows/nodes.
12695 // - Update visibility flag.
DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode * node)12696 static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node)
12697 {
12698     ImGuiContext& g = *GImGui;
12699     IM_ASSERT(node->ParentNode == NULL || node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node);
12700 
12701     // Inherit most flags
12702     if (node->ParentNode)
12703         node->SharedFlags = node->ParentNode->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_;
12704 
12705     // Recurse into children
12706     // There is the possibility that one of our child becoming empty will delete itself and moving its sibling contents into 'node'.
12707     // If 'node->ChildNode[0]' delete itself, then 'node->ChildNode[1]->Windows' will be moved into 'node'
12708     // 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)
12709     if (node->ChildNodes[0])
12710         DockNodeUpdateVisibleFlagAndInactiveChilds(node->ChildNodes[0]);
12711     if (node->ChildNodes[1])
12712         DockNodeUpdateVisibleFlagAndInactiveChilds(node->ChildNodes[1]);
12713 
12714     // Remove inactive windows
12715     // Merge node flags overrides stored in windows
12716     for (int window_n = 0; window_n < node->Windows.Size; window_n++)
12717     {
12718         ImGuiWindow* window = node->Windows[window_n];
12719         IM_ASSERT(window->DockNode == node);
12720 
12721         bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount);
12722         bool remove = false;
12723         remove |= node_was_active && (window->LastFrameActive + 1 < g.FrameCount);
12724         remove |= node_was_active && (node->WantCloseAll || node->WantCloseTabId == window->ID) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument);  // Submit all _expected_ closure from last frame
12725         remove |= (window->DockTabWantClose);
12726         if (remove)
12727         {
12728             window->DockTabWantClose = false;
12729             if (node->Windows.Size == 1 && !node->IsCentralNode())
12730             {
12731                 DockNodeHideHostWindow(node);
12732                 node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow;
12733                 DockNodeRemoveWindow(node, window, node->ID); // Will delete the node so it'll be invalid on return
12734                 return;
12735             }
12736             DockNodeRemoveWindow(node, window, node->ID);
12737             window_n--;
12738         }
12739         else
12740         {
12741             node->LocalFlags &= ~window->WindowClass.DockNodeFlagsOverrideClear;
12742             node->LocalFlags |= window->WindowClass.DockNodeFlagsOverrideSet;
12743         }
12744     }
12745 
12746     // Auto-hide tab bar option
12747     ImGuiDockNodeFlags node_flags = node->GetMergedFlags();
12748     if (node->WantHiddenTabBarUpdate && node->Windows.Size == 1 && (node_flags & ImGuiDockNodeFlags_AutoHideTabBar) && !node->IsHiddenTabBar())
12749         node->WantHiddenTabBarToggle = true;
12750     node->WantHiddenTabBarUpdate = false;
12751 
12752     // Cancel toggling if we know our tab bar is enforced to be hidden at all times
12753     if (node->WantHiddenTabBarToggle && node->VisibleWindow && (node->VisibleWindow->WindowClass.DockNodeFlagsOverrideSet & ImGuiDockNodeFlags_HiddenTabBar))
12754         node->WantHiddenTabBarToggle = false;
12755 
12756     // Apply toggles at a single point of the frame (here!)
12757     if (node->Windows.Size > 1)
12758         node->LocalFlags &= ~ImGuiDockNodeFlags_HiddenTabBar;
12759     else if (node->WantHiddenTabBarToggle)
12760         node->LocalFlags ^= ImGuiDockNodeFlags_HiddenTabBar;
12761     node->WantHiddenTabBarToggle = false;
12762 
12763     DockNodeUpdateVisibleFlag(node);
12764 }
12765 
DockNodeUpdateVisibleFlag(ImGuiDockNode * node)12766 static void ImGui::DockNodeUpdateVisibleFlag(ImGuiDockNode* node)
12767 {
12768     // Update visibility flag
12769     bool is_visible = (node->ParentNode == NULL) ? node->IsDockSpace() : node->IsCentralNode();
12770     is_visible |= (node->Windows.Size > 0);
12771     is_visible |= (node->ChildNodes[0] && node->ChildNodes[0]->IsVisible);
12772     is_visible |= (node->ChildNodes[1] && node->ChildNodes[1]->IsVisible);
12773     node->IsVisible = is_visible;
12774 }
12775 
DockNodeStartMouseMovingWindow(ImGuiDockNode * node,ImGuiWindow * window)12776 static void ImGui::DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window)
12777 {
12778     ImGuiContext& g = *GImGui;
12779     IM_ASSERT(node->WantMouseMove == true);
12780     StartMouseMovingWindow(window);
12781     g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - node->Pos;
12782     g.MovingWindow = window; // If we are docked into a non moveable root window, StartMouseMovingWindow() won't set g.MovingWindow. Override that decision.
12783     node->WantMouseMove = false;
12784 }
12785 
12786 // Update CentralNode, OnlyNodeWithWindows, LastFocusedNodeID. Copy window class.
DockNodeUpdateForRootNode(ImGuiDockNode * node)12787 static void ImGui::DockNodeUpdateForRootNode(ImGuiDockNode* node)
12788 {
12789     DockNodeUpdateVisibleFlagAndInactiveChilds(node);
12790 
12791     // FIXME-DOCK: Merge this scan into the one above.
12792     // - Setup central node pointers
12793     // - 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!)
12794     ImGuiDockNodeFindInfoResults results;
12795     DockNodeFindInfo(node, &results);
12796     node->CentralNode = results.CentralNode;
12797     node->OnlyNodeWithWindows = (results.CountNodesWithWindows == 1) ? results.FirstNodeWithWindows : NULL;
12798     if (node->LastFocusedNodeId == 0 && results.FirstNodeWithWindows != NULL)
12799         node->LastFocusedNodeId = results.FirstNodeWithWindows->ID;
12800 
12801     // Copy the window class from of our first window so it can be used for proper dock filtering.
12802     // When node has mixed windows, prioritize the class with the most constraint (DockingAllowUnclassed = false) as the reference to copy.
12803     // FIXME-DOCK: We don't recurse properly, this code could be reworked to work from DockNodeUpdateScanRec.
12804     if (ImGuiDockNode* first_node_with_windows = results.FirstNodeWithWindows)
12805     {
12806         node->WindowClass = first_node_with_windows->Windows[0]->WindowClass;
12807         for (int n = 1; n < first_node_with_windows->Windows.Size; n++)
12808             if (first_node_with_windows->Windows[n]->WindowClass.DockingAllowUnclassed == false)
12809             {
12810                 node->WindowClass = first_node_with_windows->Windows[n]->WindowClass;
12811                 break;
12812             }
12813     }
12814 }
12815 
DockNodeUpdate(ImGuiDockNode * node)12816 static void ImGui::DockNodeUpdate(ImGuiDockNode* node)
12817 {
12818     ImGuiContext& g = *GImGui;
12819     IM_ASSERT(node->LastFrameActive != g.FrameCount);
12820     node->LastFrameAlive = g.FrameCount;
12821     node->MarkedForPosSizeWrite = false;
12822 
12823     node->CentralNode = node->OnlyNodeWithWindows = NULL;
12824     if (node->IsRootNode())
12825         DockNodeUpdateForRootNode(node);
12826 
12827     // Remove tab bar if not needed
12828     if (node->TabBar && node->IsNoTabBar())
12829         DockNodeRemoveTabBar(node);
12830 
12831     // 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)
12832     bool want_to_hide_host_window = false;
12833     if (node->Windows.Size <= 1 && node->IsFloatingNode() && node->IsLeafNode())
12834         if (!g.IO.ConfigDockingAlwaysTabBar && (node->Windows.Size == 0 || !node->Windows[0]->WindowClass.DockingAlwaysTabBar))
12835             want_to_hide_host_window = true;
12836     if (want_to_hide_host_window)
12837     {
12838         if (node->Windows.Size == 1)
12839         {
12840             // Floating window pos/size is authoritative
12841             ImGuiWindow* single_window = node->Windows[0];
12842             node->Pos = single_window->Pos;
12843             node->Size = single_window->SizeFull;
12844             node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window;
12845 
12846             // Transfer focus immediately so when we revert to a regular window it is immediately selected
12847             if (node->HostWindow && g.NavWindow == node->HostWindow)
12848                 FocusWindow(single_window);
12849             if (node->HostWindow)
12850             {
12851                 single_window->Viewport = node->HostWindow->Viewport;
12852                 single_window->ViewportId = node->HostWindow->ViewportId;
12853                 if (node->HostWindow->ViewportOwned)
12854                 {
12855                     single_window->Viewport->Window = single_window;
12856                     single_window->ViewportOwned = true;
12857                 }
12858             }
12859         }
12860 
12861         DockNodeHideHostWindow(node);
12862         node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow;
12863         node->WantCloseAll = false;
12864         node->WantCloseTabId = 0;
12865         node->HasCloseButton = node->HasWindowMenuButton = node->EnableCloseButton = false;
12866         node->LastFrameActive = g.FrameCount;
12867 
12868         if (node->WantMouseMove && node->Windows.Size == 1)
12869             DockNodeStartMouseMovingWindow(node, node->Windows[0]);
12870         return;
12871     }
12872 
12873     // In some circumstance we will defer creating the host window (so everything will be kept hidden),
12874     // while the expected visible window is resizing itself.
12875     // This is important for first-time (no ini settings restored) single window when io.ConfigDockingAlwaysTabBar is enabled,
12876     // otherwise the node ends up using the minimum window size. Effectively those windows will take an extra frame to show up:
12877     //   N+0: Begin(): window created (with no known size), node is created
12878     //   N+1: DockNodeUpdate(): node skip creating host window / Begin(): window size applied, not visible
12879     //   N+2: DockNodeUpdate(): node can create host window / Begin(): window becomes visible
12880     // We could remove this frame if we could reliably calculate the expected window size during node update, before the Begin() code.
12881     // It would require a generalization of CalcWindowExpectedSize(), probably extracting code away from Begin().
12882     // In reality it isn't very important as user quickly ends up with size data in .ini file.
12883     if (node->IsVisible && node->HostWindow == NULL && node->IsFloatingNode() && node->IsLeafNode())
12884     {
12885         IM_ASSERT(node->Windows.Size > 0);
12886         ImGuiWindow* ref_window = NULL;
12887         if (node->SelectedTabId != 0) // Note that we prune single-window-node settings on .ini loading, so this is generally 0 for them!
12888             ref_window = DockNodeFindWindowByID(node, node->SelectedTabId);
12889         if (ref_window == NULL)
12890             ref_window = node->Windows[0];
12891         if (ref_window->AutoFitFramesX > 0 || ref_window->AutoFitFramesY > 0)
12892         {
12893             node->State = ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing;
12894             return;
12895         }
12896     }
12897 
12898     const ImGuiDockNodeFlags node_flags = node->GetMergedFlags();
12899 
12900     // Bind or create host window
12901     ImGuiWindow* host_window = NULL;
12902     bool beginned_into_host_window = false;
12903     if (node->IsDockSpace())
12904     {
12905         // [Explicit root dockspace node]
12906         IM_ASSERT(node->HostWindow);
12907         node->EnableCloseButton = false;
12908         node->HasCloseButton = (node_flags & ImGuiDockNodeFlags_NoCloseButton) == 0;
12909         node->HasWindowMenuButton = (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0;
12910         host_window = node->HostWindow;
12911     }
12912     else
12913     {
12914         // [Automatic root or child nodes]
12915         node->EnableCloseButton = false;
12916         node->HasCloseButton = (node->Windows.Size > 0) && (node_flags & ImGuiDockNodeFlags_NoCloseButton) == 0;
12917         node->HasWindowMenuButton = (node->Windows.Size > 0) && (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0;
12918         for (int window_n = 0; window_n < node->Windows.Size; window_n++)
12919         {
12920             // FIXME-DOCK: Setting DockIsActive here means that for single active window in a leaf node, DockIsActive will be cleared until the next Begin() call.
12921             ImGuiWindow* window = node->Windows[window_n];
12922             window->DockIsActive = (node->Windows.Size > 1);
12923             node->EnableCloseButton |= window->HasCloseButton;
12924         }
12925 
12926         if (node->IsRootNode() && node->IsVisible)
12927         {
12928             ImGuiWindow* ref_window = (node->Windows.Size > 0) ? node->Windows[0] : NULL;
12929 
12930             // Sync Pos
12931             if (node->AuthorityForPos == ImGuiDataAuthority_Window && ref_window)
12932                 SetNextWindowPos(ref_window->Pos);
12933             else if (node->AuthorityForPos == ImGuiDataAuthority_DockNode)
12934                 SetNextWindowPos(node->Pos);
12935 
12936             // Sync Size
12937             if (node->AuthorityForSize == ImGuiDataAuthority_Window && ref_window)
12938                 SetNextWindowSize(ref_window->SizeFull);
12939             else if (node->AuthorityForSize == ImGuiDataAuthority_DockNode)
12940                 SetNextWindowSize(node->Size);
12941 
12942             // Sync Collapsed
12943             if (node->AuthorityForSize == ImGuiDataAuthority_Window && ref_window)
12944                 SetNextWindowCollapsed(ref_window->Collapsed);
12945 
12946             // Sync Viewport
12947             if (node->AuthorityForViewport == ImGuiDataAuthority_Window && ref_window)
12948                 SetNextWindowViewport(ref_window->ViewportId);
12949 
12950             SetNextWindowClass(&node->WindowClass);
12951 
12952             // Begin into the host window
12953             char window_label[20];
12954             DockNodeGetHostWindowTitle(node, window_label, IM_ARRAYSIZE(window_label));
12955             ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_DockNodeHost;
12956             window_flags |= ImGuiWindowFlags_NoFocusOnAppearing;
12957             window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoCollapse;
12958             window_flags |= ImGuiWindowFlags_NoTitleBar;
12959 
12960             PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
12961             Begin(window_label, NULL, window_flags);
12962             PopStyleVar();
12963             beginned_into_host_window = true;
12964 
12965             node->HostWindow = host_window = g.CurrentWindow;
12966             host_window->DockNodeAsHost = node;
12967             host_window->DC.CursorPos = host_window->Pos;
12968             node->Pos = host_window->Pos;
12969             node->Size = host_window->Size;
12970 
12971             // We set ImGuiWindowFlags_NoFocusOnAppearing because we don't want the host window to take full focus (e.g. steal NavWindow)
12972             // But we still it bring it to the front of display. There's no way to choose this precise behavior via window flags.
12973             // 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.
12974             // 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
12975             // 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
12976             // after the dock host window, losing their top-most status.
12977             if (node->HostWindow->Appearing)
12978                 BringWindowToDisplayFront(node->HostWindow);
12979 
12980             node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Auto;
12981         }
12982         else if (node->ParentNode)
12983         {
12984             node->HostWindow = host_window = node->ParentNode->HostWindow;
12985             node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Auto;
12986         }
12987         if (node->WantMouseMove && node->HostWindow)
12988             DockNodeStartMouseMovingWindow(node, node->HostWindow);
12989     }
12990 
12991     // Update focused node (the one whose title bar is highlight) within a node tree
12992     if (node->IsSplitNode())
12993         IM_ASSERT(node->TabBar == NULL);
12994     if (node->IsRootNode())
12995         if (g.NavWindow && g.NavWindow->RootWindowDockStop->DockNode && g.NavWindow->RootWindowDockStop->ParentWindow == host_window)
12996             node->LastFocusedNodeId = g.NavWindow->RootWindowDockStop->DockNode->ID;
12997 
12998     // We need to draw a background at the root level if requested by ImGuiDockNodeFlags_PassthruCentralNode, but we will only know the correct pos/size after
12999     // processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order!
13000     const bool render_dockspace_bg = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0;
13001     if (render_dockspace_bg)
13002     {
13003         host_window->DrawList->ChannelsSplit(2);
13004         host_window->DrawList->ChannelsSetCurrent(1);
13005     }
13006 
13007     // Register a hit-test hole in the window unless we are currently dragging a window that is compatible with our dockspace
13008     const ImGuiDockNode* central_node = node->CentralNode;
13009     const bool central_node_hole = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0 && central_node != NULL && central_node->IsEmpty();
13010     bool central_node_hole_register_hit_test_hole = central_node_hole;
13011     if (central_node_hole)
13012         if (const ImGuiPayload* payload = ImGui::GetDragDropPayload())
13013             if (payload->IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) && DockNodeIsDropAllowed(host_window, *(ImGuiWindow**)payload->Data))
13014                 central_node_hole_register_hit_test_hole = false;
13015     if (central_node_hole_register_hit_test_hole)
13016     {
13017         // Add a little padding to match the "resize from edges" behavior and allow grabbing the splitter easily.
13018         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
13019         ImRect central_hole(central_node->Pos, central_node->Pos + central_node->Size);
13020         central_hole.Expand(ImVec2(-WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS, -WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS));
13021         if (central_node_hole && !central_hole.IsInverted())
13022         {
13023             SetWindowHitTestHole(host_window, central_hole.Min, central_hole.Max - central_hole.Min);
13024             SetWindowHitTestHole(host_window->ParentWindow, central_hole.Min, central_hole.Max - central_hole.Min);
13025         }
13026     }
13027 
13028     // Update position/size, process and draw resizing splitters
13029     if (node->IsRootNode() && host_window)
13030     {
13031         DockNodeTreeUpdatePosSize(node, host_window->Pos, host_window->Size);
13032         DockNodeTreeUpdateSplitter(node);
13033     }
13034 
13035     // Draw empty node background (currently can only be the Central Node)
13036     if (host_window && node->IsEmpty() && node->IsVisible && !(node_flags & ImGuiDockNodeFlags_PassthruCentralNode))
13037         host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_DockingEmptyBg));
13038 
13039     // Draw whole dockspace background if ImGuiDockNodeFlags_PassthruCentralNode if set.
13040     if (render_dockspace_bg && node->IsVisible)
13041     {
13042         host_window->DrawList->ChannelsSetCurrent(0);
13043         if (central_node_hole)
13044             RenderRectFilledWithHole(host_window->DrawList, node->Rect(), central_node->Rect(), GetColorU32(ImGuiCol_WindowBg), 0.0f);
13045         else
13046             host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_WindowBg), 0.0f);
13047         host_window->DrawList->ChannelsMerge();
13048     }
13049 
13050     // Draw and populate Tab Bar
13051     if (host_window && node->Windows.Size > 0)
13052     {
13053         DockNodeUpdateTabBar(node, host_window);
13054     }
13055     else
13056     {
13057         node->WantCloseAll = false;
13058         node->WantCloseTabId = 0;
13059         node->IsFocused = false;
13060     }
13061     if (node->TabBar && node->TabBar->SelectedTabId)
13062         node->SelectedTabId = node->TabBar->SelectedTabId;
13063     else if (node->Windows.Size > 0)
13064         node->SelectedTabId = node->Windows[0]->ID;
13065 
13066     // Draw payload drop target
13067     if (host_window && node->IsVisible)
13068         if (node->IsRootNode() && (g.MovingWindow == NULL || g.MovingWindow->RootWindow != host_window))
13069             BeginDockableDragDropTarget(host_window);
13070 
13071     // We update this after DockNodeUpdateTabBar()
13072     node->LastFrameActive = g.FrameCount;
13073 
13074     // Recurse into children
13075     // FIXME-DOCK FIXME-OPT: Should not need to recurse into children
13076     if (host_window)
13077     {
13078         if (node->ChildNodes[0])
13079             DockNodeUpdate(node->ChildNodes[0]);
13080         if (node->ChildNodes[1])
13081             DockNodeUpdate(node->ChildNodes[1]);
13082 
13083         // Render outer borders last (after the tab bar)
13084         if (node->IsRootNode())
13085             RenderWindowOuterBorders(host_window);
13086     }
13087 
13088     // End host window
13089     if (beginned_into_host_window) //-V1020
13090         End();
13091 }
13092 
13093 // 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)13094 static int IMGUI_CDECL TabItemComparerByDockOrder(const void* lhs, const void* rhs)
13095 {
13096     ImGuiWindow* a = ((const ImGuiTabItem*)lhs)->Window;
13097     ImGuiWindow* b = ((const ImGuiTabItem*)rhs)->Window;
13098     if (int d = ((a->DockOrder == -1) ? INT_MAX : a->DockOrder) - ((b->DockOrder == -1) ? INT_MAX : b->DockOrder))
13099         return d;
13100     return (a->BeginOrderWithinContext - b->BeginOrderWithinContext);
13101 }
13102 
DockNodeUpdateWindowMenu(ImGuiDockNode * node,ImGuiTabBar * tab_bar)13103 static ImGuiID ImGui::DockNodeUpdateWindowMenu(ImGuiDockNode* node, ImGuiTabBar* tab_bar)
13104 {
13105     // Try to position the menu so it is more likely to stays within the same viewport
13106     ImGuiContext& g = *GImGui;
13107     ImGuiID ret_tab_id = 0;
13108     if (g.Style.WindowMenuButtonPosition == ImGuiDir_Left)
13109         SetNextWindowPos(ImVec2(node->Pos.x, node->Pos.y + GetFrameHeight()), ImGuiCond_Always, ImVec2(0.0f, 0.0f));
13110     else
13111         SetNextWindowPos(ImVec2(node->Pos.x + node->Size.x, node->Pos.y + GetFrameHeight()), ImGuiCond_Always, ImVec2(1.0f, 0.0f));
13112     if (BeginPopup("#WindowMenu"))
13113     {
13114         node->IsFocused = true;
13115         if (tab_bar->Tabs.Size == 1)
13116         {
13117             if (MenuItem("Hide tab bar", NULL, node->IsHiddenTabBar()))
13118                 node->WantHiddenTabBarToggle = true;
13119         }
13120         else
13121         {
13122             for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
13123             {
13124                 ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
13125                 IM_ASSERT(tab->Window != NULL);
13126                 if (Selectable(tab->Window->Name, tab->ID == tab_bar->SelectedTabId))
13127                     ret_tab_id = tab->ID;
13128                 SameLine();
13129                 Text("   ");
13130             }
13131         }
13132         EndPopup();
13133     }
13134     return ret_tab_id;
13135 }
13136 
DockNodeUpdateTabBar(ImGuiDockNode * node,ImGuiWindow * host_window)13137 static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window)
13138 {
13139     ImGuiContext& g = *GImGui;
13140     ImGuiStyle& style = g.Style;
13141 
13142     const bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount);
13143     const bool closed_all = node->WantCloseAll && node_was_active;
13144     const ImGuiID closed_one = node->WantCloseTabId && node_was_active;
13145     node->WantCloseAll = false;
13146     node->WantCloseTabId = 0;
13147 
13148     // Decide if we should use a focused title bar color
13149     bool is_focused = false;
13150     ImGuiDockNode* root_node = DockNodeGetRootNode(node);
13151     if (g.NavWindowingTarget)
13152         is_focused = (g.NavWindowingTarget->DockNode == node);
13153     else if (g.NavWindow && g.NavWindow->RootWindowForTitleBarHighlight == host_window->RootWindow && root_node->LastFocusedNodeId == node->ID)
13154         is_focused = true;
13155 
13156     // Hidden tab bar will show a triangle on the upper-left (in Begin)
13157     if (node->IsHiddenTabBar() || node->IsNoTabBar())
13158     {
13159         node->VisibleWindow = (node->Windows.Size > 0) ? node->Windows[0] : NULL;
13160         node->IsFocused = is_focused;
13161         if (is_focused)
13162             node->LastFrameFocused = g.FrameCount;
13163         if (node->VisibleWindow)
13164         {
13165             // Notify root of visible window (used to display title in OS task bar)
13166             if (is_focused || root_node->VisibleWindow == NULL)
13167                 root_node->VisibleWindow = node->VisibleWindow;
13168             if (node->TabBar)
13169                 node->TabBar->VisibleTabId = node->VisibleWindow->ID;
13170         }
13171         return;
13172     }
13173 
13174     // 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
13175     bool backup_skip_item = host_window->SkipItems;
13176     if (!node->IsDockSpace())
13177     {
13178         host_window->SkipItems = false;
13179         host_window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
13180         host_window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu);
13181     }
13182 
13183     // Use PushOverrideID() instead of PushID() to use the node id _without_ the host window ID.
13184     // 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,
13185     // as docked windows themselves will override the stack with their own root ID.
13186     PushOverrideID(node->ID);
13187     ImGuiTabBar* tab_bar = node->TabBar;
13188     bool tab_bar_is_recreated = (tab_bar == NULL); // Tab bar are automatically destroyed when a node gets hidden
13189     if (tab_bar == NULL)
13190     {
13191         DockNodeAddTabBar(node);
13192         tab_bar = node->TabBar;
13193     }
13194 
13195     ImGuiID focus_tab_id = 0;
13196     node->IsFocused = is_focused;
13197 
13198     const ImGuiDockNodeFlags node_flags = node->GetMergedFlags();
13199     const bool has_window_menu_button = (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0;
13200     const bool has_close_button = (node_flags & ImGuiDockNodeFlags_NoCloseButton) == 0;
13201 
13202     // In a dock node, the Collapse Button turns into the Window Menu button.
13203     // FIXME-DOCK FIXME-OPT: Could we recycle popups id across multiple dock nodes?
13204     if (has_window_menu_button && IsPopupOpen("#WindowMenu"))
13205     {
13206         if (ImGuiID tab_id = DockNodeUpdateWindowMenu(node, tab_bar))
13207             focus_tab_id = tab_bar->NextSelectedTabId = tab_id;
13208         is_focused |= node->IsFocused;
13209     }
13210 
13211     // Layout
13212     ImRect title_bar_rect, tab_bar_rect;
13213     ImVec2 window_menu_button_pos;
13214     DockNodeCalcTabBarLayout(node, &title_bar_rect, &tab_bar_rect, &window_menu_button_pos);
13215 
13216     // Title bar
13217     if (is_focused)
13218         node->LastFrameFocused = g.FrameCount;
13219     ImU32 title_bar_col = GetColorU32(host_window->Collapsed ? ImGuiCol_TitleBgCollapsed : is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
13220     host_window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window->WindowRounding, ImDrawCornerFlags_Top);
13221 
13222     // Docking/Collapse button
13223     if (has_window_menu_button)
13224     {
13225         if (CollapseButton(host_window->GetID("#COLLAPSE"), window_menu_button_pos, node))
13226             OpenPopup("#WindowMenu");
13227         if (IsItemActive())
13228             focus_tab_id = tab_bar->SelectedTabId;
13229     }
13230 
13231     // Submit new tabs and apply NavWindow focus back to the tab bar. They will be added as Unsorted and sorted below based on relative DockOrder value.
13232     const int tabs_count_old = tab_bar->Tabs.Size;
13233     for (int window_n = 0; window_n < node->Windows.Size; window_n++)
13234     {
13235         ImGuiWindow* window = node->Windows[window_n];
13236         if (g.NavWindow && g.NavWindow->RootWindowDockStop == window)
13237             tab_bar->SelectedTabId = window->ID;
13238         if (TabBarFindTabByID(tab_bar, window->ID) == NULL)
13239             TabBarAddTab(tab_bar, ImGuiTabItemFlags_Unsorted, window);
13240     }
13241 
13242     // If multiple tabs are appearing on the same frame, sort them based on their persistent DockOrder value
13243     int tabs_unsorted_start = tab_bar->Tabs.Size;
13244     for (int tab_n = tab_bar->Tabs.Size - 1; tab_n >= 0 && (tab_bar->Tabs[tab_n].Flags & ImGuiTabItemFlags_Unsorted); tab_n--)
13245     {
13246         // 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?
13247         tab_bar->Tabs[tab_n].Flags &= ~ImGuiTabItemFlags_Unsorted;
13248         tabs_unsorted_start = tab_n;
13249     }
13250     if (tab_bar->Tabs.Size > tabs_unsorted_start)
13251     {
13252         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)" : "");
13253         for (int tab_n = tabs_unsorted_start; tab_n < tab_bar->Tabs.Size; tab_n++)
13254             IMGUI_DEBUG_LOG_DOCKING(" - Tab '%s' Order %d\n", tab_bar->Tabs[tab_n].Window->Name, tab_bar->Tabs[tab_n].Window->DockOrder);
13255     if (tab_bar->Tabs.Size > tabs_unsorted_start + 1)
13256         ImQsort(tab_bar->Tabs.Data + tabs_unsorted_start, tab_bar->Tabs.Size - tabs_unsorted_start, sizeof(ImGuiTabItem), TabItemComparerByDockOrder);
13257     }
13258 
13259     // Selected newly added tabs, or persistent tab ID if the tab bar was just recreated
13260     if (tab_bar_is_recreated && TabBarFindTabByID(tab_bar, node->SelectedTabId) != NULL)
13261         tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = node->SelectedTabId;
13262     else if (tab_bar->Tabs.Size > tabs_count_old)
13263         tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = tab_bar->Tabs.back().Window->ID;
13264 
13265     // Begin tab bar
13266     ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_AutoSelectNewTabs; // | ImGuiTabBarFlags_NoTabListScrollingButtons);
13267     tab_bar_flags |= ImGuiTabBarFlags_SaveSettings | ImGuiTabBarFlags_DockNode;
13268     if (!host_window->Collapsed && is_focused)
13269         tab_bar_flags |= ImGuiTabBarFlags_IsFocused;
13270     BeginTabBarEx(tab_bar, tab_bar_rect, tab_bar_flags, node);
13271     //host_window->DrawList->AddRect(tab_bar_rect.Min, tab_bar_rect.Max, IM_COL32(255,0,255,255));
13272 
13273     // Submit actual tabs
13274     node->VisibleWindow = NULL;
13275     for (int window_n = 0; window_n < node->Windows.Size; window_n++)
13276     {
13277         ImGuiWindow* window = node->Windows[window_n];
13278         if ((closed_all || closed_one == window->ID) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument))
13279             continue;
13280         if (window->LastFrameActive + 1 >= g.FrameCount || !node_was_active)
13281         {
13282             ImGuiTabItemFlags tab_item_flags = 0;
13283             if (window->Flags & ImGuiWindowFlags_UnsavedDocument)
13284                 tab_item_flags |= ImGuiTabItemFlags_UnsavedDocument;
13285             if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)
13286                 tab_item_flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton;
13287 
13288             bool tab_open = true;
13289             TabItemEx(tab_bar, window->Name, window->HasCloseButton ? &tab_open : NULL, tab_item_flags, window);
13290             if (!tab_open)
13291                 node->WantCloseTabId = window->ID;
13292             if (tab_bar->VisibleTabId == window->ID)
13293                 node->VisibleWindow = window;
13294 
13295             // Store last item data so it can be queried with IsItemXXX functions after the user Begin() call
13296             window->DockTabItemStatusFlags = host_window->DC.LastItemStatusFlags;
13297             window->DockTabItemRect = host_window->DC.LastItemRect;
13298 
13299             // Update navigation ID on menu layer
13300             if (g.NavWindow && g.NavWindow->RootWindowDockStop == window && (window->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) == 0)
13301                 host_window->NavLastIds[1] = window->ID;
13302         }
13303     }
13304 
13305     // Notify root of visible window (used to display title in OS task bar)
13306     if (node->VisibleWindow)
13307         if (is_focused || root_node->VisibleWindow == NULL)
13308             root_node->VisibleWindow = node->VisibleWindow;
13309 
13310     // Close button (after VisibleWindow was updated)
13311     // Note that VisibleWindow may have been overrided by CTRL+Tabbing, so VisibleWindow->ID may be != from tab_bar->SelectedTabId
13312     if (has_close_button && node->VisibleWindow)
13313     {
13314         if (!node->VisibleWindow->HasCloseButton)
13315         {
13316             PushItemFlag(ImGuiItemFlags_Disabled, true);
13317             PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_Text] * ImVec4(1.0f,1.0f,1.0f,0.5f));
13318         }
13319         const float button_sz = g.FontSize;
13320         if (CloseButton(host_window->GetID("#CLOSE"), title_bar_rect.GetTR() + ImVec2(-style.FramePadding.x * 2.0f - button_sz, 0.0f)))
13321             if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_bar->VisibleTabId))
13322             {
13323                 node->WantCloseTabId = tab->ID;
13324                 TabBarCloseTab(tab_bar, tab);
13325             }
13326         //if (IsItemActive())
13327         //    focus_tab_id = tab_bar->SelectedTabId;
13328         if (!node->VisibleWindow->HasCloseButton)
13329         {
13330             PopStyleColor();
13331             PopItemFlag();
13332         }
13333     }
13334 
13335     // When clicking on the title bar outside of tabs, we still focus the selected tab for that node
13336     // FIXME: TabItem use AllowItemOverlap so we manually perform a more specific test for now (hovered || held)
13337     ImGuiID title_bar_id = host_window->GetID("#TITLEBAR");
13338     if (g.HoveredId == 0 || g.HoveredId == title_bar_id || g.ActiveId == title_bar_id)
13339     {
13340         bool held;
13341         ButtonBehavior(title_bar_rect, title_bar_id, NULL, &held);
13342         if (held)
13343         {
13344             if (IsMouseClicked(0))
13345                 focus_tab_id = tab_bar->SelectedTabId;
13346 
13347             // Forward moving request to selected window
13348             if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId))
13349                 StartMouseMovingWindowOrNode(tab->Window, node, false);
13350         }
13351     }
13352 
13353     // Forward focus from host node to selected window
13354     //if (is_focused && g.NavWindow == host_window && !g.NavWindowingTarget)
13355     //    focus_tab_id = tab_bar->SelectedTabId;
13356 
13357     // When clicked on a tab we requested focus to the docked child
13358     // This overrides the value set by "forward focus from host node to selected window".
13359     if (tab_bar->NextSelectedTabId)
13360         focus_tab_id = tab_bar->NextSelectedTabId;
13361 
13362     // Apply navigation focus
13363     if (focus_tab_id != 0)
13364         if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, focus_tab_id))
13365         {
13366             FocusWindow(tab->Window);
13367             NavInitWindow(tab->Window, false);
13368         }
13369 
13370     EndTabBar();
13371     PopID();
13372 
13373     // Restore SkipItems flag
13374     if (!node->IsDockSpace())
13375     {
13376         host_window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
13377         host_window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
13378         host_window->SkipItems = backup_skip_item;
13379     }
13380 }
13381 
DockNodeAddTabBar(ImGuiDockNode * node)13382 static void ImGui::DockNodeAddTabBar(ImGuiDockNode* node)
13383 {
13384     IM_ASSERT(node->TabBar == NULL);
13385     node->TabBar = IM_NEW(ImGuiTabBar);
13386 }
13387 
DockNodeRemoveTabBar(ImGuiDockNode * node)13388 static void ImGui::DockNodeRemoveTabBar(ImGuiDockNode* node)
13389 {
13390     if (node->TabBar == NULL)
13391         return;
13392     IM_DELETE(node->TabBar);
13393     node->TabBar = NULL;
13394 }
13395 
DockNodeIsDropAllowedOne(ImGuiWindow * payload,ImGuiWindow * host_window)13396 static bool DockNodeIsDropAllowedOne(ImGuiWindow* payload, ImGuiWindow* host_window)
13397 {
13398     if (host_window->DockNodeAsHost && host_window->DockNodeAsHost->IsDockSpace() && payload->BeginOrderWithinContext < host_window->BeginOrderWithinContext)
13399         return false;
13400 
13401     ImGuiWindowClass* host_class = host_window->DockNodeAsHost ? &host_window->DockNodeAsHost->WindowClass : &host_window->WindowClass;
13402     ImGuiWindowClass* payload_class = &payload->WindowClass;
13403     if (host_class->ClassId != payload_class->ClassId)
13404     {
13405         if (host_class->ClassId != 0 && host_class->DockingAllowUnclassed && payload_class->ClassId == 0)
13406             return true;
13407         if (payload_class->ClassId != 0 && payload_class->DockingAllowUnclassed && host_class->ClassId == 0)
13408             return true;
13409         return false;
13410     }
13411 
13412     return true;
13413 }
13414 
DockNodeIsDropAllowed(ImGuiWindow * host_window,ImGuiWindow * root_payload)13415 static bool ImGui::DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* root_payload)
13416 {
13417     if (root_payload->DockNodeAsHost && root_payload->DockNodeAsHost->IsSplitNode())
13418         return true;
13419 
13420     const int payload_count = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows.Size : 1;
13421     for (int payload_n = 0; payload_n < payload_count; payload_n++)
13422     {
13423         ImGuiWindow* payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows[payload_n] : root_payload;
13424         if (DockNodeIsDropAllowedOne(payload, host_window))
13425             return true;
13426     }
13427     return false;
13428 }
13429 
13430 // window menu button == collapse button when not in a dock node.
13431 // 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)13432 static void ImGui::DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos)
13433 {
13434     ImGuiContext& g = *GImGui;
13435     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);
13436     if (out_title_rect) { *out_title_rect = r; }
13437 
13438     ImVec2 window_menu_button_pos = r.Min;
13439     r.Min.x += g.Style.FramePadding.x;
13440     r.Max.x -= g.Style.FramePadding.x;
13441     if (node->HasCloseButton)
13442     {
13443         r.Max.x -= g.FontSize;// +1.0f; // In DockNodeUpdateTabBar() we currently display a disabled close button even if there is none.
13444     }
13445     if (node->HasWindowMenuButton && g.Style.WindowMenuButtonPosition == ImGuiDir_Left)
13446     {
13447         r.Min.x += g.FontSize; // + g.Style.ItemInnerSpacing.x; // <-- Adding ItemInnerSpacing makes the title text moves slightly when in a docking tab bar. Instead we adjusted RenderArrowDockMenu()
13448     }
13449     else if (node->HasWindowMenuButton && g.Style.WindowMenuButtonPosition == ImGuiDir_Right)
13450     {
13451         r.Max.x -= g.FontSize + g.Style.FramePadding.x;
13452         window_menu_button_pos = ImVec2(r.Max.x, r.Min.y);
13453     }
13454     if (out_tab_bar_rect) { *out_tab_bar_rect = r; }
13455     if (out_window_menu_button_pos) { *out_window_menu_button_pos = window_menu_button_pos; }
13456 }
13457 
DockNodeCalcSplitRects(ImVec2 & pos_old,ImVec2 & size_old,ImVec2 & pos_new,ImVec2 & size_new,ImGuiDir dir,ImVec2 size_new_desired)13458 void ImGui::DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired)
13459 {
13460     ImGuiContext& g = *GImGui;
13461     const float dock_spacing = g.Style.ItemInnerSpacing.x;
13462     const ImGuiAxis axis = (dir == ImGuiDir_Left || dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
13463     pos_new[axis ^ 1] = pos_old[axis ^ 1];
13464     size_new[axis ^ 1] = size_old[axis ^ 1];
13465 
13466     // Distribute size on given axis (with a desired size or equally)
13467     const float w_avail = size_old[axis] - dock_spacing;
13468     if (size_new_desired[axis] > 0.0f && size_new_desired[axis] <= w_avail * 0.5f)
13469     {
13470         size_new[axis] = size_new_desired[axis];
13471         size_old[axis] = IM_FLOOR(w_avail - size_new[axis]);
13472     }
13473     else
13474     {
13475         size_new[axis] = IM_FLOOR(w_avail * 0.5f);
13476         size_old[axis] = IM_FLOOR(w_avail - size_new[axis]);
13477     }
13478 
13479     // Position each node
13480     if (dir == ImGuiDir_Right || dir == ImGuiDir_Down)
13481     {
13482         pos_new[axis] = pos_old[axis] + size_old[axis] + dock_spacing;
13483     }
13484     else if (dir == ImGuiDir_Left || dir == ImGuiDir_Up)
13485     {
13486         pos_new[axis] = pos_old[axis];
13487         pos_old[axis] = pos_new[axis] + size_new[axis] + dock_spacing;
13488     }
13489 }
13490 
13491 // 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)13492 bool ImGui::DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_r, bool outer_docking, ImVec2* test_mouse_pos)
13493 {
13494     ImGuiContext& g = *GImGui;
13495 
13496     const float parent_smaller_axis = ImMin(parent.GetWidth(), parent.GetHeight());
13497     const float hs_for_central_nodes = ImMin(g.FontSize * 1.5f, ImMax(g.FontSize * 0.5f, parent_smaller_axis / 8.0f));
13498     float hs_w; // Half-size, longer axis
13499     float hs_h; // Half-size, smaller axis
13500     ImVec2 off; // Distance from edge or center
13501     if (outer_docking)
13502     {
13503         //hs_w = ImFloor(ImClamp(parent_smaller_axis - hs_for_central_nodes * 4.0f, g.FontSize * 0.5f, g.FontSize * 8.0f));
13504         //hs_h = ImFloor(hs_w * 0.15f);
13505         //off = ImVec2(ImFloor(parent.GetWidth() * 0.5f - GetFrameHeightWithSpacing() * 1.4f - hs_h), ImFloor(parent.GetHeight() * 0.5f - GetFrameHeightWithSpacing() * 1.4f - hs_h));
13506         hs_w = ImFloor(hs_for_central_nodes * 1.50f);
13507         hs_h = ImFloor(hs_for_central_nodes * 0.80f);
13508         off = ImVec2(ImFloor(parent.GetWidth() * 0.5f - hs_h), ImFloor(parent.GetHeight() * 0.5f - hs_h));
13509     }
13510     else
13511     {
13512         hs_w = ImFloor(hs_for_central_nodes);
13513         hs_h = ImFloor(hs_for_central_nodes * 0.90f);
13514         off = ImVec2(ImFloor(hs_w * 2.40f), ImFloor(hs_w * 2.40f));
13515     }
13516 
13517     ImVec2 c = ImFloor(parent.GetCenter());
13518     if      (dir == ImGuiDir_None)  { out_r = ImRect(c.x - hs_w, c.y - hs_w,         c.x + hs_w, c.y + hs_w);         }
13519     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); }
13520     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); }
13521     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); }
13522     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); }
13523 
13524     if (test_mouse_pos == NULL)
13525         return false;
13526 
13527     ImRect hit_r = out_r;
13528     if (!outer_docking)
13529     {
13530         // Custom hit testing for the 5-way selection, designed to reduce flickering when moving diagonally between sides
13531         hit_r.Expand(ImFloor(hs_w * 0.30f));
13532         ImVec2 mouse_delta = (*test_mouse_pos - c);
13533         float mouse_delta_len2 = ImLengthSqr(mouse_delta);
13534         float r_threshold_center = hs_w * 1.4f;
13535         float r_threshold_sides = hs_w * (1.4f + 1.2f);
13536         if (mouse_delta_len2 < r_threshold_center * r_threshold_center)
13537             return (dir == ImGuiDir_None);
13538         if (mouse_delta_len2 < r_threshold_sides * r_threshold_sides)
13539             return (dir == ImGetDirQuadrantFromDelta(mouse_delta.x, mouse_delta.y));
13540     }
13541     return hit_r.Contains(*test_mouse_pos);
13542 }
13543 
13544 // host_node may be NULL if the window doesn't have a DockNode already.
13545 // 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)13546 static void ImGui::DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, ImGuiDockPreviewData* data, bool is_explicit_target, bool is_outer_docking)
13547 {
13548     ImGuiContext& g = *GImGui;
13549 
13550     // There is an edge case when docking into a dockspace which only has inactive nodes.
13551     // In this case DockNodeTreeFindNodeByPos() will have selected a leaf node which is inactive.
13552     // Because the inactive leaf node doesn't have proper pos/size yet, we'll use the root node as reference.
13553     ImGuiDockNode* root_payload_as_host = root_payload->DockNodeAsHost;
13554     ImGuiDockNode* ref_node_for_rect = (host_node && !host_node->IsVisible) ? DockNodeGetRootNode(host_node) : host_node;
13555     if (ref_node_for_rect)
13556         IM_ASSERT(ref_node_for_rect->IsVisible);
13557 
13558     // Filter, figure out where we are allowed to dock
13559     ImGuiDockNodeFlags src_node_flags = root_payload_as_host ? root_payload_as_host->GetMergedFlags() : root_payload->WindowClass.DockNodeFlagsOverrideSet;
13560     ImGuiDockNodeFlags dst_node_flags = host_node ? host_node->GetMergedFlags() : host_window->WindowClass.DockNodeFlagsOverrideSet;
13561     data->IsCenterAvailable = true;
13562     if (is_outer_docking)
13563         data->IsCenterAvailable = false;
13564     else if (dst_node_flags & ImGuiDockNodeFlags_NoDocking)
13565         data->IsCenterAvailable = false;
13566     else if (host_node && (dst_node_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) && host_node->IsCentralNode())
13567         data->IsCenterAvailable = false;
13568     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?
13569         data->IsCenterAvailable = false;
13570     else if ((dst_node_flags & ImGuiDockNodeFlags_NoDockingOverMe) || (src_node_flags & ImGuiDockNodeFlags_NoDockingOverOther))
13571         data->IsCenterAvailable = false;
13572 
13573     data->IsSidesAvailable = true;
13574     if ((dst_node_flags & ImGuiDockNodeFlags_NoSplit) || g.IO.ConfigDockingNoSplit)
13575         data->IsSidesAvailable = false;
13576     else if (!is_outer_docking && host_node && host_node->ParentNode == NULL && host_node->IsCentralNode())
13577         data->IsSidesAvailable = false;
13578     else if ((dst_node_flags & ImGuiDockNodeFlags_NoDockingSplitMe) || (src_node_flags & ImGuiDockNodeFlags_NoDockingSplitOther))
13579         data->IsSidesAvailable = false;
13580 
13581     // Build a tentative future node (reuse same structure because it is practical. Shape will be readjusted when previewing a split)
13582     data->FutureNode.HasCloseButton = (host_node ? host_node->HasCloseButton : host_window->HasCloseButton) || (root_payload->HasCloseButton);
13583     data->FutureNode.HasWindowMenuButton = host_node ? true : ((host_window->Flags & ImGuiWindowFlags_NoCollapse) == 0);
13584     data->FutureNode.Pos = ref_node_for_rect ? ref_node_for_rect->Pos : host_window->Pos;
13585     data->FutureNode.Size = ref_node_for_rect ? ref_node_for_rect->Size : host_window->Size;
13586 
13587     // Calculate drop shapes geometry for allowed splitting directions
13588     IM_ASSERT(ImGuiDir_None == -1);
13589     data->SplitNode = host_node;
13590     data->SplitDir = ImGuiDir_None;
13591     data->IsSplitDirExplicit = false;
13592     if (!host_window->Collapsed)
13593         for (int dir = ImGuiDir_None; dir < ImGuiDir_COUNT; dir++)
13594         {
13595             if (dir == ImGuiDir_None && !data->IsCenterAvailable)
13596                 continue;
13597             if (dir != ImGuiDir_None && !data->IsSidesAvailable)
13598                 continue;
13599             if (DockNodeCalcDropRectsAndTestMousePos(data->FutureNode.Rect(), (ImGuiDir)dir, data->DropRectsDraw[dir+1], is_outer_docking, &g.IO.MousePos))
13600             {
13601                 data->SplitDir = (ImGuiDir)dir;
13602                 data->IsSplitDirExplicit = true;
13603             }
13604         }
13605 
13606     // When docking without holding Shift, we only allow and preview docking when hovering over a drop rect or over the title bar
13607     data->IsDropAllowed = (data->SplitDir != ImGuiDir_None) || (data->IsCenterAvailable);
13608     if (!is_explicit_target && !data->IsSplitDirExplicit && !g.IO.ConfigDockingWithShift)
13609         data->IsDropAllowed = false;
13610 
13611     // Calculate split area
13612     data->SplitRatio = 0.0f;
13613     if (data->SplitDir != ImGuiDir_None)
13614     {
13615         ImGuiDir split_dir = data->SplitDir;
13616         ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
13617         ImVec2 pos_new, pos_old = data->FutureNode.Pos;
13618         ImVec2 size_new, size_old = data->FutureNode.Size;
13619         DockNodeCalcSplitRects(pos_old, size_old, pos_new, size_new, split_dir, root_payload->Size);
13620 
13621         // Calculate split ratio so we can pass it down the docking request
13622         float split_ratio = ImSaturate(size_new[split_axis] / data->FutureNode.Size[split_axis]);
13623         data->FutureNode.Pos = pos_new;
13624         data->FutureNode.Size = size_new;
13625         data->SplitRatio = (split_dir == ImGuiDir_Right || split_dir == ImGuiDir_Down) ? (1.0f - split_ratio) : (split_ratio);
13626     }
13627 }
13628 
DockNodePreviewDockRender(ImGuiWindow * host_window,ImGuiDockNode * host_node,ImGuiWindow * root_payload,const ImGuiDockPreviewData * data)13629 static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, const ImGuiDockPreviewData* data)
13630 {
13631     ImGuiContext& g = *GImGui;
13632     IM_ASSERT(g.CurrentWindow == host_window);   // Because we rely on font size to calculate tab sizes
13633 
13634     // With this option, we only display the preview on the target viewport, and the payload viewport is made transparent.
13635     // To compensate for the single layer obstructed by the payload, we'll increase the alpha of the preview nodes.
13636     const bool is_transparent_payload = g.IO.ConfigDockingTransparentPayload;
13637 
13638     // In case the two windows involved are on different viewports, we will draw the overlay on each of them.
13639     int overlay_draw_lists_count = 0;
13640     ImDrawList* overlay_draw_lists[2];
13641     overlay_draw_lists[overlay_draw_lists_count++] = GetForegroundDrawList(host_window->Viewport);
13642     if (host_window->Viewport != root_payload->Viewport && !is_transparent_payload)
13643         overlay_draw_lists[overlay_draw_lists_count++] = GetForegroundDrawList(root_payload->Viewport);
13644 
13645     // Draw main preview rectangle
13646     const ImU32 overlay_col_tabs = GetColorU32(ImGuiCol_TabActive);
13647     const ImU32 overlay_col_main = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.60f : 0.40f);
13648     const ImU32 overlay_col_drop = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.90f : 0.70f);
13649     const ImU32 overlay_col_drop_hovered = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 1.20f : 1.00f);
13650     const ImU32 overlay_col_lines = GetColorU32(ImGuiCol_NavWindowingHighlight, is_transparent_payload ? 0.80f : 0.60f);
13651 
13652     // Display area preview
13653     const bool can_preview_tabs = (root_payload->DockNodeAsHost == NULL || root_payload->DockNodeAsHost->Windows.Size > 0);
13654     if (data->IsDropAllowed)
13655     {
13656         ImRect overlay_rect = data->FutureNode.Rect();
13657         if (data->SplitDir == ImGuiDir_None && can_preview_tabs)
13658             overlay_rect.Min.y += GetFrameHeight();
13659         if (data->SplitDir != ImGuiDir_None || data->IsCenterAvailable)
13660             for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++)
13661                 overlay_draw_lists[overlay_n]->AddRectFilled(overlay_rect.Min, overlay_rect.Max, overlay_col_main, host_window->WindowRounding);
13662     }
13663 
13664     // Display tab shape/label preview unless we are splitting node (it generally makes the situation harder to read)
13665     if (data->IsDropAllowed && can_preview_tabs && data->SplitDir == ImGuiDir_None && data->IsCenterAvailable)
13666     {
13667         // Compute target tab bar geometry so we can locate our preview tabs
13668         ImRect tab_bar_rect;
13669         DockNodeCalcTabBarLayout(&data->FutureNode, NULL, &tab_bar_rect, NULL);
13670         ImVec2 tab_pos = tab_bar_rect.Min;
13671         if (host_node && host_node->TabBar)
13672         {
13673             if (!host_node->IsHiddenTabBar() && !host_node->IsNoTabBar())
13674                 tab_pos.x += host_node->TabBar->OffsetMax + g.Style.ItemInnerSpacing.x; // We don't use OffsetNewTab because when using non-persistent-order tab bar it is incremented with each Tab submission.
13675             else
13676                 tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_node->Windows[0]->Name, host_node->Windows[0]->HasCloseButton).x;
13677         }
13678         else if (!(host_window->Flags & ImGuiWindowFlags_DockNodeHost))
13679         {
13680             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
13681         }
13682 
13683         // Draw tab shape/label preview (payload may be a loose window or a host window carrying multiple tabbed windows)
13684         if (root_payload->DockNodeAsHost)
13685             IM_ASSERT(root_payload->DockNodeAsHost->Windows.Size == root_payload->DockNodeAsHost->TabBar->Tabs.Size);
13686         const int payload_count = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->TabBar->Tabs.Size : 1;
13687         for (int payload_n = 0; payload_n < payload_count; payload_n++)
13688         {
13689             // Calculate the tab bounding box for each payload window
13690             ImGuiWindow* payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->TabBar->Tabs[payload_n].Window : root_payload;
13691             if (!DockNodeIsDropAllowedOne(payload, host_window))
13692                 continue;
13693 
13694             ImVec2 tab_size = TabItemCalcSize(payload->Name, payload->HasCloseButton);
13695             ImRect tab_bb(tab_pos.x, tab_pos.y, tab_pos.x + tab_size.x, tab_pos.y + tab_size.y);
13696             tab_pos.x += tab_size.x + g.Style.ItemInnerSpacing.x;
13697             for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++)
13698             {
13699                 ImGuiTabItemFlags tab_flags = ImGuiTabItemFlags_Preview | ((payload->Flags & ImGuiWindowFlags_UnsavedDocument) ? ImGuiTabItemFlags_UnsavedDocument : 0);
13700                 if (!tab_bar_rect.Contains(tab_bb))
13701                     overlay_draw_lists[overlay_n]->PushClipRect(tab_bar_rect.Min, tab_bar_rect.Max);
13702                 TabItemBackground(overlay_draw_lists[overlay_n], tab_bb, tab_flags, overlay_col_tabs);
13703                 TabItemLabelAndCloseButton(overlay_draw_lists[overlay_n], tab_bb, tab_flags, g.Style.FramePadding, payload->Name, 0, 0, false);
13704                 if (!tab_bar_rect.Contains(tab_bb))
13705                     overlay_draw_lists[overlay_n]->PopClipRect();
13706             }
13707         }
13708     }
13709 
13710     // Display drop boxes
13711     const float overlay_rounding = ImMax(3.0f, g.Style.FrameRounding);
13712     for (int dir = ImGuiDir_None; dir < ImGuiDir_COUNT; dir++)
13713     {
13714         if (!data->DropRectsDraw[dir + 1].IsInverted())
13715         {
13716             ImRect draw_r = data->DropRectsDraw[dir + 1];
13717             ImRect draw_r_in = draw_r;
13718             draw_r_in.Expand(-2.0f);
13719             ImU32 overlay_col = (data->SplitDir == (ImGuiDir)dir && data->IsSplitDirExplicit) ? overlay_col_drop_hovered : overlay_col_drop;
13720             for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++)
13721             {
13722                 ImVec2 center = ImFloor(draw_r_in.GetCenter());
13723                 overlay_draw_lists[overlay_n]->AddRectFilled(draw_r.Min, draw_r.Max, overlay_col, overlay_rounding);
13724                 overlay_draw_lists[overlay_n]->AddRect(draw_r_in.Min, draw_r_in.Max, overlay_col_lines, overlay_rounding);
13725                 if (dir == ImGuiDir_Left || dir == ImGuiDir_Right)
13726                     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);
13727                 if (dir == ImGuiDir_Up || dir == ImGuiDir_Down)
13728                     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);
13729             }
13730         }
13731 
13732         // Stop after ImGuiDir_None
13733         if ((host_node && (host_node->GetMergedFlags() & ImGuiDockNodeFlags_NoSplit)) || g.IO.ConfigDockingNoSplit)
13734             return;
13735     }
13736 }
13737 
13738 //-----------------------------------------------------------------------------
13739 // Docking: ImGuiDockNode Tree manipulation functions
13740 //-----------------------------------------------------------------------------
13741 // - DockNodeTreeSplit()
13742 // - DockNodeTreeMerge()
13743 // - DockNodeTreeUpdatePosSize()
13744 // - DockNodeTreeUpdateSplitterFindTouchingNode()
13745 // - DockNodeTreeUpdateSplitter()
13746 // - DockNodeTreeFindFallbackLeafNode()
13747 // - DockNodeTreeFindNodeByPos()
13748 //-----------------------------------------------------------------------------
13749 
DockNodeTreeSplit(ImGuiContext * ctx,ImGuiDockNode * parent_node,ImGuiAxis split_axis,int split_inheritor_child_idx,float split_ratio,ImGuiDockNode * new_node)13750 void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_inheritor_child_idx, float split_ratio, ImGuiDockNode* new_node)
13751 {
13752     ImGuiContext& g = *GImGui;
13753     IM_ASSERT(split_axis != ImGuiAxis_None);
13754 
13755     ImGuiDockNode* child_0 = (new_node && split_inheritor_child_idx != 0) ? new_node : DockContextAddNode(ctx, 0);
13756     child_0->ParentNode = parent_node;
13757 
13758     ImGuiDockNode* child_1 = (new_node && split_inheritor_child_idx != 1) ? new_node : DockContextAddNode(ctx, 0);
13759     child_1->ParentNode = parent_node;
13760 
13761     ImGuiDockNode* child_inheritor = (split_inheritor_child_idx == 0) ? child_0 : child_1;
13762     DockNodeMoveChildNodes(child_inheritor, parent_node);
13763     parent_node->ChildNodes[0] = child_0;
13764     parent_node->ChildNodes[1] = child_1;
13765     parent_node->ChildNodes[split_inheritor_child_idx]->VisibleWindow = parent_node->VisibleWindow;
13766     parent_node->SplitAxis = split_axis;
13767     parent_node->VisibleWindow = NULL;
13768     parent_node->AuthorityForPos = parent_node->AuthorityForSize = ImGuiDataAuthority_DockNode;
13769 
13770     float size_avail = (parent_node->Size[split_axis] - DOCKING_SPLITTER_SIZE);
13771     size_avail = ImMax(size_avail, g.Style.WindowMinSize[split_axis] * 2.0f);
13772     IM_ASSERT(size_avail > 0.0f); // If you created a node manually with DockBuilderAddNode(), you need to also call DockBuilderSetNodeSize() before splitting.
13773     child_0->SizeRef = child_1->SizeRef = parent_node->Size;
13774     child_0->SizeRef[split_axis] = ImFloor(size_avail * split_ratio);
13775     child_1->SizeRef[split_axis] = ImFloor(size_avail - child_0->SizeRef[split_axis]);
13776 
13777     DockNodeMoveWindows(parent_node->ChildNodes[split_inheritor_child_idx], parent_node);
13778     DockNodeTreeUpdatePosSize(parent_node, parent_node->Pos, parent_node->Size);
13779 
13780     // Flags transfer (e.g. this is where we transfer the ImGuiDockNodeFlags_CentralNode property)
13781     child_0->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_;
13782     child_1->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_;
13783     child_inheritor->LocalFlags = parent_node->LocalFlags & ImGuiDockNodeFlags_LocalFlagsTransferMask_;
13784     parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_;
13785     if (child_inheritor->IsCentralNode())
13786         DockNodeGetRootNode(parent_node)->CentralNode = child_inheritor;
13787 }
13788 
DockNodeTreeMerge(ImGuiContext * ctx,ImGuiDockNode * parent_node,ImGuiDockNode * merge_lead_child)13789 void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child)
13790 {
13791     // When called from DockContextProcessUndockNode() it is possible that one of the child is NULL.
13792     ImGuiDockNode* child_0 = parent_node->ChildNodes[0];
13793     ImGuiDockNode* child_1 = parent_node->ChildNodes[1];
13794     IM_ASSERT(child_0 || child_1);
13795     IM_ASSERT(merge_lead_child == child_0 || merge_lead_child == child_1);
13796     if ((child_0 && child_0->Windows.Size > 0) || (child_1 && child_1->Windows.Size > 0))
13797     {
13798         IM_ASSERT(parent_node->TabBar == NULL);
13799         IM_ASSERT(parent_node->Windows.Size == 0);
13800     }
13801     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);
13802 
13803     ImVec2 backup_last_explicit_size = parent_node->SizeRef;
13804     DockNodeMoveChildNodes(parent_node, merge_lead_child);
13805     if (child_0)
13806     {
13807         DockNodeMoveWindows(parent_node, child_0); // Generally only 1 of the 2 child node will have windows
13808         DockSettingsRenameNodeReferences(child_0->ID, parent_node->ID);
13809     }
13810     if (child_1)
13811     {
13812         DockNodeMoveWindows(parent_node, child_1);
13813         DockSettingsRenameNodeReferences(child_1->ID, parent_node->ID);
13814     }
13815     DockNodeApplyPosSizeToWindows(parent_node);
13816     parent_node->AuthorityForPos = parent_node->AuthorityForSize = parent_node->AuthorityForViewport = ImGuiDataAuthority_Auto;
13817     parent_node->VisibleWindow = merge_lead_child->VisibleWindow;
13818     parent_node->SizeRef = backup_last_explicit_size;
13819 
13820     // Flags transfer
13821     parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_; // Preserve Dockspace flag
13822     parent_node->LocalFlags |= (child_0 ? child_0->LocalFlags : 0) & ImGuiDockNodeFlags_LocalFlagsTransferMask_;
13823     parent_node->LocalFlags |= (child_1 ? child_1->LocalFlags : 0) & ImGuiDockNodeFlags_LocalFlagsTransferMask_;
13824 
13825     if (child_0)
13826     {
13827         ctx->DockContext.Nodes.SetVoidPtr(child_0->ID, NULL);
13828         IM_DELETE(child_0);
13829     }
13830     if (child_1)
13831     {
13832         ctx->DockContext.Nodes.SetVoidPtr(child_1->ID, NULL);
13833         IM_DELETE(child_1);
13834     }
13835 }
13836 
13837 // 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)13838 void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, bool only_write_to_marked_nodes)
13839 {
13840     // During the regular dock node update we write to all nodes.
13841     // 'only_write_to_marked_nodes' is only set when turning a node visible mid-frame and we need its size right-away.
13842     const bool write_to_node = (only_write_to_marked_nodes == false) || (node->MarkedForPosSizeWrite);
13843     if (write_to_node)
13844     {
13845         node->Pos = pos;
13846         node->Size = size;
13847     }
13848 
13849     if (node->IsLeafNode())
13850         return;
13851 
13852     ImGuiDockNode* child_0 = node->ChildNodes[0];
13853     ImGuiDockNode* child_1 = node->ChildNodes[1];
13854     ImVec2 child_0_pos = pos, child_1_pos = pos;
13855     ImVec2 child_0_size = size, child_1_size = size;
13856     if (child_0->IsVisible && child_1->IsVisible)
13857     {
13858         const float spacing = DOCKING_SPLITTER_SIZE;
13859         const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis;
13860         const float size_avail = ImMax(size[axis] - spacing, 0.0f);
13861 
13862         // Size allocation policy
13863         // 1) The first 0..WindowMinSize[axis]*2 are allocated evenly to both windows.
13864         ImGuiContext& g = *GImGui;
13865         const float size_min_each = ImFloor(ImMin(size_avail, g.Style.WindowMinSize[axis] * 2.0f) * 0.5f);
13866 
13867         // 2) Process locked absolute size (during a splitter resize we preserve the child of nodes not touching the splitter edge)
13868         if (child_0->WantLockSizeOnce && !child_1->WantLockSizeOnce)
13869         {
13870             child_0_size[axis] = child_0->SizeRef[axis] = ImMin(size_avail - 1.0f, child_0->Size[axis]);
13871             child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]);
13872             IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f);
13873         }
13874         else if (child_1->WantLockSizeOnce && !child_0->WantLockSizeOnce)
13875         {
13876             child_1_size[axis] = child_1->SizeRef[axis] = ImMin(size_avail - 1.0f, child_1->Size[axis]);
13877             child_0_size[axis] = child_0->SizeRef[axis] = (size_avail - child_1_size[axis]);
13878             IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f);
13879         }
13880         else if (child_0->WantLockSizeOnce && child_1->WantLockSizeOnce)
13881         {
13882             // FIXME-DOCK: We cannot honor the requested size, so apply ratio.
13883             // Currently this path will only be taken if code programmatically sets WantLockSizeOnce
13884             float ratio_0 = child_0_size[axis] / (child_0_size[axis] + child_1_size[axis]);
13885             child_0_size[axis] = child_0->SizeRef[axis] = ImFloor(size_avail * ratio_0);
13886             child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]);
13887             IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f);
13888         }
13889 
13890         // 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
13891         else if (child_1->IsCentralNode() && child_0->SizeRef[axis] != 0.0f)
13892         {
13893             child_0_size[axis] = ImMin(size_avail - size_min_each, child_0->SizeRef[axis]);
13894             child_1_size[axis] = (size_avail - child_0_size[axis]);
13895         }
13896         else if (child_0->IsCentralNode() && child_1->SizeRef[axis] != 0.0f)
13897         {
13898             child_1_size[axis] = ImMin(size_avail - size_min_each, child_1->SizeRef[axis]);
13899             child_0_size[axis] = (size_avail - child_1_size[axis]);
13900         }
13901         else
13902         {
13903             // 4) Otherwise distribute according to the relative ratio of each SizeRef value
13904             float split_ratio = child_0->SizeRef[axis] / (child_0->SizeRef[axis] + child_1->SizeRef[axis]);
13905             child_0_size[axis] = ImMax(size_min_each, ImFloor(size_avail * split_ratio + 0.5F));
13906             child_1_size[axis] = (size_avail - child_0_size[axis]);
13907         }
13908 
13909         child_1_pos[axis] += spacing + child_0_size[axis];
13910     }
13911     child_0->WantLockSizeOnce = child_1->WantLockSizeOnce = false;
13912 
13913     if (child_0->IsVisible)
13914         DockNodeTreeUpdatePosSize(child_0, child_0_pos, child_0_size);
13915     if (child_1->IsVisible)
13916         DockNodeTreeUpdatePosSize(child_1, child_1_pos, child_1_size);
13917 }
13918 
DockNodeTreeUpdateSplitterFindTouchingNode(ImGuiDockNode * node,ImGuiAxis axis,int side,ImVector<ImGuiDockNode * > * touching_nodes)13919 static void DockNodeTreeUpdateSplitterFindTouchingNode(ImGuiDockNode* node, ImGuiAxis axis, int side, ImVector<ImGuiDockNode*>* touching_nodes)
13920 {
13921     if (node->IsLeafNode())
13922     {
13923         touching_nodes->push_back(node);
13924         return;
13925     }
13926     if (node->ChildNodes[0]->IsVisible)
13927         if (node->SplitAxis != axis || side == 0 || !node->ChildNodes[1]->IsVisible)
13928             DockNodeTreeUpdateSplitterFindTouchingNode(node->ChildNodes[0], axis, side, touching_nodes);
13929     if (node->ChildNodes[1]->IsVisible)
13930         if (node->SplitAxis != axis || side == 1 || !node->ChildNodes[0]->IsVisible)
13931             DockNodeTreeUpdateSplitterFindTouchingNode(node->ChildNodes[1], axis, side, touching_nodes);
13932 }
13933 
DockNodeTreeUpdateSplitter(ImGuiDockNode * node)13934 void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node)
13935 {
13936     if (node->IsLeafNode())
13937         return;
13938 
13939     ImGuiContext& g = *GImGui;
13940 
13941     ImGuiDockNode* child_0 = node->ChildNodes[0];
13942     ImGuiDockNode* child_1 = node->ChildNodes[1];
13943     if (child_0->IsVisible && child_1->IsVisible)
13944     {
13945         // Bounding box of the splitter cover the space between both nodes (w = Spacing, h = Size[xy^1] for when splitting horizontally)
13946         const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis;
13947         IM_ASSERT(axis != ImGuiAxis_None);
13948         ImRect bb;
13949         bb.Min = child_0->Pos;
13950         bb.Max = child_1->Pos;
13951         bb.Min[axis] += child_0->Size[axis];
13952         bb.Max[axis ^ 1] += child_1->Size[axis ^ 1];
13953         //if (g.IO.KeyCtrl) GetForegroundDrawList(g.CurrentWindow->Viewport)->AddRect(bb.Min, bb.Max, IM_COL32(255,0,255,255));
13954 
13955         const ImGuiDockNodeFlags merged_flags = child_0->GetMergedFlags() | child_1->GetMergedFlags();
13956         const ImGuiDockNodeFlags no_resize_axis_flag = (axis == ImGuiAxis_X) ? ImGuiDockNodeFlags_NoResizeX : ImGuiDockNodeFlags_NoResizeY;
13957         if ((merged_flags & ImGuiDockNodeFlags_NoResize) || (merged_flags & no_resize_axis_flag))
13958         {
13959             ImGuiWindow* window = g.CurrentWindow;
13960             window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator), g.Style.FrameRounding);
13961         }
13962         else
13963         {
13964             //bb.Min[axis] += 1; // Display a little inward so highlight doesn't connect with nearby tabs on the neighbor node.
13965             //bb.Max[axis] -= 1;
13966             PushID(node->ID);
13967 
13968             // Gather list of nodes that are touching the splitter line. Find resizing limits based on those nodes.
13969             ImVector<ImGuiDockNode*> touching_nodes[2];
13970             float min_size = g.Style.WindowMinSize[axis];
13971             float resize_limits[2];
13972             resize_limits[0] = node->ChildNodes[0]->Pos[axis] + min_size;
13973             resize_limits[1] = node->ChildNodes[1]->Pos[axis] + node->ChildNodes[1]->Size[axis] - min_size;
13974 
13975             ImGuiID splitter_id = GetID("##Splitter");
13976             if (g.ActiveId == splitter_id)
13977             {
13978                 // Only process when splitter is active
13979                 DockNodeTreeUpdateSplitterFindTouchingNode(child_0, axis, 1, &touching_nodes[0]);
13980                 DockNodeTreeUpdateSplitterFindTouchingNode(child_1, axis, 0, &touching_nodes[1]);
13981                 for (int touching_node_n = 0; touching_node_n < touching_nodes[0].Size; touching_node_n++)
13982                     resize_limits[0] = ImMax(resize_limits[0], touching_nodes[0][touching_node_n]->Rect().Min[axis] + min_size);
13983                 for (int touching_node_n = 0; touching_node_n < touching_nodes[1].Size; touching_node_n++)
13984                     resize_limits[1] = ImMin(resize_limits[1], touching_nodes[1][touching_node_n]->Rect().Max[axis] - min_size);
13985 
13986                 /*
13987                 // [DEBUG] Render limits
13988                 ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList((ImGuiViewportP*)GetMainViewport());
13989                 for (int n = 0; n < 2; n++)
13990                     if (axis == ImGuiAxis_X)
13991                         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);
13992                     else
13993                         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);
13994                 */
13995             }
13996 
13997             // Use a short delay before highlighting the splitter (and changing the mouse cursor) in order for regular mouse movement to not highlight many splitters
13998             float cur_size_0 = child_0->Size[axis];
13999             float cur_size_1 = child_1->Size[axis];
14000             float min_size_0 = resize_limits[0] - child_0->Pos[axis];
14001             float min_size_1 = child_1->Pos[axis] + child_1->Size[axis] - resize_limits[1];
14002             if (SplitterBehavior(bb, GetID("##Splitter"), axis, &cur_size_0, &cur_size_1, min_size_0, min_size_1, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS, WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER))
14003             {
14004                 if (touching_nodes[0].Size > 0 && touching_nodes[1].Size > 0)
14005                 {
14006                     child_0->Size[axis] = child_0->SizeRef[axis] = cur_size_0;
14007                     child_1->Pos[axis] -= cur_size_1 - child_1->Size[axis];
14008                     child_1->Size[axis] = child_1->SizeRef[axis] = cur_size_1;
14009 
14010                     // Lock the size of every node that is a sibling of the node we are touching
14011                     // This might be less desirable if we can merge sibling of a same axis into the same parental level.
14012                     for (int side_n = 0; side_n < 2; side_n++)
14013                         for (int touching_node_n = 0; touching_node_n < touching_nodes[side_n].Size; touching_node_n++)
14014                         {
14015                             ImGuiDockNode* touching_node = touching_nodes[side_n][touching_node_n];
14016                             //ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList((ImGuiViewportP*)GetMainViewport());
14017                             //draw_list->AddRect(touching_node->Pos, touching_node->Pos + touching_node->Size, IM_COL32(255, 128, 0, 255));
14018                             while (touching_node->ParentNode != node)
14019                             {
14020                                 if (touching_node->ParentNode->SplitAxis == axis)
14021                                 {
14022                                     // Mark other node so its size will be preserved during the upcoming call to DockNodeTreeUpdatePosSize().
14023                                     ImGuiDockNode* node_to_preserve = touching_node->ParentNode->ChildNodes[side_n];
14024                                     node_to_preserve->WantLockSizeOnce = true;
14025                                     //draw_list->AddRect(touching_node->Pos, touching_node->Rect().Max, IM_COL32(255, 0, 0, 255));
14026                                     //draw_list->AddRectFilled(node_to_preserve->Pos, node_to_preserve->Rect().Max, IM_COL32(0, 255, 0, 100));
14027                                 }
14028                                 touching_node = touching_node->ParentNode;
14029                             }
14030                         }
14031 
14032                     DockNodeTreeUpdatePosSize(child_0, child_0->Pos, child_0->Size);
14033                     DockNodeTreeUpdatePosSize(child_1, child_1->Pos, child_1->Size);
14034                     MarkIniSettingsDirty();
14035                 }
14036             }
14037             PopID();
14038         }
14039     }
14040 
14041     if (child_0->IsVisible)
14042         DockNodeTreeUpdateSplitter(child_0);
14043     if (child_1->IsVisible)
14044         DockNodeTreeUpdateSplitter(child_1);
14045 }
14046 
DockNodeTreeFindFallbackLeafNode(ImGuiDockNode * node)14047 ImGuiDockNode* ImGui::DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node)
14048 {
14049     if (node->IsLeafNode())
14050         return node;
14051     if (ImGuiDockNode* leaf_node = DockNodeTreeFindFallbackLeafNode(node->ChildNodes[0]))
14052         return leaf_node;
14053     if (ImGuiDockNode* leaf_node = DockNodeTreeFindFallbackLeafNode(node->ChildNodes[1]))
14054         return leaf_node;
14055     return NULL;
14056 }
14057 
DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode * node,ImVec2 pos)14058 ImGuiDockNode* ImGui::DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos)
14059 {
14060     if (!node->IsVisible)
14061         return NULL;
14062 
14063     const float dock_spacing = 0.0f;// g.Style.ItemInnerSpacing.x; // FIXME: Relation to DOCKING_SPLITTER_SIZE?
14064     ImRect r(node->Pos, node->Pos + node->Size);
14065     r.Expand(dock_spacing * 0.5f);
14066     bool inside = r.Contains(pos);
14067     if (!inside)
14068         return NULL;
14069 
14070     if (node->IsLeafNode())
14071         return node;
14072     if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(node->ChildNodes[0], pos))
14073         return hovered_node;
14074     if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(node->ChildNodes[1], pos))
14075         return hovered_node;
14076 
14077     return NULL;
14078 }
14079 
14080 //-----------------------------------------------------------------------------
14081 // Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport)
14082 //-----------------------------------------------------------------------------
14083 // - SetWindowDock() [Internal]
14084 // - DockSpace()
14085 // - DockSpaceOverViewport()
14086 //-----------------------------------------------------------------------------
14087 
14088 // [Internal] Called via SetNextWindowDockID()
SetWindowDock(ImGuiWindow * window,ImGuiID dock_id,ImGuiCond cond)14089 void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond)
14090 {
14091     // Test condition (NB: bit 0 is always true) and clear flags for next time
14092     if (cond && (window->SetWindowDockAllowFlags & cond) == 0)
14093         return;
14094     window->SetWindowDockAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
14095 
14096     if (window->DockId == dock_id)
14097         return;
14098 
14099     // If the user attempt to set a dock id that is a split node, we'll dig within to find a suitable docking spot
14100     ImGuiContext* ctx = GImGui;
14101     if (ImGuiDockNode* new_node = DockContextFindNodeByID(ctx, dock_id))
14102         if (new_node->IsSplitNode())
14103         {
14104             // Policy: Find central node or latest focused node. We first move back to our root node.
14105             new_node = DockNodeGetRootNode(new_node);
14106             if (new_node->CentralNode)
14107             {
14108                 IM_ASSERT(new_node->CentralNode->IsCentralNode());
14109                 dock_id = new_node->CentralNode->ID;
14110             }
14111             else
14112             {
14113                 dock_id = new_node->LastFocusedNodeId;
14114             }
14115         }
14116 
14117     if (window->DockId == dock_id)
14118         return;
14119 
14120     if (window->DockNode)
14121         DockNodeRemoveWindow(window->DockNode, window, 0);
14122     window->DockId = dock_id;
14123 }
14124 
14125 // Create an explicit dockspace node within an existing window. Also expose dock node flags and creates a CentralNode by default.
14126 // The Central Node is always displayed even when empty and shrink/extend according to the requested size of its neighbors.
14127 // 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)14128 void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags flags, const ImGuiWindowClass* window_class)
14129 {
14130     ImGuiContext* ctx = GImGui;
14131     ImGuiContext& g = *ctx;
14132     ImGuiWindow* window = GetCurrentWindow();
14133     if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
14134         return;
14135 
14136     // Early out if parent window is hidden/collapsed
14137     // This is faster but also DockNodeUpdateTabBar() relies on TabBarLayout() running (which won't if SkipItems=true) to set NextSelectedTabId = 0). See #2960.
14138     // If for whichever reason this is causing problem we would need to ensure that DockNodeUpdateTabBar() ends up clearing NextSelectedTabId even if SkipItems=true.
14139     if (window->SkipItems)
14140         flags |= ImGuiDockNodeFlags_KeepAliveOnly;
14141 
14142     IM_ASSERT((flags & ImGuiDockNodeFlags_DockSpace) == 0);
14143     ImGuiDockNode* node = DockContextFindNodeByID(ctx, id);
14144     if (!node)
14145     {
14146         IMGUI_DEBUG_LOG_DOCKING("DockSpace: dockspace node 0x%08X created\n", id);
14147         node = DockContextAddNode(ctx, id);
14148         node->LocalFlags |= ImGuiDockNodeFlags_CentralNode;
14149     }
14150     if (window_class && window_class->ClassId != node->WindowClass.ClassId)
14151         IMGUI_DEBUG_LOG_DOCKING("DockSpace: dockspace node 0x%08X: setup WindowClass 0x%08X -> 0x%08X\n", id, node->WindowClass.ClassId, window_class->ClassId);
14152     node->SharedFlags = flags;
14153     node->WindowClass = window_class ? *window_class : ImGuiWindowClass();
14154 
14155     // When a DockSpace transitioned form implicit to explicit this may be called a second time
14156     // 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.
14157     if (node->LastFrameActive == g.FrameCount && !(flags & ImGuiDockNodeFlags_KeepAliveOnly))
14158     {
14159         IM_ASSERT(node->IsDockSpace() == false && "Cannot call DockSpace() twice a frame with the same ID");
14160         node->LocalFlags |= ImGuiDockNodeFlags_DockSpace;
14161         return;
14162     }
14163     node->LocalFlags |= ImGuiDockNodeFlags_DockSpace;
14164 
14165     // Keep alive mode, this is allow windows docked into this node so stay docked even if they are not visible
14166     if (flags & ImGuiDockNodeFlags_KeepAliveOnly)
14167     {
14168         node->LastFrameAlive = g.FrameCount;
14169         return;
14170     }
14171 
14172     const ImVec2 content_avail = GetContentRegionAvail();
14173     ImVec2 size = ImFloor(size_arg);
14174     if (size.x <= 0.0f)
14175         size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
14176     if (size.y <= 0.0f)
14177         size.y = ImMax(content_avail.y + size.y, 4.0f);
14178     IM_ASSERT(size.x > 0.0f && size.y > 0.0f);
14179 
14180     node->Pos = window->DC.CursorPos;
14181     node->Size = node->SizeRef = size;
14182     SetNextWindowPos(node->Pos);
14183     SetNextWindowSize(node->Size);
14184     g.NextWindowData.PosUndock = false;
14185 
14186     // FIXME-DOCK Why do we need a child window to host a dockspace, could we host it in the existing window?
14187     ImGuiWindowFlags window_flags = ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_DockNodeHost;
14188     window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar;
14189     window_flags |= ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;
14190 
14191     char title[256];
14192     ImFormatString(title, IM_ARRAYSIZE(title), "%s/DockSpace_%08X", window->Name, id);
14193 
14194     // FIXME-DOCK: What is the reason for not simply calling BeginChild()?
14195     if (node->Windows.Size > 0 || node->IsSplitNode())
14196         PushStyleColor(ImGuiCol_ChildBg, IM_COL32(0, 0, 0, 0));
14197     PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0.0f);
14198     Begin(title, NULL, window_flags);
14199     PopStyleVar();
14200     if (node->Windows.Size > 0 || node->IsSplitNode())
14201         PopStyleColor();
14202 
14203     ImGuiWindow* host_window = g.CurrentWindow;
14204     host_window->DockNodeAsHost = node;
14205     host_window->ChildId = window->GetID(title);
14206     node->HostWindow = host_window;
14207     node->OnlyNodeWithWindows = NULL;
14208 
14209     IM_ASSERT(node->IsRootNode());
14210 
14211     // We need to handle the rare case were a central node is missing.
14212     // This can happen if the node was first created manually with DockBuilderAddNode() but _without_ the ImGuiDockNodeFlags_Dockspace.
14213     // Doing it correctly would set the _CentralNode flags, which would then propagate according to subsequent split.
14214     // 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.
14215     // The specific sub-property of _CentralNode we are interested in recovering here is the "Don't delete when empty" property,
14216     // as it doesn't make sense for an empty dockspace to not have this property.
14217     if (node->IsLeafNode() && !node->IsCentralNode())
14218         node->LocalFlags |= ImGuiDockNodeFlags_CentralNode;
14219 
14220     // Update the node
14221     DockNodeUpdate(node);
14222 
14223     g.WithinEndChild = true;
14224     End();
14225     ItemSize(size);
14226     g.WithinEndChild = false;
14227 }
14228 
14229 // Tips: Use with ImGuiDockNodeFlags_PassthruCentralNode!
14230 // The limitation with this call is that your window won't have a menu bar.
14231 // 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.
14232 // But you can also use BeginMainMenuBar(). If you really want a menu bar inside the same window as the one hosting the dockspace, you will need to copy this code somewhere and tweak it.
DockSpaceOverViewport(ImGuiViewport * viewport,ImGuiDockNodeFlags dockspace_flags,const ImGuiWindowClass * window_class)14233 ImGuiID ImGui::DockSpaceOverViewport(ImGuiViewport* viewport, ImGuiDockNodeFlags dockspace_flags, const ImGuiWindowClass* window_class)
14234 {
14235     if (viewport == NULL)
14236         viewport = GetMainViewport();
14237 
14238     SetNextWindowPos(viewport->GetWorkPos());
14239     SetNextWindowSize(viewport->GetWorkSize());
14240     SetNextWindowViewport(viewport->ID);
14241 
14242     ImGuiWindowFlags host_window_flags = 0;
14243     host_window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking;
14244     host_window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
14245     if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode)
14246         host_window_flags |= ImGuiWindowFlags_NoBackground;
14247 
14248     char label[32];
14249     ImFormatString(label, IM_ARRAYSIZE(label), "DockSpaceViewport_%08X", viewport->ID);
14250 
14251     PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
14252     PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
14253     PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
14254     Begin(label, NULL, host_window_flags);
14255     PopStyleVar(3);
14256 
14257     ImGuiID dockspace_id = GetID("DockSpace");
14258     DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags, window_class);
14259     End();
14260 
14261     return dockspace_id;
14262 }
14263 
14264 //-----------------------------------------------------------------------------
14265 // Docking: Builder Functions
14266 //-----------------------------------------------------------------------------
14267 // Very early end-user API to manipulate dock nodes.
14268 // Only available in imgui_internal.h. Expect this API to change/break!
14269 // It is expected that those functions are all called _before_ the dockspace node submission.
14270 //-----------------------------------------------------------------------------
14271 // - DockBuilderDockWindow()
14272 // - DockBuilderGetNode()
14273 // - DockBuilderSetNodePos()
14274 // - DockBuilderSetNodeSize()
14275 // - DockBuilderAddNode()
14276 // - DockBuilderRemoveNode()
14277 // - DockBuilderRemoveNodeChildNodes()
14278 // - DockBuilderRemoveNodeDockedWindows()
14279 // - DockBuilderSplitNode()
14280 // - DockBuilderCopyNodeRec()
14281 // - DockBuilderCopyNode()
14282 // - DockBuilderCopyWindowSettings()
14283 // - DockBuilderCopyDockSpace()
14284 // - DockBuilderFinish()
14285 //-----------------------------------------------------------------------------
14286 
DockBuilderDockWindow(const char * window_name,ImGuiID node_id)14287 void ImGui::DockBuilderDockWindow(const char* window_name, ImGuiID node_id)
14288 {
14289     // We don't preserve relative order of multiple docked windows (by clearing DockOrder back to -1)
14290     ImGuiID window_id = ImHashStr(window_name);
14291     if (ImGuiWindow* window = FindWindowByID(window_id))
14292     {
14293         // Apply to created window
14294         SetWindowDock(window, node_id, ImGuiCond_Always);
14295         window->DockOrder = -1;
14296     }
14297     else
14298     {
14299         // Apply to settings
14300         ImGuiWindowSettings* settings = FindWindowSettings(window_id);
14301         if (settings == NULL)
14302             settings = CreateNewWindowSettings(window_name);
14303         settings->DockId = node_id;
14304         settings->DockOrder = -1;
14305     }
14306 }
14307 
DockBuilderGetNode(ImGuiID node_id)14308 ImGuiDockNode* ImGui::DockBuilderGetNode(ImGuiID node_id)
14309 {
14310     ImGuiContext* ctx = GImGui;
14311     return DockContextFindNodeByID(ctx, node_id);
14312 }
14313 
DockBuilderSetNodePos(ImGuiID node_id,ImVec2 pos)14314 void ImGui::DockBuilderSetNodePos(ImGuiID node_id, ImVec2 pos)
14315 {
14316     ImGuiContext* ctx = GImGui;
14317     ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_id);
14318     if (node == NULL)
14319         return;
14320     node->Pos = pos;
14321     node->AuthorityForPos = ImGuiDataAuthority_DockNode;
14322 }
14323 
DockBuilderSetNodeSize(ImGuiID node_id,ImVec2 size)14324 void ImGui::DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size)
14325 {
14326     ImGuiContext* ctx = GImGui;
14327     ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_id);
14328     if (node == NULL)
14329         return;
14330     IM_ASSERT(size.x > 0.0f && size.y > 0.0f);
14331     node->Size = node->SizeRef = size;
14332     node->AuthorityForSize = ImGuiDataAuthority_DockNode;
14333 }
14334 
14335 // Make sure to use the ImGuiDockNodeFlags_DockSpace flag to create a dockspace node! Otherwise this will create a floating node!
14336 // - Floating node: you can then call DockBuilderSetNodePos()/DockBuilderSetNodeSize() to position and size the floating node.
14337 // - Dockspace node: calling DockBuilderSetNodePos() is unnecessary.
14338 // - If you intend to split a node immediately after creation using DockBuilderSplitNode(), make sure to call DockBuilderSetNodeSize() beforehand!
14339 //   For various reason, the splitting code currently needs a base size otherwise space may not be allocated as precisely as you would expect.
14340 // - Use (id == 0) to let the system allocate a node identifier.
14341 // - Existing node with a same id will be removed.
DockBuilderAddNode(ImGuiID id,ImGuiDockNodeFlags flags)14342 ImGuiID ImGui::DockBuilderAddNode(ImGuiID id, ImGuiDockNodeFlags flags)
14343 {
14344     ImGuiContext* ctx = GImGui;
14345     ImGuiDockNode* node = NULL;
14346 
14347     if (id != 0)
14348         DockBuilderRemoveNode(id);
14349 
14350     if (flags & ImGuiDockNodeFlags_DockSpace)
14351     {
14352         DockSpace(id, ImVec2(0, 0), (flags & ~ImGuiDockNodeFlags_DockSpace) | ImGuiDockNodeFlags_KeepAliveOnly);
14353         node = DockContextFindNodeByID(ctx, id);
14354     }
14355     else
14356     {
14357         node = DockContextAddNode(ctx, id);
14358         node->LocalFlags = flags;
14359     }
14360     node->LastFrameAlive = ctx->FrameCount;   // Set this otherwise BeginDocked will undock during the same frame.
14361     return node->ID;
14362 }
14363 
DockBuilderRemoveNode(ImGuiID node_id)14364 void ImGui::DockBuilderRemoveNode(ImGuiID node_id)
14365 {
14366     ImGuiContext* ctx = GImGui;
14367     ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_id);
14368     if (node == NULL)
14369         return;
14370     DockBuilderRemoveNodeDockedWindows(node_id, true);
14371     DockBuilderRemoveNodeChildNodes(node_id);
14372     if (node->IsCentralNode() && node->ParentNode)
14373         node->ParentNode->LocalFlags |= ImGuiDockNodeFlags_CentralNode;
14374     DockContextRemoveNode(ctx, node, true);
14375 }
14376 
14377 // root_id = 0 to remove all, root_id != 0 to remove child of given node.
DockBuilderRemoveNodeChildNodes(ImGuiID root_id)14378 void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id)
14379 {
14380     ImGuiContext* ctx = GImGui;
14381     ImGuiDockContext* dc  = &ctx->DockContext;
14382 
14383     ImGuiDockNode* root_node = root_id ? DockContextFindNodeByID(ctx, root_id) : NULL;
14384     if (root_id && root_node == NULL)
14385         return;
14386     bool has_central_node = false;
14387 
14388     ImGuiDataAuthority backup_root_node_authority_for_pos = root_node ? root_node->AuthorityForPos : ImGuiDataAuthority_Auto;
14389     ImGuiDataAuthority backup_root_node_authority_for_size = root_node ? root_node->AuthorityForSize : ImGuiDataAuthority_Auto;
14390 
14391     // Process active windows
14392     ImVector<ImGuiDockNode*> nodes_to_remove;
14393     for (int n = 0; n < dc->Nodes.Data.Size; n++)
14394         if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
14395         {
14396             bool want_removal = (root_id == 0) || (node->ID != root_id && DockNodeGetRootNode(node)->ID == root_id);
14397             if (want_removal)
14398             {
14399                 if (node->IsCentralNode())
14400                     has_central_node = true;
14401                 if (root_id != 0)
14402                     DockContextQueueNotifyRemovedNode(ctx, node);
14403                 if (root_node)
14404                     DockNodeMoveWindows(root_node, node);
14405                 nodes_to_remove.push_back(node);
14406             }
14407         }
14408 
14409     // DockNodeMoveWindows->DockNodeAddWindow will normally set those when reaching two windows (which is only adequate during interactive merge)
14410     // Make sure we don't lose our current pos/size. (FIXME-DOCK: Consider tidying up that code in DockNodeAddWindow instead)
14411     if (root_node)
14412     {
14413         root_node->AuthorityForPos = backup_root_node_authority_for_pos;
14414         root_node->AuthorityForSize = backup_root_node_authority_for_size;
14415     }
14416 
14417     // Apply to settings
14418     for (ImGuiWindowSettings* settings = ctx->SettingsWindows.begin(); settings != NULL; settings = ctx->SettingsWindows.next_chunk(settings))
14419         if (ImGuiID window_settings_dock_id = settings->DockId)
14420             for (int n = 0; n < nodes_to_remove.Size; n++)
14421                 if (nodes_to_remove[n]->ID == window_settings_dock_id)
14422                 {
14423                     settings->DockId = root_id;
14424                     break;
14425                 }
14426 
14427     // Not really efficient, but easier to destroy a whole hierarchy considering DockContextRemoveNode is attempting to merge nodes
14428     if (nodes_to_remove.Size > 1)
14429         ImQsort(nodes_to_remove.Data, nodes_to_remove.Size, sizeof(ImGuiDockNode*), DockNodeComparerDepthMostFirst);
14430     for (int n = 0; n < nodes_to_remove.Size; n++)
14431         DockContextRemoveNode(ctx, nodes_to_remove[n], false);
14432 
14433     if (root_id == 0)
14434     {
14435         dc->Nodes.Clear();
14436         dc->Requests.clear();
14437     }
14438     else if (has_central_node)
14439     {
14440         root_node->LocalFlags |= ImGuiDockNodeFlags_CentralNode;
14441         root_node->CentralNode = root_node;
14442     }
14443 }
14444 
DockBuilderRemoveNodeDockedWindows(ImGuiID root_id,bool clear_settings_refs)14445 void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiID root_id, bool clear_settings_refs)
14446 {
14447     // Clear references in settings
14448     ImGuiContext* ctx = GImGui;
14449     ImGuiContext& g = *ctx;
14450     if (clear_settings_refs)
14451     {
14452         for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
14453         {
14454             bool want_removal = (root_id == 0) || (settings->DockId == root_id);
14455             if (!want_removal && settings->DockId != 0)
14456                 if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, settings->DockId))
14457                     if (DockNodeGetRootNode(node)->ID == root_id)
14458                         want_removal = true;
14459             if (want_removal)
14460                 settings->DockId = 0;
14461         }
14462     }
14463 
14464     // Clear references in windows
14465     for (int n = 0; n < g.Windows.Size; n++)
14466     {
14467         ImGuiWindow* window = g.Windows[n];
14468         bool want_removal = (root_id == 0) || (window->DockNode && DockNodeGetRootNode(window->DockNode)->ID == root_id) || (window->DockNodeAsHost && window->DockNodeAsHost->ID == root_id);
14469         if (want_removal)
14470         {
14471             const ImGuiID backup_dock_id = window->DockId;
14472             IM_UNUSED(backup_dock_id);
14473             DockContextProcessUndockWindow(ctx, window, clear_settings_refs);
14474             if (!clear_settings_refs)
14475                 IM_ASSERT(window->DockId == backup_dock_id);
14476         }
14477     }
14478 }
14479 
14480 // 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.
14481 // Return value is ID of the node at the specified direction, so same as (*out_id_at_dir) if that pointer is set.
14482 // 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)14483 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)
14484 {
14485     ImGuiContext* ctx = GImGui;
14486     IM_ASSERT(split_dir != ImGuiDir_None);
14487     IMGUI_DEBUG_LOG_DOCKING("DockBuilderSplitNode node 0x%08X, split_dir %d\n", id, split_dir);
14488 
14489     ImGuiDockNode* node = DockContextFindNodeByID(ctx, id);
14490     if (node == NULL)
14491     {
14492         IM_ASSERT(node != NULL);
14493         return 0;
14494     }
14495 
14496     IM_ASSERT(!node->IsSplitNode()); // Assert if already Split
14497 
14498     ImGuiDockRequest req;
14499     req.Type = ImGuiDockRequestType_Split;
14500     req.DockTargetWindow = NULL;
14501     req.DockTargetNode = node;
14502     req.DockPayload = NULL;
14503     req.DockSplitDir = split_dir;
14504     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);
14505     req.DockSplitOuter = false;
14506     DockContextProcessDock(ctx, &req);
14507 
14508     ImGuiID id_at_dir = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 0 : 1]->ID;
14509     ImGuiID id_at_opposite_dir = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0]->ID;
14510     if (out_id_at_dir)
14511         *out_id_at_dir = id_at_dir;
14512     if (out_id_at_opposite_dir)
14513         *out_id_at_opposite_dir = id_at_opposite_dir;
14514     return id_at_dir;
14515 }
14516 
DockBuilderCopyNodeRec(ImGuiDockNode * src_node,ImGuiID dst_node_id_if_known,ImVector<ImGuiID> * out_node_remap_pairs)14517 static ImGuiDockNode* DockBuilderCopyNodeRec(ImGuiDockNode* src_node, ImGuiID dst_node_id_if_known, ImVector<ImGuiID>* out_node_remap_pairs)
14518 {
14519     ImGuiContext* ctx = GImGui;
14520     ImGuiDockNode* dst_node = ImGui::DockContextAddNode(ctx, dst_node_id_if_known);
14521     dst_node->SharedFlags = src_node->SharedFlags;
14522     dst_node->LocalFlags = src_node->LocalFlags;
14523     dst_node->Pos = src_node->Pos;
14524     dst_node->Size = src_node->Size;
14525     dst_node->SizeRef = src_node->SizeRef;
14526     dst_node->SplitAxis = src_node->SplitAxis;
14527 
14528     out_node_remap_pairs->push_back(src_node->ID);
14529     out_node_remap_pairs->push_back(dst_node->ID);
14530 
14531     for (int child_n = 0; child_n < IM_ARRAYSIZE(src_node->ChildNodes); child_n++)
14532         if (src_node->ChildNodes[child_n])
14533         {
14534             dst_node->ChildNodes[child_n] = DockBuilderCopyNodeRec(src_node->ChildNodes[child_n], 0, out_node_remap_pairs);
14535             dst_node->ChildNodes[child_n]->ParentNode = dst_node;
14536         }
14537 
14538     IMGUI_DEBUG_LOG_DOCKING("Fork node %08X -> %08X (%d childs)\n", src_node->ID, dst_node->ID, dst_node->IsSplitNode() ? 2 : 0);
14539     return dst_node;
14540 }
14541 
DockBuilderCopyNode(ImGuiID src_node_id,ImGuiID dst_node_id,ImVector<ImGuiID> * out_node_remap_pairs)14542 void ImGui::DockBuilderCopyNode(ImGuiID src_node_id, ImGuiID dst_node_id, ImVector<ImGuiID>* out_node_remap_pairs)
14543 {
14544     ImGuiContext* ctx = GImGui;
14545     IM_ASSERT(src_node_id != 0);
14546     IM_ASSERT(dst_node_id != 0);
14547     IM_ASSERT(out_node_remap_pairs != NULL);
14548 
14549     ImGuiDockNode* src_node = DockContextFindNodeByID(ctx, src_node_id);
14550     IM_ASSERT(src_node != NULL);
14551 
14552     out_node_remap_pairs->clear();
14553     DockBuilderRemoveNode(dst_node_id);
14554     DockBuilderCopyNodeRec(src_node, dst_node_id, out_node_remap_pairs);
14555 
14556     IM_ASSERT((out_node_remap_pairs->Size % 2) == 0);
14557 }
14558 
DockBuilderCopyWindowSettings(const char * src_name,const char * dst_name)14559 void ImGui::DockBuilderCopyWindowSettings(const char* src_name, const char* dst_name)
14560 {
14561     ImGuiWindow* src_window = FindWindowByName(src_name);
14562     if (src_window == NULL)
14563         return;
14564     if (ImGuiWindow* dst_window = FindWindowByName(dst_name))
14565     {
14566         dst_window->Pos = src_window->Pos;
14567         dst_window->Size = src_window->Size;
14568         dst_window->SizeFull = src_window->SizeFull;
14569         dst_window->Collapsed = src_window->Collapsed;
14570     }
14571     else if (ImGuiWindowSettings* dst_settings = FindOrCreateWindowSettings(dst_name))
14572     {
14573         ImVec2ih window_pos_2ih = ImVec2ih(src_window->Pos);
14574         if (src_window->ViewportId != 0 && src_window->ViewportId != IMGUI_VIEWPORT_DEFAULT_ID)
14575         {
14576             dst_settings->ViewportPos = window_pos_2ih;
14577             dst_settings->ViewportId = src_window->ViewportId;
14578             dst_settings->Pos = ImVec2ih(0, 0);
14579         }
14580         else
14581         {
14582             dst_settings->Pos = window_pos_2ih;
14583         }
14584         dst_settings->Size = ImVec2ih(src_window->SizeFull);
14585         dst_settings->Collapsed = src_window->Collapsed;
14586     }
14587 }
14588 
14589 // 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)14590 void ImGui::DockBuilderCopyDockSpace(ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector<const char*>* in_window_remap_pairs)
14591 {
14592     IM_ASSERT(src_dockspace_id != 0);
14593     IM_ASSERT(dst_dockspace_id != 0);
14594     IM_ASSERT(in_window_remap_pairs != NULL);
14595     IM_ASSERT((in_window_remap_pairs->Size % 2) == 0);
14596 
14597     // Duplicate entire dock
14598     // 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,
14599     // whereas we could attempt to at least keep them together in a new, same floating node.
14600     ImVector<ImGuiID> node_remap_pairs;
14601     DockBuilderCopyNode(src_dockspace_id, dst_dockspace_id, &node_remap_pairs);
14602 
14603     // Attempt to transition all the upcoming windows associated to dst_dockspace_id into the newly created hierarchy of dock nodes
14604     // (The windows associated to src_dockspace_id are staying in place)
14605     ImVector<ImGuiID> src_windows;
14606     for (int remap_window_n = 0; remap_window_n < in_window_remap_pairs->Size; remap_window_n += 2)
14607     {
14608         const char* src_window_name = (*in_window_remap_pairs)[remap_window_n];
14609         const char* dst_window_name = (*in_window_remap_pairs)[remap_window_n + 1];
14610         ImGuiID src_window_id = ImHashStr(src_window_name);
14611         src_windows.push_back(src_window_id);
14612 
14613         // Search in the remapping tables
14614         ImGuiID src_dock_id = 0;
14615         if (ImGuiWindow* src_window = FindWindowByID(src_window_id))
14616             src_dock_id = src_window->DockId;
14617         else if (ImGuiWindowSettings* src_window_settings = FindWindowSettings(src_window_id))
14618             src_dock_id = src_window_settings->DockId;
14619         ImGuiID dst_dock_id = 0;
14620         for (int dock_remap_n = 0; dock_remap_n < node_remap_pairs.Size; dock_remap_n += 2)
14621             if (node_remap_pairs[dock_remap_n] == src_dock_id)
14622             {
14623                 dst_dock_id = node_remap_pairs[dock_remap_n + 1];
14624                 //node_remap_pairs[dock_remap_n] = node_remap_pairs[dock_remap_n + 1] = 0; // Clear
14625                 break;
14626             }
14627 
14628         if (dst_dock_id != 0)
14629         {
14630             // Docked windows gets redocked into the new node hierarchy.
14631             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);
14632             DockBuilderDockWindow(dst_window_name, dst_dock_id);
14633         }
14634         else
14635         {
14636             // Floating windows gets their settings transferred (regardless of whether the new window already exist or not)
14637             // When this is leading to a Copy and not a Move, we would get two overlapping floating windows. Could we possibly dock them together?
14638             IMGUI_DEBUG_LOG_DOCKING("Remap window settings '%s' -> '%s'\n", src_window_name, dst_window_name);
14639             DockBuilderCopyWindowSettings(src_window_name, dst_window_name);
14640         }
14641     }
14642 
14643     // 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")
14644     // Find those windows and move to them to the cloned dock node. This may be optional?
14645     for (int dock_remap_n = 0; dock_remap_n < node_remap_pairs.Size; dock_remap_n += 2)
14646         if (ImGuiID src_dock_id = node_remap_pairs[dock_remap_n])
14647         {
14648             ImGuiID dst_dock_id = node_remap_pairs[dock_remap_n + 1];
14649             ImGuiDockNode* node = DockBuilderGetNode(src_dock_id);
14650             for (int window_n = 0; window_n < node->Windows.Size; window_n++)
14651             {
14652                 ImGuiWindow* window = node->Windows[window_n];
14653                 if (src_windows.contains(window->ID))
14654                     continue;
14655 
14656                 // Docked windows gets redocked into the new node hierarchy.
14657                 IMGUI_DEBUG_LOG_DOCKING("Remap window '%s' %08X -> %08X\n", window->Name, src_dock_id, dst_dock_id);
14658                 DockBuilderDockWindow(window->Name, dst_dock_id);
14659             }
14660         }
14661 }
14662 
DockBuilderFinish(ImGuiID root_id)14663 void ImGui::DockBuilderFinish(ImGuiID root_id)
14664 {
14665     ImGuiContext* ctx = GImGui;
14666     //DockContextRebuild(ctx);
14667     DockContextBuildAddWindowsToNodes(ctx, root_id);
14668 }
14669 
14670 //-----------------------------------------------------------------------------
14671 // Docking: Begin/End Support Functions (called from Begin/End)
14672 //-----------------------------------------------------------------------------
14673 // - GetWindowAlwaysWantOwnTabBar()
14674 // - DockContextBindNodeToWindow()
14675 // - BeginDocked()
14676 // - BeginDockableDragDropSource()
14677 // - BeginDockableDragDropTarget()
14678 //-----------------------------------------------------------------------------
14679 
GetWindowAlwaysWantOwnTabBar(ImGuiWindow * window)14680 bool ImGui::GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window)
14681 {
14682     ImGuiContext& g = *GImGui;
14683     if (g.IO.ConfigDockingAlwaysTabBar || window->WindowClass.DockingAlwaysTabBar)
14684         if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking)) == 0)
14685             if (!window->IsFallbackWindow)    // We don't support AlwaysTabBar on the fallback/implicit window to avoid unused dock-node overhead/noise
14686                 return true;
14687     return false;
14688 }
14689 
DockContextBindNodeToWindow(ImGuiContext * ctx,ImGuiWindow * window)14690 static ImGuiDockNode* ImGui::DockContextBindNodeToWindow(ImGuiContext* ctx, ImGuiWindow* window)
14691 {
14692     ImGuiContext& g = *ctx;
14693     ImGuiDockNode* node = DockContextFindNodeByID(ctx, window->DockId);
14694     IM_ASSERT(window->DockNode == NULL);
14695 
14696     // We should not be docking into a split node (SetWindowDock should avoid this)
14697     if (node && node->IsSplitNode())
14698     {
14699         DockContextProcessUndockWindow(ctx, window);
14700         return NULL;
14701     }
14702 
14703     // Create node
14704     if (node == NULL)
14705     {
14706         node = DockContextAddNode(ctx, window->DockId);
14707         node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window;
14708         node->LastFrameAlive = g.FrameCount;
14709     }
14710 
14711     // If the node just turned visible and is part of a hierarchy, it doesn't have a Size assigned by DockNodeTreeUpdatePosSize() yet,
14712     // so we're forcing a Pos/Size update from the first ancestor that is already visible (often it will be the root node).
14713     // 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.
14714     // This is a little wonky because we don't normally update the Pos/Size of visible node mid-frame.
14715     if (!node->IsVisible)
14716     {
14717         ImGuiDockNode* ancestor_node = node;
14718         while (!ancestor_node->IsVisible)
14719         {
14720             ancestor_node->IsVisible = true;
14721             ancestor_node->MarkedForPosSizeWrite = true;
14722             if (ancestor_node->ParentNode)
14723                 ancestor_node = ancestor_node->ParentNode;
14724         }
14725         IM_ASSERT(ancestor_node->Size.x > 0.0f && ancestor_node->Size.y > 0.0f);
14726         DockNodeTreeUpdatePosSize(ancestor_node, ancestor_node->Pos, ancestor_node->Size, true);
14727     }
14728 
14729     // Add window to node
14730     DockNodeAddWindow(node, window, true);
14731     IM_ASSERT(node == window->DockNode);
14732     return node;
14733 }
14734 
BeginDocked(ImGuiWindow * window,bool * p_open)14735 void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open)
14736 {
14737     ImGuiContext* ctx = GImGui;
14738     ImGuiContext& g = *ctx;
14739 
14740     const bool auto_dock_node = GetWindowAlwaysWantOwnTabBar(window);
14741     if (auto_dock_node)
14742     {
14743         if (window->DockId == 0)
14744         {
14745             IM_ASSERT(window->DockNode == NULL);
14746             window->DockId = DockContextGenNodeID(ctx);
14747         }
14748     }
14749     else
14750     {
14751         // Calling SetNextWindowPos() undock windows by default (by setting PosUndock)
14752         bool want_undock = false;
14753         want_undock |= (window->Flags & ImGuiWindowFlags_NoDocking) != 0;
14754         want_undock |= (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) && (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) && g.NextWindowData.PosUndock;
14755         if (want_undock)
14756         {
14757             DockContextProcessUndockWindow(ctx, window);
14758             return;
14759         }
14760     }
14761 
14762     // Bind to our dock node
14763     ImGuiDockNode* node = window->DockNode;
14764     if (node != NULL)
14765         IM_ASSERT(window->DockId == node->ID);
14766     if (window->DockId != 0 && node == NULL)
14767     {
14768         node = DockContextBindNodeToWindow(ctx, window);
14769         if (node == NULL)
14770             return;
14771     }
14772 
14773 #if 0
14774     // Undock if the ImGuiDockNodeFlags_NoDockingInCentralNode got set
14775     if (node->IsCentralNode && (node->Flags & ImGuiDockNodeFlags_NoDockingInCentralNode))
14776     {
14777         DockContextProcessUndockWindow(ctx, window);
14778         return;
14779     }
14780 #endif
14781 
14782     // Undock if our dockspace node disappeared
14783     // Note how we are testing for LastFrameAlive and NOT LastFrameActive. A DockSpace node can be maintained alive while being inactive with ImGuiDockNodeFlags_KeepAliveOnly.
14784     if (node->LastFrameAlive < g.FrameCount)
14785     {
14786         // If the window has been orphaned, transition the docknode to an implicit node processed in DockContextUpdateDocking()
14787         ImGuiDockNode* root_node = DockNodeGetRootNode(node);
14788         if (root_node->LastFrameAlive < g.FrameCount)
14789         {
14790             DockContextProcessUndockWindow(ctx, window);
14791         }
14792         else
14793         {
14794             window->DockIsActive = true;
14795             window->DockTabIsVisible = false;
14796         }
14797         return;
14798     }
14799 
14800     // Fast path return. It is common for windows to hold on a persistent DockId but be the only visible window,
14801     // and never create neither a host window neither a tab bar.
14802     // FIXME-DOCK: replace ->HostWindow NULL compare with something more explicit (~was initially intended as a first frame test)
14803     if (node->HostWindow == NULL)
14804     {
14805         window->DockIsActive = (node->State == ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing);
14806         window->DockTabIsVisible = false;
14807         return;
14808     }
14809 
14810     // We can have zero-sized nodes (e.g. children of a small-size dockspace)
14811     IM_ASSERT(node->HostWindow);
14812     IM_ASSERT(node->IsLeafNode());
14813     IM_ASSERT(node->Size.x >= 0.0f && node->Size.y >= 0.0f);
14814     node->State = ImGuiDockNodeState_HostWindowVisible;
14815 
14816     // Undock if we are submitted earlier than the host window
14817     if (window->BeginOrderWithinContext < node->HostWindow->BeginOrderWithinContext)
14818     {
14819         DockContextProcessUndockWindow(ctx, window);
14820         return;
14821     }
14822 
14823     // Position/Size window
14824     SetNextWindowPos(node->Pos);
14825     SetNextWindowSize(node->Size);
14826     g.NextWindowData.PosUndock = false; // Cancel implicit undocking of SetNextWindowPos()
14827     window->DockIsActive = true;
14828     window->DockTabIsVisible = false;
14829     if (node->SharedFlags & ImGuiDockNodeFlags_KeepAliveOnly)
14830         return;
14831 
14832     // When the window is selected we mark it as visible.
14833     if (node->VisibleWindow == window)
14834         window->DockTabIsVisible = true;
14835 
14836     // Update window flag
14837     IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) == 0);
14838     window->Flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_NoResize;
14839     if (node->IsHiddenTabBar() || node->IsNoTabBar())
14840         window->Flags |= ImGuiWindowFlags_NoTitleBar;
14841     else
14842         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!
14843 
14844     // Save new dock order only if the tab bar has been visible once.
14845     // This allows multiple windows to be created in the same frame and have their respective dock orders preserved.
14846     if (node->TabBar && node->TabBar->CurrFrameVisible != -1)
14847         window->DockOrder = (short)DockNodeGetTabOrder(window);
14848 
14849     if ((node->WantCloseAll || node->WantCloseTabId == window->ID) && p_open != NULL)
14850         *p_open = false;
14851 
14852     // Update ChildId to allow returning from Child to Parent with Escape
14853     ImGuiWindow* parent_window = window->DockNode->HostWindow;
14854     window->ChildId = parent_window->GetID(window->Name);
14855 }
14856 
BeginDockableDragDropSource(ImGuiWindow * window)14857 void ImGui::BeginDockableDragDropSource(ImGuiWindow* window)
14858 {
14859     ImGuiContext& g = *GImGui;
14860     IM_ASSERT(g.ActiveId == window->MoveId);
14861     IM_ASSERT(g.MovingWindow == window);
14862 
14863     window->DC.LastItemId = window->MoveId;
14864     window = window->RootWindow;
14865     IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0);
14866     bool is_drag_docking = (g.IO.ConfigDockingWithShift) || ImRect(0, 0, window->SizeFull.x, GetFrameHeight()).Contains(g.ActiveIdClickOffset);
14867     if (is_drag_docking && BeginDragDropSource(ImGuiDragDropFlags_SourceNoPreviewTooltip | ImGuiDragDropFlags_SourceNoHoldToOpenOthers | ImGuiDragDropFlags_SourceAutoExpirePayload))
14868     {
14869         SetDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, &window, sizeof(window));
14870         EndDragDropSource();
14871     }
14872 }
14873 
BeginDockableDragDropTarget(ImGuiWindow * window)14874 void ImGui::BeginDockableDragDropTarget(ImGuiWindow* window)
14875 {
14876     ImGuiContext* ctx = GImGui;
14877     ImGuiContext& g = *ctx;
14878 
14879     //IM_ASSERT(window->RootWindow == window); // May also be a DockSpace
14880     IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0);
14881     if (!g.DragDropActive)
14882         return;
14883     //GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
14884     if (!BeginDragDropTargetCustom(window->Rect(), window->ID))
14885         return;
14886 
14887     // Peek into the payload before calling AcceptDragDropPayload() so we can handle overlapping dock nodes with filtering
14888     // (this is a little unusual pattern, normally most code would call AcceptDragDropPayload directly)
14889     const ImGuiPayload* payload = &g.DragDropPayload;
14890     if (!payload->IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) || !DockNodeIsDropAllowed(window, *(ImGuiWindow**)payload->Data))
14891     {
14892         EndDragDropTarget();
14893         return;
14894     }
14895 
14896     ImGuiWindow* payload_window = *(ImGuiWindow**)payload->Data;
14897     if (AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect))
14898     {
14899         // Select target node
14900         // (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)
14901         bool dock_into_floating_window = false;
14902         ImGuiDockNode* node = NULL;
14903         if (window->DockNodeAsHost)
14904         {
14905             // Cannot assume that node will != NULL even though we passed the rectangle test: it depends on padding/spacing handled by DockNodeTreeFindVisibleNodeByPos().
14906             node = DockNodeTreeFindVisibleNodeByPos(window->DockNodeAsHost, g.IO.MousePos);
14907 
14908             // There is an edge case when docking into a dockspace which only has _inactive_ nodes (because none of the windows are active)
14909             // In this case we need to fallback into any leaf mode, possibly the central node.
14910             // FIXME-20181220: We should not have to test for IsLeafNode() here but we have another bug to fix first.
14911             if (node && node->IsDockSpace() && node->IsRootNode())
14912                 node = (node->CentralNode && node->IsLeafNode()) ? node->CentralNode : DockNodeTreeFindFallbackLeafNode(node);
14913         }
14914         else
14915         {
14916             if (window->DockNode)
14917                 node = window->DockNode;
14918             else
14919                 dock_into_floating_window = true; // Dock into a regular window
14920         }
14921 
14922         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()));
14923         const bool is_explicit_target = g.IO.ConfigDockingWithShift || IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max);
14924 
14925         // Preview docking request and find out split direction/ratio
14926         //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.
14927         const bool do_preview = payload->IsPreview() || payload->IsDelivery();
14928         if (do_preview && (node != NULL || dock_into_floating_window))
14929         {
14930             ImGuiDockPreviewData split_inner;
14931             ImGuiDockPreviewData split_outer;
14932             ImGuiDockPreviewData* split_data = &split_inner;
14933             if (node && (node->ParentNode || node->IsCentralNode()))
14934                 if (ImGuiDockNode* root_node = DockNodeGetRootNode(node))
14935                 {
14936                     DockNodePreviewDockSetup(window, root_node, payload_window, &split_outer, is_explicit_target, true);
14937                     if (split_outer.IsSplitDirExplicit)
14938                         split_data = &split_outer;
14939                 }
14940             DockNodePreviewDockSetup(window, node, payload_window, &split_inner, is_explicit_target, false);
14941             if (split_data == &split_outer)
14942                 split_inner.IsDropAllowed = false;
14943 
14944             // Draw inner then outer, so that previewed tab (in inner data) will be behind the outer drop boxes
14945             DockNodePreviewDockRender(window, node, payload_window, &split_inner);
14946             DockNodePreviewDockRender(window, node, payload_window, &split_outer);
14947 
14948             // Queue docking request
14949             if (split_data->IsDropAllowed && payload->IsDelivery())
14950                 DockContextQueueDock(ctx, window, split_data->SplitNode, payload_window, split_data->SplitDir, split_data->SplitRatio, split_data == &split_outer);
14951         }
14952     }
14953     EndDragDropTarget();
14954 }
14955 
14956 //-----------------------------------------------------------------------------
14957 // Docking: Settings
14958 //-----------------------------------------------------------------------------
14959 // - DockSettingsRenameNodeReferences()
14960 // - DockSettingsRemoveNodeReferences()
14961 // - DockSettingsFindNodeSettings()
14962 // - DockSettingsHandler_ApplyAll()
14963 // - DockSettingsHandler_ReadOpen()
14964 // - DockSettingsHandler_ReadLine()
14965 // - DockSettingsHandler_DockNodeToSettings()
14966 // - DockSettingsHandler_WriteAll()
14967 //-----------------------------------------------------------------------------
14968 
DockSettingsRenameNodeReferences(ImGuiID old_node_id,ImGuiID new_node_id)14969 static void ImGui::DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id)
14970 {
14971     ImGuiContext& g = *GImGui;
14972     IMGUI_DEBUG_LOG_DOCKING("DockSettingsRenameNodeReferences: from 0x%08X -> to 0x%08X\n", old_node_id, new_node_id);
14973     for (int window_n = 0; window_n < g.Windows.Size; window_n++)
14974     {
14975         ImGuiWindow* window = g.Windows[window_n];
14976         if (window->DockId == old_node_id && window->DockNode == NULL)
14977             window->DockId = new_node_id;
14978     }
14979     //// FIXME-OPT: We could remove this loop by storing the index in the map
14980     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
14981         if (settings->DockId == old_node_id)
14982             settings->DockId = new_node_id;
14983 }
14984 
14985 // Remove references stored in ImGuiWindowSettings to the given ImGuiDockNodeSettings
DockSettingsRemoveNodeReferences(ImGuiID * node_ids,int node_ids_count)14986 static void ImGui::DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ids_count)
14987 {
14988     ImGuiContext& g = *GImGui;
14989     int found = 0;
14990     //// FIXME-OPT: We could remove this loop by storing the index in the map
14991     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
14992         for (int node_n = 0; node_n < node_ids_count; node_n++)
14993             if (settings->DockId == node_ids[node_n])
14994             {
14995                 settings->DockId = 0;
14996                 settings->DockOrder = -1;
14997                 if (++found < node_ids_count)
14998                     break;
14999                 return;
15000             }
15001 }
15002 
DockSettingsFindNodeSettings(ImGuiContext * ctx,ImGuiID id)15003 static ImGuiDockNodeSettings* ImGui::DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID id)
15004 {
15005     // FIXME-OPT
15006     ImGuiDockContext* dc  = &ctx->DockContext;
15007     for (int n = 0; n < dc->NodesSettings.Size; n++)
15008         if (dc->NodesSettings[n].ID == id)
15009             return &dc->NodesSettings[n];
15010     return NULL;
15011 }
15012 
15013 // Clear settings data
DockSettingsHandler_ClearAll(ImGuiContext * ctx,ImGuiSettingsHandler *)15014 static void ImGui::DockSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
15015 {
15016     ImGuiDockContext* dc  = &ctx->DockContext;
15017     dc->NodesSettings.clear();
15018     DockContextClearNodes(ctx, 0, true);
15019 }
15020 
15021 // Recreate nodes based on settings data
DockSettingsHandler_ApplyAll(ImGuiContext * ctx,ImGuiSettingsHandler *)15022 static void ImGui::DockSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
15023 {
15024     // Prune settings at boot time only
15025     ImGuiDockContext* dc  = &ctx->DockContext;
15026     if (ctx->Windows.Size == 0)
15027         DockContextPruneUnusedSettingsNodes(ctx);
15028     DockContextBuildNodesFromSettings(ctx, dc->NodesSettings.Data, dc->NodesSettings.Size);
15029     DockContextBuildAddWindowsToNodes(ctx, 0);
15030 }
15031 
DockSettingsHandler_ReadOpen(ImGuiContext *,ImGuiSettingsHandler *,const char * name)15032 static void* ImGui::DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
15033 {
15034     if (strcmp(name, "Data") != 0)
15035         return NULL;
15036     return (void*)1;
15037 }
15038 
DockSettingsHandler_ReadLine(ImGuiContext * ctx,ImGuiSettingsHandler *,void *,const char * line)15039 static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettingsHandler*, void*, const char* line)
15040 {
15041     char c = 0;
15042     int x = 0, y = 0;
15043     int r = 0;
15044 
15045     // Parsing, e.g.
15046     // " DockNode   ID=0x00000001 Pos=383,193 Size=201,322 Split=Y,0.506 "
15047     // "   DockNode ID=0x00000002 Parent=0x00000001 "
15048     // Important: this code expect currently fields in a fixed order.
15049     ImGuiDockNodeSettings node;
15050     line = ImStrSkipBlank(line);
15051     if      (strncmp(line, "DockNode", 8) == 0)  { line = ImStrSkipBlank(line + strlen("DockNode")); }
15052     else if (strncmp(line, "DockSpace", 9) == 0) { line = ImStrSkipBlank(line + strlen("DockSpace")); node.Flags |= ImGuiDockNodeFlags_DockSpace; }
15053     else return;
15054     if (sscanf(line, "ID=0x%08X%n",      &node.ID, &r) == 1)            { line += r; } else return;
15055     if (sscanf(line, " Parent=0x%08X%n", &node.ParentNodeId, &r) == 1)  { line += r; if (node.ParentNodeId == 0) return; }
15056     if (sscanf(line, " Window=0x%08X%n", &node.ParentWindowId, &r) ==1) { line += r; if (node.ParentWindowId == 0) return; }
15057     if (node.ParentNodeId == 0)
15058     {
15059         if (sscanf(line, " Pos=%i,%i%n",  &x, &y, &r) == 2)         { line += r; node.Pos = ImVec2ih((short)x, (short)y); } else return;
15060         if (sscanf(line, " Size=%i,%i%n", &x, &y, &r) == 2)         { line += r; node.Size = ImVec2ih((short)x, (short)y); } else return;
15061     }
15062     else
15063     {
15064         if (sscanf(line, " SizeRef=%i,%i%n", &x, &y, &r) == 2)      { line += r; node.SizeRef = ImVec2ih((short)x, (short)y); }
15065     }
15066     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; }
15067     if (sscanf(line, " NoResize=%d%n", &x, &r) == 1)                { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoResize; }
15068     if (sscanf(line, " CentralNode=%d%n", &x, &r) == 1)             { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_CentralNode; }
15069     if (sscanf(line, " NoTabBar=%d%n", &x, &r) == 1)                { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoTabBar; }
15070     if (sscanf(line, " HiddenTabBar=%d%n", &x, &r) == 1)            { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_HiddenTabBar; }
15071     if (sscanf(line, " NoWindowMenuButton=%d%n", &x, &r) == 1)      { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoWindowMenuButton; }
15072     if (sscanf(line, " NoCloseButton=%d%n", &x, &r) == 1)           { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoCloseButton; }
15073     if (sscanf(line, " Selected=0x%08X%n", &node.SelectedWindowId,&r) == 1) { line += r; }
15074     if (node.ParentNodeId != 0)
15075         if (ImGuiDockNodeSettings* parent_settings = DockSettingsFindNodeSettings(ctx, node.ParentNodeId))
15076             node.Depth = parent_settings->Depth + 1;
15077     ctx->DockContext.NodesSettings.push_back(node);
15078 }
15079 
DockSettingsHandler_DockNodeToSettings(ImGuiDockContext * dc,ImGuiDockNode * node,int depth)15080 static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDockNode* node, int depth)
15081 {
15082     ImGuiDockNodeSettings node_settings;
15083     IM_ASSERT(depth < (1 << (sizeof(node_settings.Depth) << 3)));
15084     node_settings.ID = node->ID;
15085     node_settings.ParentNodeId = node->ParentNode ? node->ParentNode->ID : 0;
15086     node_settings.ParentWindowId = (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow) ? node->HostWindow->ParentWindow->ID : 0;
15087     node_settings.SelectedWindowId = node->SelectedTabId;
15088     node_settings.SplitAxis = (signed char)(node->IsSplitNode() ? node->SplitAxis : ImGuiAxis_None);
15089     node_settings.Depth = (char)depth;
15090     node_settings.Flags = (node->LocalFlags & ImGuiDockNodeFlags_SavedFlagsMask_);
15091     node_settings.Pos = ImVec2ih(node->Pos);
15092     node_settings.Size = ImVec2ih(node->Size);
15093     node_settings.SizeRef = ImVec2ih(node->SizeRef);
15094     dc->NodesSettings.push_back(node_settings);
15095     if (node->ChildNodes[0])
15096         DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[0], depth + 1);
15097     if (node->ChildNodes[1])
15098         DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[1], depth + 1);
15099 }
15100 
DockSettingsHandler_WriteAll(ImGuiContext * ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)15101 static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
15102 {
15103     ImGuiContext& g = *ctx;
15104     ImGuiDockContext* dc = &ctx->DockContext;
15105     if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
15106         return;
15107 
15108     // Gather settings data
15109     // (unlike our windows settings, because nodes are always built we can do a full rewrite of the SettingsNode buffer)
15110     dc->NodesSettings.resize(0);
15111     dc->NodesSettings.reserve(dc->Nodes.Data.Size);
15112     for (int n = 0; n < dc->Nodes.Data.Size; n++)
15113         if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
15114             if (node->IsRootNode())
15115                 DockSettingsHandler_DockNodeToSettings(dc, node, 0);
15116 
15117     int max_depth = 0;
15118     for (int node_n = 0; node_n < dc->NodesSettings.Size; node_n++)
15119         max_depth = ImMax((int)dc->NodesSettings[node_n].Depth, max_depth);
15120 
15121     // Write to text buffer
15122     buf->appendf("[%s][Data]\n", handler->TypeName);
15123     for (int node_n = 0; node_n < dc->NodesSettings.Size; node_n++)
15124     {
15125         const int line_start_pos = buf->size(); (void)line_start_pos;
15126         const ImGuiDockNodeSettings* node_settings = &dc->NodesSettings[node_n];
15127         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
15128         buf->appendf(" ID=0x%08X", node_settings->ID);
15129         if (node_settings->ParentNodeId)
15130         {
15131             buf->appendf(" Parent=0x%08X SizeRef=%d,%d", node_settings->ParentNodeId, node_settings->SizeRef.x, node_settings->SizeRef.y);
15132         }
15133         else
15134         {
15135             if (node_settings->ParentWindowId)
15136                 buf->appendf(" Window=0x%08X", node_settings->ParentWindowId);
15137             buf->appendf(" Pos=%d,%d Size=%d,%d", node_settings->Pos.x, node_settings->Pos.y, node_settings->Size.x, node_settings->Size.y);
15138         }
15139         if (node_settings->SplitAxis != ImGuiAxis_None)
15140             buf->appendf(" Split=%c", (node_settings->SplitAxis == ImGuiAxis_X) ? 'X' : 'Y');
15141         if (node_settings->Flags & ImGuiDockNodeFlags_NoResize)
15142             buf->appendf(" NoResize=1");
15143         if (node_settings->Flags & ImGuiDockNodeFlags_CentralNode)
15144             buf->appendf(" CentralNode=1");
15145         if (node_settings->Flags & ImGuiDockNodeFlags_NoTabBar)
15146             buf->appendf(" NoTabBar=1");
15147         if (node_settings->Flags & ImGuiDockNodeFlags_HiddenTabBar)
15148             buf->appendf(" HiddenTabBar=1");
15149         if (node_settings->Flags & ImGuiDockNodeFlags_NoWindowMenuButton)
15150             buf->appendf(" NoWindowMenuButton=1");
15151         if (node_settings->Flags & ImGuiDockNodeFlags_NoCloseButton)
15152             buf->appendf(" NoCloseButton=1");
15153         if (node_settings->SelectedWindowId)
15154             buf->appendf(" Selected=0x%08X", node_settings->SelectedWindowId);
15155 
15156 #if IMGUI_DEBUG_INI_SETTINGS
15157         // [DEBUG] Include comments in the .ini file to ease debugging
15158         if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_settings->ID))
15159         {
15160             buf->appendf("%*s", ImMax(2, (line_start_pos + 92) - buf->size()), "");     // Align everything
15161             if (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow)
15162                 buf->appendf(" ; in '%s'", node->HostWindow->ParentWindow->Name);
15163             // Iterate settings so we can give info about windows that didn't exist during the session.
15164             int contains_window = 0;
15165             for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
15166                 if (settings->DockId == node_settings->ID)
15167                 {
15168                     if (contains_window++ == 0)
15169                         buf->appendf(" ; contains ");
15170                     buf->appendf("'%s' ", settings->GetName());
15171                 }
15172         }
15173 #endif
15174         buf->appendf("\n");
15175     }
15176     buf->appendf("\n");
15177 }
15178 
15179 
15180 //-----------------------------------------------------------------------------
15181 // [SECTION] PLATFORM DEPENDENT HELPERS
15182 //-----------------------------------------------------------------------------
15183 
15184 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
15185 
15186 #ifdef _MSC_VER
15187 #pragma comment(lib, "user32")
15188 #pragma comment(lib, "kernel32")
15189 #endif
15190 
15191 // Win32 clipboard implementation
15192 // We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown()
GetClipboardTextFn_DefaultImpl(void *)15193 static const char* GetClipboardTextFn_DefaultImpl(void*)
15194 {
15195     ImGuiContext& g = *GImGui;
15196     g.ClipboardHandlerData.clear();
15197     if (!::OpenClipboard(NULL))
15198         return NULL;
15199     HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
15200     if (wbuf_handle == NULL)
15201     {
15202         ::CloseClipboard();
15203         return NULL;
15204     }
15205     if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle))
15206     {
15207         int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL);
15208         g.ClipboardHandlerData.resize(buf_len);
15209         ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL);
15210     }
15211     ::GlobalUnlock(wbuf_handle);
15212     ::CloseClipboard();
15213     return g.ClipboardHandlerData.Data;
15214 }
15215 
SetClipboardTextFn_DefaultImpl(void *,const char * text)15216 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
15217 {
15218     if (!::OpenClipboard(NULL))
15219         return;
15220     const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);
15221     HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR));
15222     if (wbuf_handle == NULL)
15223     {
15224         ::CloseClipboard();
15225         return;
15226     }
15227     WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle);
15228     ::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length);
15229     ::GlobalUnlock(wbuf_handle);
15230     ::EmptyClipboard();
15231     if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
15232         ::GlobalFree(wbuf_handle);
15233     ::CloseClipboard();
15234 }
15235 
15236 #elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS)
15237 
15238 #include <Carbon/Carbon.h>  // Use old API to avoid need for separate .mm file
15239 static PasteboardRef main_clipboard = 0;
15240 
15241 // OSX clipboard implementation
15242 // If you enable this you will need to add '-framework ApplicationServices' to your linker command-line!
SetClipboardTextFn_DefaultImpl(void *,const char * text)15243 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
15244 {
15245     if (!main_clipboard)
15246         PasteboardCreate(kPasteboardClipboard, &main_clipboard);
15247     PasteboardClear(main_clipboard);
15248     CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text));
15249     if (cf_data)
15250     {
15251         PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0);
15252         CFRelease(cf_data);
15253     }
15254 }
15255 
GetClipboardTextFn_DefaultImpl(void *)15256 static const char* GetClipboardTextFn_DefaultImpl(void*)
15257 {
15258     if (!main_clipboard)
15259         PasteboardCreate(kPasteboardClipboard, &main_clipboard);
15260     PasteboardSynchronize(main_clipboard);
15261 
15262     ItemCount item_count = 0;
15263     PasteboardGetItemCount(main_clipboard, &item_count);
15264     for (ItemCount i = 0; i < item_count; i++)
15265     {
15266         PasteboardItemID item_id = 0;
15267         PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id);
15268         CFArrayRef flavor_type_array = 0;
15269         PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array);
15270         for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++)
15271         {
15272             CFDataRef cf_data;
15273             if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr)
15274             {
15275                 ImGuiContext& g = *GImGui;
15276                 g.ClipboardHandlerData.clear();
15277                 int length = (int)CFDataGetLength(cf_data);
15278                 g.ClipboardHandlerData.resize(length + 1);
15279                 CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data);
15280                 g.ClipboardHandlerData[length] = 0;
15281                 CFRelease(cf_data);
15282                 return g.ClipboardHandlerData.Data;
15283             }
15284         }
15285     }
15286     return NULL;
15287 }
15288 
15289 #else
15290 
15291 // Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers.
GetClipboardTextFn_DefaultImpl(void *)15292 static const char* GetClipboardTextFn_DefaultImpl(void*)
15293 {
15294     ImGuiContext& g = *GImGui;
15295     return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin();
15296 }
15297 
SetClipboardTextFn_DefaultImpl(void *,const char * text)15298 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
15299 {
15300     ImGuiContext& g = *GImGui;
15301     g.ClipboardHandlerData.clear();
15302     const char* text_end = text + strlen(text);
15303     g.ClipboardHandlerData.resize((int)(text_end - text) + 1);
15304     memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text));
15305     g.ClipboardHandlerData[(int)(text_end - text)] = 0;
15306 }
15307 
15308 #endif
15309 
15310 //-----------------------------------------------------------------------------
15311 // [SECTION] METRICS/DEBUG WINDOW
15312 //-----------------------------------------------------------------------------
15313 
RenderViewportThumbnail(ImDrawList * draw_list,ImGuiViewportP * viewport,const ImRect & bb)15314 static void RenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb)
15315 {
15316     ImGuiContext& g = *GImGui;
15317     ImGuiWindow* window = g.CurrentWindow;
15318 
15319     ImVec2 scale = bb.GetSize() / viewport->Size;
15320     ImVec2 off = bb.Min - viewport->Pos * scale;
15321     float alpha_mul = (viewport->Flags & ImGuiViewportFlags_Minimized) ? 0.30f : 1.00f;
15322     window->DrawList->AddRectFilled(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f));
15323     for (int i = 0; i != g.Windows.Size; i++)
15324     {
15325         ImGuiWindow* thumb_window = g.Windows[i];
15326         if (!thumb_window->WasActive || ((thumb_window->Flags & ImGuiWindowFlags_ChildWindow)))
15327             continue;
15328         if (thumb_window->SkipItems && (thumb_window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME-DOCK: Skip hidden docked windows. Identify those betters.
15329             continue;
15330         if (thumb_window->Viewport != viewport)
15331             continue;
15332 
15333         ImRect thumb_r = thumb_window->Rect();
15334         ImRect title_r = thumb_window->TitleBarRect();
15335         ImRect thumb_r_scaled = ImRect(ImFloor(off + thumb_r.Min * scale), ImFloor(off +  thumb_r.Max * scale));
15336         ImRect title_r_scaled = ImRect(ImFloor(off + title_r.Min * scale), ImFloor(off +  ImVec2(title_r.Max.x, title_r.Min.y) * scale) + ImVec2(0,5)); // Exaggerate title bar height
15337         thumb_r_scaled.ClipWithFull(bb);
15338         title_r_scaled.ClipWithFull(bb);
15339         const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight);
15340         window->DrawList->AddRectFilled(thumb_r_scaled.Min, thumb_r_scaled.Max, ImGui::GetColorU32(ImGuiCol_WindowBg, alpha_mul));
15341         window->DrawList->AddRectFilled(title_r_scaled.Min, title_r_scaled.Max, ImGui::GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg, alpha_mul));
15342         window->DrawList->AddRect(thumb_r_scaled.Min, thumb_r_scaled.Max, ImGui::GetColorU32(ImGuiCol_Border, alpha_mul));
15343         if (ImGuiWindow* window_for_title = GetWindowForTitleDisplay(thumb_window))
15344             window->DrawList->AddText(g.Font, g.FontSize * 1.0f, title_r_scaled.Min, ImGui::GetColorU32(ImGuiCol_Text, alpha_mul), window_for_title->Name, ImGui::FindRenderedTextEnd(window_for_title->Name));
15345     }
15346     draw_list->AddRect(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_Border, alpha_mul));
15347 }
15348 
ShowViewportThumbnails()15349 void ImGui::ShowViewportThumbnails()
15350 {
15351     ImGuiContext& g = *GImGui;
15352     ImGuiWindow* window = g.CurrentWindow;
15353 
15354     // 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.
15355     float SCALE = 1.0f / 8.0f;
15356     ImRect bb_full;
15357     //for (int n = 0; n < g.PlatformIO.Monitors.Size; n++)
15358     //    bb_full.Add(GetPlatformMonitorMainRect(g.PlatformIO.Monitors[n]));
15359     for (int n = 0; n < g.Viewports.Size; n++)
15360         bb_full.Add(g.Viewports[n]->GetMainRect());
15361     ImVec2 p = window->DC.CursorPos;
15362     ImVec2 off = p - bb_full.Min * SCALE;
15363     //for (int n = 0; n < g.PlatformIO.Monitors.Size; n++)
15364     //    window->DrawList->AddRect(off + g.PlatformIO.Monitors[n].MainPos * SCALE, off + (g.PlatformIO.Monitors[n].MainPos + g.PlatformIO.Monitors[n].MainSize) * SCALE, ImGui::GetColorU32(ImGuiCol_Border));
15365     for (int n = 0; n < g.Viewports.Size; n++)
15366     {
15367         ImGuiViewportP* viewport = g.Viewports[n];
15368         ImRect viewport_draw_bb(off + (viewport->Pos) * SCALE, off + (viewport->Pos + viewport->Size) * SCALE);
15369         RenderViewportThumbnail(window->DrawList, viewport, viewport_draw_bb);
15370     }
15371     ImGui::Dummy(bb_full.GetSize() * SCALE);
15372 }
15373 
15374 #ifndef IMGUI_DISABLE_METRICS_WINDOW
15375 
15376 // Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds.
MetricsHelpMarker(const char * desc)15377 static void MetricsHelpMarker(const char* desc)
15378 {
15379     ImGui::TextDisabled("(?)");
15380     if (ImGui::IsItemHovered())
15381     {
15382         ImGui::BeginTooltip();
15383         ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
15384         ImGui::TextUnformatted(desc);
15385         ImGui::PopTextWrapPos();
15386         ImGui::EndTooltip();
15387     }
15388 }
15389 
ShowMetricsWindow(bool * p_open)15390 void ImGui::ShowMetricsWindow(bool* p_open)
15391 {
15392     if (!ImGui::Begin("Dear ImGui Metrics", p_open))
15393     {
15394         ImGui::End();
15395         return;
15396     }
15397 
15398     // Debugging enums
15399     enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type
15400     const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentRegionRect" };
15401     enum { TRT_OuterRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersIdeal, TRT_ColumnsContentRowsFrozen, TRT_ColumnsContentRowsUnfrozen, TRT_Count }; // Tables Rect Type
15402     const char* trt_rects_names[TRT_Count] = { "OuterRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentRowsFrozen", "ColumnsContentRowsUnfrozen" };
15403 
15404     // State
15405     static bool show_windows_rects = false;
15406     static int  show_windows_rect_type = WRT_WorkRect;
15407     static bool show_windows_begin_order = false;
15408     static bool show_tables_rects = false;
15409     static int  show_tables_rect_type = TRT_WorkRect;
15410     static bool show_drawcmd_mesh = true;
15411     static bool show_drawcmd_aabb = true;
15412     static bool show_docking_nodes = false;
15413 
15414     // Basic info
15415     ImGuiContext& g = *GImGui;
15416     ImGuiIO& io = ImGui::GetIO();
15417     ImGui::Text("Dear ImGui %s", ImGui::GetVersion());
15418     ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
15419     ImGui::Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
15420     ImGui::Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
15421     ImGui::Text("%d active allocations", io.MetricsActiveAllocations);
15422     ImGui::Separator();
15423 
15424     // Helper functions to display common structures:
15425     // - NodeDrawList()
15426     // - NodeColumns()
15427     // - NodeWindow()
15428     // - NodeWindows()
15429     // - NodeViewport()
15430     // - NodeDockNode()
15431     // - NodeTabBar()
15432     // - NodeStorage()
15433     struct Funcs
15434     {
15435         static ImRect GetWindowRect(ImGuiWindow* window, int rect_type)
15436         {
15437             if (rect_type == WRT_OuterRect)                 { return window->Rect(); }
15438             else if (rect_type == WRT_OuterRectClipped)     { return window->OuterRectClipped; }
15439             else if (rect_type == WRT_InnerRect)            { return window->InnerRect; }
15440             else if (rect_type == WRT_InnerClipRect)        { return window->InnerClipRect; }
15441             else if (rect_type == WRT_WorkRect)             { return window->WorkRect; }
15442             else if (rect_type == WRT_Content)              { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
15443             else if (rect_type == WRT_ContentRegionRect)    { return window->ContentRegionRect; }
15444             IM_ASSERT(0);
15445             return ImRect();
15446         }
15447 
15448         static void NodeDrawCmdShowMeshAndBoundingBox(ImDrawList* fg_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, int elem_offset, bool show_mesh, bool show_aabb)
15449         {
15450             IM_ASSERT(show_mesh || show_aabb);
15451             ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
15452 
15453             // Draw wire-frame version of all triangles
15454             ImRect clip_rect = draw_cmd->ClipRect;
15455             ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
15456             ImDrawListFlags backup_flags = fg_draw_list->Flags;
15457             fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
15458             for (unsigned int base_idx = elem_offset; base_idx < (elem_offset + draw_cmd->ElemCount); base_idx += 3)
15459             {
15460                 ImVec2 triangle[3];
15461                 for (int n = 0; n < 3; n++)
15462                 {
15463                     ImVec2 p = draw_list->VtxBuffer[idx_buffer ? idx_buffer[base_idx + n] : (base_idx + n)].pos;
15464                     triangle[n] = p;
15465                     vtxs_rect.Add(p);
15466                 }
15467                 if (show_mesh)
15468                     fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), true, 1.0f); // In yellow: mesh triangles
15469             }
15470             // Draw bounding boxes
15471             if (show_aabb)
15472             {
15473                 fg_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU
15474                 fg_draw_list->AddRect(ImFloor(vtxs_rect.Min), ImFloor(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles
15475             }
15476             fg_draw_list->Flags = backup_flags;
15477         }
15478 
15479         // Note that both 'window' and 'viewport' may be NULL here. Viewport is generally null of destroyed popups which previously owned a viewport.
15480         static void NodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, ImDrawList* draw_list, const char* label)
15481         {
15482             bool node_open = ImGui::TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, draw_list->CmdBuffer.Size);
15483             if (draw_list == ImGui::GetWindowDrawList())
15484             {
15485                 ImGui::SameLine();
15486                 ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
15487                 if (node_open) ImGui::TreePop();
15488                 return;
15489             }
15490 
15491             ImDrawList* fg_draw_list = viewport ? GetForegroundDrawList(viewport) : NULL; // Render additional visuals into the top-most draw list
15492             if (window && fg_draw_list && ImGui::IsItemHovered())
15493                 fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
15494             if (!node_open)
15495                 return;
15496 
15497             if (window && !window->WasActive)
15498                 ImGui::TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!");
15499 
15500             unsigned int elem_offset = 0;
15501             for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
15502             {
15503                 if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0)
15504                     continue;
15505                 if (pcmd->UserCallback)
15506                 {
15507                     ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
15508                     continue;
15509                 }
15510 
15511                 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
15512                 char buf[300];
15513                 ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d triangles, Tex 0x%p, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
15514                     pcmd->ElemCount / 3, (void*)(intptr_t)pcmd->TextureId,
15515                     pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
15516                 bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf);
15517                 if (ImGui::IsItemHovered() && (show_drawcmd_mesh || show_drawcmd_aabb) && fg_draw_list)
15518                     NodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, elem_offset, show_drawcmd_mesh, show_drawcmd_aabb);
15519                 if (!pcmd_node_open)
15520                     continue;
15521 
15522                 // Calculate approximate coverage area (touched pixel count)
15523                 // This will be in pixels squared as long there's no post-scaling happening to the renderer output.
15524                 float total_area = 0.0f;
15525                 for (unsigned int base_idx = elem_offset; base_idx < (elem_offset + pcmd->ElemCount); base_idx += 3)
15526                 {
15527                     ImVec2 triangle[3];
15528                     for (int n = 0; n < 3; n++)
15529                         triangle[n] = draw_list->VtxBuffer[idx_buffer ? idx_buffer[base_idx + n] : (base_idx + n)].pos;
15530                     total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]);
15531                 }
15532 
15533                 // Display vertex information summary. Hover to get all triangles drawn in wire-frame
15534                 ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area);
15535                 ImGui::Selectable(buf);
15536                 if (ImGui::IsItemHovered() && fg_draw_list)
15537                     NodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, elem_offset, true, false);
15538 
15539                 // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
15540                 ImGuiListClipper clipper(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
15541                 while (clipper.Step())
15542                     for (int prim = clipper.DisplayStart, idx_i = elem_offset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++)
15543                     {
15544                         char* buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf);
15545                         ImVec2 triangle[3];
15546                         for (int n = 0; n < 3; n++, idx_i++)
15547                         {
15548                             ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[idx_i] : idx_i];
15549                             triangle[n] = v.pos;
15550                             buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n",
15551                                 (n == 0) ? "Vert:" : "     ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
15552                         }
15553 
15554                         ImGui::Selectable(buf, false);
15555                         if (fg_draw_list && ImGui::IsItemHovered())
15556                         {
15557                             ImDrawListFlags backup_flags = fg_draw_list->Flags;
15558                             fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
15559                             fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255,255,0,255), true, 1.0f);
15560                             fg_draw_list->Flags = backup_flags;
15561                         }
15562                     }
15563                 ImGui::TreePop();
15564             }
15565             ImGui::TreePop();
15566         }
15567 
15568         static void NodeColumns(const ImGuiColumns* columns)
15569         {
15570             if (!ImGui::TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
15571                 return;
15572             ImGui::BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX);
15573             for (int column_n = 0; column_n < columns->Columns.Size; column_n++)
15574                 ImGui::BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, GetColumnOffsetFromNorm(columns, columns->Columns[column_n].OffsetNorm));
15575             ImGui::TreePop();
15576         }
15577 
15578         static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label)
15579         {
15580             if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size))
15581                 return;
15582             ImGui::Text("(In front-to-back order:)");
15583             for (int i = windows.Size - 1; i >= 0; i--) // Iterate front to back
15584             {
15585                 ImGui::PushID(windows[i]);
15586                 Funcs::NodeWindow(windows[i], "Window");
15587                 ImGui::PopID();
15588             }
15589             ImGui::TreePop();
15590         }
15591 
15592         static void NodeWindow(ImGuiWindow* window, const char* label)
15593         {
15594             if (window == NULL)
15595             {
15596                 ImGui::BulletText("%s: NULL", label);
15597                 return;
15598             }
15599 
15600             ImGuiContext& g = *GImGui;
15601             const bool is_active = window->WasActive;
15602             ImGuiTreeNodeFlags tree_node_flags = (window == g.NavWindow) ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None;
15603             if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
15604             const bool open = ImGui::TreeNodeEx(label, tree_node_flags, "%s '%s'%s", label, window->Name, is_active ? "" : " *Inactive*");
15605             if (!is_active) { PopStyleColor(); }
15606             if (ImGui::IsItemHovered() && is_active)
15607                 ImGui::GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
15608             if (!open)
15609                 return;
15610 
15611             if (window->MemoryCompacted)
15612                 ImGui::TextDisabled("Note: some memory buffers have been compacted/freed.");
15613 
15614             ImGuiWindowFlags flags = window->Flags;
15615             NodeDrawList(window, window->Viewport, window->DrawList, "DrawList");
15616             ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y);
15617             ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
15618                 (flags & ImGuiWindowFlags_ChildWindow)  ? "Child " : "",      (flags & ImGuiWindowFlags_Tooltip)     ? "Tooltip "   : "",  (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
15619                 (flags & ImGuiWindowFlags_Modal)        ? "Modal " : "",      (flags & ImGuiWindowFlags_ChildMenu)   ? "ChildMenu " : "",  (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
15620                 (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
15621             ImGui::BulletText("WindowClassId: 0x%08X", window->WindowClass.ClassId);
15622             ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y, window->ScrollbarX ? "X" : "", window->ScrollbarY ? "Y" : "");
15623             ImGui::BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
15624             ImGui::BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems);
15625             ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask);
15626             ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
15627             if (!window->NavRectRel[0].IsInverted())
15628                 ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y);
15629             else
15630                 ImGui::BulletText("NavRectRel[0]: <None>");
15631             ImGui::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);
15632             ImGui::BulletText("ViewportMonitor: %d", window->Viewport ? window->Viewport->PlatformMonitor : -1);
15633             ImGui::BulletText("DockId: 0x%04X, DockOrder: %d, Act: %d, Vis: %d", window->DockId, window->DockOrder, window->DockIsActive, window->DockTabIsVisible);
15634             if (window->DockNode || window->DockNodeAsHost)
15635                 NodeDockNode(window->DockNodeAsHost ? window->DockNodeAsHost : window->DockNode, window->DockNodeAsHost ? "DockNodeAsHost" : "DockNode");
15636             if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
15637             if (window->RootWindowDockStop != window->RootWindow) NodeWindow(window->RootWindowDockStop, "RootWindowDockStop");
15638             if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow");
15639             if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
15640             if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
15641             {
15642                 for (int n = 0; n < window->ColumnsStorage.Size; n++)
15643                     NodeColumns(&window->ColumnsStorage[n]);
15644                 ImGui::TreePop();
15645             }
15646             NodeStorage(&window->StateStorage, "Storage");
15647             ImGui::TreePop();
15648         }
15649 
15650         static void NodeWindowSettings(ImGuiWindowSettings* settings)
15651         {
15652             ImGui::Text("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d",
15653                 settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed);
15654         }
15655 
15656         static void NodeViewport(ImGuiViewportP* viewport)
15657         {
15658             ImGui::SetNextItemOpen(true, ImGuiCond_Once);
15659             if (ImGui::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"))
15660             {
15661                 ImGuiWindowFlags flags = viewport->Flags;
15662                 ImGui::BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Offset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f\nMonitor: %d, DpiScale: %.0f%%",
15663                     viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y,
15664                     viewport->WorkOffsetMin.x, viewport->WorkOffsetMin.y, viewport->WorkOffsetMax.x, viewport->WorkOffsetMax.y,
15665                     viewport->PlatformMonitor, viewport->DpiScale * 100.0f);
15666                 if (viewport->Idx > 0) { ImGui::SameLine(); if (ImGui::SmallButton("Reset Pos")) { viewport->Pos = ImVec2(200,200); if (viewport->Window) viewport->Window->Pos = ImVec2(200,200); } }
15667                 ImGui::BulletText("Flags: 0x%04X =%s%s%s%s%s%s%s", viewport->Flags,
15668                     (flags & ImGuiViewportFlags_CanHostOtherWindows) ? " CanHostOtherWindows" : "", (flags & ImGuiViewportFlags_NoDecoration) ? " NoDecoration" : "",
15669                     (flags & ImGuiViewportFlags_NoFocusOnAppearing)  ? " NoFocusOnAppearing"  : "", (flags & ImGuiViewportFlags_NoInputs)     ? " NoInputs"     : "",
15670                     (flags & ImGuiViewportFlags_NoRendererClear)     ? " NoRendererClear"     : "", (flags & ImGuiViewportFlags_Minimized)    ? " Minimized"    : "",
15671                     (flags & ImGuiViewportFlags_NoAutoMerge)         ? " NoAutoMerge"         : "");
15672                 for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++)
15673                     for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++)
15674                         Funcs::NodeDrawList(NULL, viewport, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList");
15675                 ImGui::TreePop();
15676             }
15677         }
15678 
15679         static void NodeDockNode(ImGuiDockNode* node, const char* label)
15680         {
15681             ImGuiContext& g = *GImGui;
15682             const bool is_alive = (g.FrameCount - node->LastFrameAlive < 2);    // Submitted with ImGuiDockNodeFlags_KeepAliveOnly
15683             const bool is_active = (g.FrameCount - node->LastFrameActive < 2);  // Submitted
15684             if (!is_alive) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
15685             bool open;
15686             if (node->Windows.Size > 0)
15687                 open = ImGui::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");
15688             else
15689                 open = ImGui::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");
15690             if (!is_alive) { PopStyleColor(); }
15691             if (is_active && ImGui::IsItemHovered())
15692                 GetForegroundDrawList(node->HostWindow ? node->HostWindow : node->VisibleWindow)->AddRect(node->Pos, node->Pos + node->Size, IM_COL32(255, 255, 0, 255));
15693             if (open)
15694             {
15695                 IM_ASSERT(node->ChildNodes[0] == NULL || node->ChildNodes[0]->ParentNode == node);
15696                 IM_ASSERT(node->ChildNodes[1] == NULL || node->ChildNodes[1]->ParentNode == node);
15697                 ImGui::BulletText("Pos (%.0f,%.0f), Size (%.0f, %.0f) Ref (%.0f, %.0f)",
15698                     node->Pos.x, node->Pos.y, node->Size.x, node->Size.y, node->SizeRef.x, node->SizeRef.y);
15699                 NodeWindow(node->HostWindow, "HostWindow");
15700                 NodeWindow(node->VisibleWindow, "VisibleWindow");
15701                 ImGui::BulletText("SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X", node->SelectedTabId, node->LastFocusedNodeId);
15702                 ImGui::BulletText("Misc:%s%s%s%s%s",
15703                     node->IsDockSpace() ? " IsDockSpace" : "",
15704                     node->IsCentralNode() ? " IsCentralNode" : "",
15705                     is_alive ? " IsAlive" : "", is_active ? " IsActive" : "",
15706                     node->WantLockSizeOnce ? " WantLockSizeOnce" : "");
15707                 if (ImGui::TreeNode("flags", "LocalFlags: 0x%04X SharedFlags: 0x%04X", node->LocalFlags, node->SharedFlags))
15708                 {
15709                     ImGui::CheckboxFlags("LocalFlags: NoDocking", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoDocking);
15710                     ImGui::CheckboxFlags("LocalFlags: NoSplit", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoSplit);
15711                     ImGui::CheckboxFlags("LocalFlags: NoResize", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoResize);
15712                     ImGui::CheckboxFlags("LocalFlags: NoResizeX", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoResizeX);
15713                     ImGui::CheckboxFlags("LocalFlags: NoResizeY", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoResizeY);
15714                     ImGui::CheckboxFlags("LocalFlags: NoTabBar", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoTabBar);
15715                     ImGui::CheckboxFlags("LocalFlags: HiddenTabBar", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_HiddenTabBar);
15716                     ImGui::CheckboxFlags("LocalFlags: NoWindowMenuButton", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoWindowMenuButton);
15717                     ImGui::CheckboxFlags("LocalFlags: NoCloseButton", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoCloseButton);
15718                     ImGui::TreePop();
15719                 }
15720                 if (node->ParentNode)
15721                     NodeDockNode(node->ParentNode, "ParentNode");
15722                 if (node->ChildNodes[0])
15723                     NodeDockNode(node->ChildNodes[0], "Child[0]");
15724                 if (node->ChildNodes[1])
15725                     NodeDockNode(node->ChildNodes[1], "Child[1]");
15726                 if (node->TabBar)
15727                     NodeTabBar(node->TabBar);
15728                 ImGui::TreePop();
15729             }
15730         }
15731 
15732         static void NodeTabBar(ImGuiTabBar* tab_bar)
15733         {
15734             // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
15735             char buf[256];
15736             char* p = buf;
15737             const char* buf_end = buf + IM_ARRAYSIZE(buf);
15738             const bool is_active = (tab_bar->PrevFrameVisible >= ImGui::GetFrameCount() - 2);
15739             p += ImFormatString(p, buf_end - p, "Tab Bar 0x%08X (%d tabs)%s", tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*");
15740             if (tab_bar->Flags & ImGuiTabBarFlags_DockNode)
15741             {
15742                 p += ImFormatString(p, buf_end - p, "  { ");
15743                 for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++)
15744                     p += ImFormatString(p, buf_end - p, "%s'%s'", tab_n > 0 ? ", " : "", tab_bar->Tabs[tab_n].Window->Name);
15745                 p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } ");
15746             }
15747             if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
15748             bool open = ImGui::TreeNode(tab_bar, "%s", buf);
15749             if (!is_active) { PopStyleColor(); }
15750             if (open)
15751             {
15752                 for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
15753                 {
15754                     const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
15755                     ImGui::PushID(tab);
15756                     if (ImGui::SmallButton("<")) { TabBarQueueChangeTabOrder(tab_bar, tab, -1); } ImGui::SameLine(0, 2);
15757                     if (ImGui::SmallButton(">")) { TabBarQueueChangeTabOrder(tab_bar, tab, +1); } ImGui::SameLine();
15758                     ImGui::Text("%02d%c Tab 0x%08X '%s'", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->Window || tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "");
15759                     ImGui::PopID();
15760                 }
15761                 ImGui::TreePop();
15762             }
15763         }
15764 
15765         static void NodeStorage(ImGuiStorage* storage, const char* label)
15766         {
15767             if (!ImGui::TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes()))
15768                 return;
15769             for (int n = 0; n < storage->Data.Size; n++)
15770             {
15771                 const ImGuiStorage::ImGuiStoragePair& p = storage->Data[n];
15772                 ImGui::BulletText("Key 0x%08X Value { i: %d }", p.key, p.val_i); // Important: we currently don't store a type, real value may not be integer.
15773             }
15774             ImGui::TreePop();
15775         }
15776     };
15777 
15778     // Tools
15779     if (ImGui::TreeNode("Tools"))
15780     {
15781         // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
15782         if (ImGui::Button("Item Picker.."))
15783             ImGui::DebugStartItemPicker();
15784         ImGui::SameLine();
15785         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.");
15786 
15787         ImGui::Checkbox("Show windows begin order", &show_windows_begin_order);
15788         ImGui::Checkbox("Show windows rectangles", &show_windows_rects);
15789         ImGui::SameLine();
15790         ImGui::SetNextItemWidth(ImGui::GetFontSize() * 12);
15791         show_windows_rects |= ImGui::Combo("##show_windows_rect_type", &show_windows_rect_type, wrt_rects_names, WRT_Count, WRT_Count);
15792         if (show_windows_rects && g.NavWindow)
15793         {
15794             ImGui::BulletText("'%s':", g.NavWindow->Name);
15795             ImGui::Indent();
15796             for (int rect_n = 0; rect_n < WRT_Count; rect_n++)
15797             {
15798                 ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n);
15799                 ImGui::Text("(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), wrt_rects_names[rect_n]);
15800             }
15801             ImGui::Unindent();
15802         }
15803         ImGui::Checkbox("Show mesh when hovering ImDrawCmd", &show_drawcmd_mesh);
15804         ImGui::Checkbox("Show bounding boxes when hovering ImDrawCmd", &show_drawcmd_aabb);
15805         ImGui::TreePop();
15806     }
15807 
15808     // Contents
15809     Funcs::NodeWindows(g.Windows, "Windows");
15810     //Funcs::NodeWindows(g.WindowsFocusOrder, "WindowsFocusOrder");
15811     if (ImGui::TreeNode("Viewport", "Viewports (%d)", g.Viewports.Size))
15812     {
15813         ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing());
15814         ImGui::ShowViewportThumbnails();
15815         ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing());
15816         bool open = ImGui::TreeNode("Monitors", "Monitors (%d)", g.PlatformIO.Monitors.Size);
15817         ImGui::SameLine();
15818         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.");
15819         if (open)
15820         {
15821             for (int i = 0; i < g.PlatformIO.Monitors.Size; i++)
15822             {
15823                 const ImGuiPlatformMonitor& mon = g.PlatformIO.Monitors[i];
15824                 ImGui::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)",
15825                     i, mon.DpiScale * 100.0f,
15826                     mon.MainPos.x, mon.MainPos.y, mon.MainPos.x + mon.MainSize.x, mon.MainPos.y + mon.MainSize.y, mon.MainSize.x, mon.MainSize.y,
15827                     mon.WorkPos.x, mon.WorkPos.y, mon.WorkPos.x + mon.WorkSize.x, mon.WorkPos.y + mon.WorkSize.y, mon.WorkSize.x, mon.WorkSize.y);
15828             }
15829             ImGui::TreePop();
15830         }
15831         for (int i = 0; i < g.Viewports.Size; i++)
15832             Funcs::NodeViewport(g.Viewports[i]);
15833         ImGui::TreePop();
15834     }
15835 
15836     // Details for Popups
15837     if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
15838     {
15839         for (int i = 0; i < g.OpenPopupStack.Size; i++)
15840         {
15841             ImGuiWindow* window = g.OpenPopupStack[i].Window;
15842             ImGui::BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenPopupStack[i].PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : "");
15843         }
15844         ImGui::TreePop();
15845     }
15846 
15847     // Details for TabBars
15848     if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetSize()))
15849     {
15850         for (int n = 0; n < g.TabBars.GetSize(); n++)
15851             Funcs::NodeTabBar(g.TabBars.GetByIndex(n));
15852         ImGui::TreePop();
15853     }
15854 
15855     // Details for Tables
15856     IM_UNUSED(trt_rects_names);
15857     IM_UNUSED(show_tables_rects);
15858     IM_UNUSED(show_tables_rect_type);
15859 #ifdef IMGUI_HAS_TABLE
15860     if (ImGui::TreeNode("Tables", "Tables (%d)", g.Tables.GetSize()))
15861     {
15862         for (int n = 0; n < g.Tables.GetSize(); n++)
15863             Funcs::NodeTable(g.Tables.GetByIndex(n));
15864         ImGui::TreePop();
15865     }
15866 #endif // #ifdef IMGUI_HAS_TABLE
15867 
15868     // Details for Docking
15869 #ifdef IMGUI_HAS_DOCK
15870     if (ImGui::TreeNode("Dock nodes"))
15871     {
15872         static bool root_nodes_only = true;
15873         ImGuiDockContext* dc = &g.DockContext;
15874         ImGui::Checkbox("List root nodes", &root_nodes_only);
15875         ImGui::Checkbox("Ctrl shows window dock info", &show_docking_nodes);
15876         if (ImGui::SmallButton("Clear nodes")) { DockContextClearNodes(&g, 0, true); }
15877         ImGui::SameLine();
15878         if (ImGui::SmallButton("Rebuild all")) { dc->WantFullRebuild = true; }
15879         for (int n = 0; n < dc->Nodes.Data.Size; n++)
15880             if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
15881                 if (!root_nodes_only || node->IsRootNode())
15882                     Funcs::NodeDockNode(node, "Node");
15883         ImGui::TreePop();
15884     }
15885 #endif // #ifdef IMGUI_HAS_DOCK
15886 
15887     // Settings
15888     if (ImGui::TreeNode("Settings"))
15889     {
15890         if (ImGui::SmallButton("Clear"))
15891             ImGui::ClearIniSettings();
15892         ImGui::SameLine();
15893         if (ImGui::SmallButton("Save to memory"))
15894             ImGui::SaveIniSettingsToMemory();
15895         ImGui::SameLine();
15896         if (ImGui::SmallButton("Save to disk"))
15897             ImGui::SaveIniSettingsToDisk(g.IO.IniFilename);
15898         ImGui::SameLine();
15899         if (g.IO.IniFilename)
15900             ImGui::Text("\"%s\"", g.IO.IniFilename);
15901         else
15902             ImGui::TextUnformatted("<NULL>");
15903         ImGui::Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer);
15904         if (ImGui::TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size))
15905         {
15906             for (int n = 0; n < g.SettingsHandlers.Size; n++)
15907                 ImGui::BulletText("%s", g.SettingsHandlers[n].TypeName);
15908             ImGui::TreePop();
15909         }
15910         if (ImGui::TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size()))
15911         {
15912             for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
15913                 Funcs::NodeWindowSettings(settings);
15914             ImGui::TreePop();
15915         }
15916 
15917 #ifdef IMGUI_HAS_TABLE
15918         if (ImGui::TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size()))
15919         {
15920             for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))
15921                 Funcs::NodeTableSettings(settings);
15922             ImGui::TreePop();
15923         }
15924 #endif // #ifdef IMGUI_HAS_TABLE
15925 
15926 #ifdef IMGUI_HAS_DOCK
15927         if (ImGui::TreeNode("SettingsDocking", "Settings packed data: Docking"))
15928         {
15929             ImGuiDockContext* dc = &g.DockContext;
15930             ImGui::Text("In SettingsWindows:");
15931             for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
15932                 if (settings->DockId != 0)
15933                     ImGui::BulletText("Window '%s' -> DockId %08X", settings->GetName(), settings->DockId);
15934             ImGui::Text("In SettingsNodes:");
15935             for (int n = 0; n < dc->NodesSettings.Size; n++)
15936             {
15937                 ImGuiDockNodeSettings* settings = &dc->NodesSettings[n];
15938                 const char* selected_tab_name = NULL;
15939                 if (settings->SelectedWindowId)
15940                 {
15941                     if (ImGuiWindow* window = FindWindowByID(settings->SelectedWindowId))
15942                         selected_tab_name = window->Name;
15943                     else if (ImGuiWindowSettings* window_settings = FindWindowSettings(settings->SelectedWindowId))
15944                         selected_tab_name = window_settings->GetName();
15945                 }
15946                 ImGui::BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings->ID, settings->ParentNodeId, settings->SelectedWindowId, selected_tab_name ? selected_tab_name : settings->SelectedWindowId ? "N/A" : "");
15947             }
15948             ImGui::TreePop();
15949         }
15950 #endif // #ifdef IMGUI_HAS_DOCK
15951 
15952         if (ImGui::TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size()))
15953         {
15954             ImGui::InputTextMultiline("##Ini", (char*)(void*)g.SettingsIniData.c_str(), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, 0.0f), ImGuiInputTextFlags_ReadOnly);
15955             ImGui::TreePop();
15956         }
15957         ImGui::TreePop();
15958     }
15959 
15960     // Misc Details
15961     if (ImGui::TreeNode("Internal state"))
15962     {
15963         const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
15964 
15965         ImGui::Text("WINDOWING");
15966         ImGui::Indent();
15967         ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
15968         ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
15969         ImGui::Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL");
15970         ImGui::Text("HoveredDockNode: 0x%08X", g.HoveredDockNode ? g.HoveredDockNode->ID : 0);
15971         ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
15972         ImGui::Text("MouseViewport: 0x%08X (UserHovered 0x%08X, LastHovered 0x%08X)", g.MouseViewport->ID, g.IO.MouseHoveredViewport, g.MouseLastHoveredViewport ? g.MouseLastHoveredViewport->ID : 0);
15973         ImGui::Unindent();
15974 
15975         ImGui::Text("ITEMS");
15976         ImGui::Indent();
15977         ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, input_source_names[g.ActiveIdSource]);
15978         ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
15979         ImGui::Text("HoveredId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not
15980         ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
15981         ImGui::Unindent();
15982 
15983         ImGui::Text("NAV,FOCUS");
15984         ImGui::Indent();
15985         ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
15986         ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
15987         ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
15988         ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
15989         ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
15990         ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
15991         ImGui::Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId);
15992         ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
15993         ImGui::Unindent();
15994 
15995         ImGui::TreePop();
15996     }
15997 
15998     // Overlay: Display windows Rectangles and Begin Order
15999     if (show_windows_rects || show_windows_begin_order)
16000     {
16001         for (int n = 0; n < g.Windows.Size; n++)
16002         {
16003             ImGuiWindow* window = g.Windows[n];
16004             if (!window->WasActive)
16005                 continue;
16006             ImDrawList* draw_list = GetForegroundDrawList(window);
16007             if (show_windows_rects)
16008             {
16009                 ImRect r = Funcs::GetWindowRect(window, show_windows_rect_type);
16010                 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
16011             }
16012             if (show_windows_begin_order && !(window->Flags & ImGuiWindowFlags_ChildWindow))
16013             {
16014                 char buf[32];
16015                 ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
16016                 float font_size = ImGui::GetFontSize();
16017                 draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
16018                 draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf);
16019             }
16020         }
16021     }
16022 
16023 #ifdef IMGUI_HAS_TABLE
16024     // Overlay: Display Tables Rectangles
16025     if (show_tables_rects)
16026     {
16027         for (int table_n = 0; table_n < g.Tables.GetSize(); table_n++)
16028         {
16029             ImGuiTable* table = g.Tables.GetByIndex(table_n);
16030         }
16031     }
16032 #endif // #ifdef IMGUI_HAS_TABLE
16033 
16034 #ifdef IMGUI_HAS_DOCK
16035     // Overlay: Display Docking info
16036     if (show_docking_nodes && g.IO.KeyCtrl && g.HoveredDockNode)
16037     {
16038         char buf[64] = "";
16039         char* p = buf;
16040         ImGuiDockNode* node = g.HoveredDockNode;
16041         ImDrawList* overlay_draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList((ImGuiViewportP*)GetMainViewport());
16042         p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "DockId: %X%s\n", node->ID, node->IsCentralNode() ? " *CentralNode*" : "");
16043         p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "WindowClass: %08X\n", node->WindowClass.ClassId);
16044         p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "Size: (%.0f, %.0f)\n", node->Size.x, node->Size.y);
16045         p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "SizeRef: (%.0f, %.0f)\n", node->SizeRef.x, node->SizeRef.y);
16046         int depth = DockNodeGetDepth(node);
16047         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));
16048         ImVec2 pos = node->Pos + ImVec2(3, 3) * (float)depth;
16049         overlay_draw_list->AddRectFilled(pos - ImVec2(1, 1), pos + CalcTextSize(buf) + ImVec2(1, 1), IM_COL32(200, 100, 100, 255));
16050         overlay_draw_list->AddText(NULL, 0.0f, pos, IM_COL32(255, 255, 255, 255), buf);
16051     }
16052 #endif // #ifdef IMGUI_HAS_DOCK
16053 
16054     ImGui::End();
16055 }
16056 
16057 #else
16058 
ShowMetricsWindow(bool *)16059 void ImGui::ShowMetricsWindow(bool*) { }
16060 
16061 #endif
16062 
16063 //-----------------------------------------------------------------------------
16064 
16065 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
16066 // 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.
16067 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
16068 #include "imgui_user.inl"
16069 #endif
16070 
16071 //-----------------------------------------------------------------------------
16072 
16073 #endif // #ifndef IMGUI_DISABLE
16074