• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // dear imgui, v1.67
2 // (main code and documentation)
3 
4 // Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code.
5 // Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase.
6 // Get latest version at https://github.com/ocornut/imgui
7 // Releases change-log at https://github.com/ocornut/imgui/releases
8 // Technical Support for Getting Started https://discourse.dearimgui.org/c/getting-started
9 // Gallery (please post your screenshots/video there!): https://github.com/ocornut/imgui/issues/1269
10 
11 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
12 // See LICENSE.txt for copyright and licensing details (standard MIT License).
13 // This library is free but I need your support to sustain development and maintenance.
14 // Businesses: you can support continued maintenance and development via support contracts or sponsoring, see docs/README.
15 // Individuals: you can support continued maintenance and development via donations or Patreon https://www.patreon.com/imgui.
16 
17 // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
18 // Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
19 // modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
20 // come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
21 // to a better solution or official support for them.
22 
23 /*
24 
25 Index of this file:
26 
27 DOCUMENTATION
28 
29 - MISSION STATEMENT
30 - END-USER GUIDE
31 - PROGRAMMER GUIDE (read me!)
32   - Read first.
33   - How to update to a newer version of Dear ImGui.
34   - Getting started with integrating Dear ImGui in your code/engine.
35   - This is how a simple application may look like (2 variations).
36   - This is how a simple rendering function may look like.
37   - Using gamepad/keyboard navigation controls.
38 - API BREAKING CHANGES (read me when you update!)
39 - FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
40   - How can I tell whether to dispatch mouse/keyboard to imgui or to my application?
41   - How can I display an image? What is ImTextureID, how does it works?
42   - How can I have multiple widgets with the same label or with an empty label? A primer on labels and the ID Stack.
43   - How can I use my own math types instead of ImVec2/ImVec4?
44   - How can I load a different font than the default?
45   - How can I easily use icons in my application?
46   - How can I load multiple fonts?
47   - How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic?
48   - How can I interact with standard C++ types (such as std::string and std::vector)?
49   - How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
50   - How can I use Dear ImGui on a platform that doesn't have a mouse or a keyboard? (input share, remoting, gamepad)
51   - I integrated Dear ImGui in my engine and the text or lines are blurry..
52   - I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
53   - How can I help?
54 
55 CODE
56 (search for "[SECTION]" in the code to find them)
57 
58 // [SECTION] FORWARD DECLARATIONS
59 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
60 // [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
61 // [SECTION] MISC HELPER/UTILITIES (Maths, String, Format, Hash, File functions)
62 // [SECTION] MISC HELPER/UTILITIES (ImText* functions)
63 // [SECTION] MISC HELPER/UTILITIES (Color functions)
64 // [SECTION] ImGuiStorage
65 // [SECTION] ImGuiTextFilter
66 // [SECTION] ImGuiTextBuffer
67 // [SECTION] ImGuiListClipper
68 // [SECTION] RENDER HELPERS
69 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
70 // [SECTION] TOOLTIPS
71 // [SECTION] POPUPS
72 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
73 // [SECTION] COLUMNS
74 // [SECTION] DRAG AND DROP
75 // [SECTION] LOGGING/CAPTURING
76 // [SECTION] SETTINGS
77 // [SECTION] PLATFORM DEPENDENT HELPERS
78 // [SECTION] METRICS/DEBUG WINDOW
79 
80 */
81 
82 //-----------------------------------------------------------------------------
83 // DOCUMENTATION
84 //-----------------------------------------------------------------------------
85 
86 /*
87 
88  MISSION STATEMENT
89  =================
90 
91  - Easy to use to create code-driven and data-driven tools.
92  - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
93  - Easy to hack and improve.
94  - Minimize screen real-estate usage.
95  - Minimize setup and maintenance.
96  - Minimize state storage on user side.
97  - Portable, minimize dependencies, run on target (consoles, phones, etc.).
98  - Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window,.
99    opening a tree node for the first time, etc. but a typical frame should not allocate anything).
100 
101  Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
102  - Doesn't look fancy, doesn't animate.
103  - Limited layout features, intricate layouts are typically crafted in code.
104 
105 
106  END-USER GUIDE
107  ==============
108 
109  - Double-click on title bar to collapse window.
110  - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin().
111  - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents).
112  - Click and drag on any empty space to move window.
113  - TAB/SHIFT+TAB to cycle through keyboard editable fields.
114  - CTRL+Click on a slider or drag box to input value as text.
115  - Use mouse wheel to scroll.
116  - Text editor:
117    - Hold SHIFT or use mouse to select text.
118    - CTRL+Left/Right to word jump.
119    - CTRL+Shift+Left/Right to select words.
120    - CTRL+A our Double-Click to select all.
121    - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
122    - CTRL+Z,CTRL+Y to undo/redo.
123    - ESCAPE to revert text to its original value.
124    - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
125    - Controls are automatically adjusted for OSX to match standard OSX text editing operations.
126  - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard.
127  - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://goo.gl/9LgVZW
128 
129 
130  PROGRAMMER GUIDE
131  ================
132 
133  READ FIRST:
134 
135  - Read the FAQ below this section!
136  - 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
137    or destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, less bugs.
138  - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
139  - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
140  - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
141    You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links docs/README.md.
142  - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
143    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,
144    where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
145  - Our origin are on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
146  - This codebase is also optimized to yield decent performances with typical "Debug" builds settings.
147  - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).
148    If you get an assert, read the messages and comments around the assert.
149  - 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.
150  - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
151    See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
152    However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase.
153  - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!).
154 
155  HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI:
156 
157  - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
158  - Or maintain your own branch where you have imconfig.h modified.
159  - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
160    If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
161    from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
162    likely be a comment about it. Please report any issue to the GitHub page!
163  - Try to keep your copy of dear imgui reasonably up to date.
164 
165  GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE:
166 
167  - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
168  - Add the Dear ImGui source files to your projects or using your preferred build system.
169    It is recommended you build and statically link the .cpp files as part of your project and not as shared library (DLL).
170  - You can later customize the imconfig.h file to tweak some compile-time behavior, such as integrating imgui types with your own maths types.
171  - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
172  - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
173    Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
174    phases of your own application. All rendering informatioe are stored into command-lists that you will retrieve after calling ImGui::Render().
175  - Refer to the bindings and demo applications in the examples/ folder for instruction on how to setup your code.
176  - 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.
177 
178  HOW A SIMPLE APPLICATION MAY LOOK LIKE:
179  EXHIBIT 1: USING THE EXAMPLE BINDINGS (imgui_impl_XXX.cpp files from the examples/ folder).
180 
181      // Application init: create a dear imgui context, setup some options, load fonts
182      ImGui::CreateContext();
183      ImGuiIO& io = ImGui::GetIO();
184      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
185      // TODO: Fill optional fields of the io structure later.
186      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
187 
188      // Initialize helper Platform and Renderer bindings (here we are using imgui_impl_win32 and imgui_impl_dx11)
189      ImGui_ImplWin32_Init(hwnd);
190      ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
191 
192      // Application main loop
193      while (true)
194      {
195          // Feed inputs to dear imgui, start new frame
196          ImGui_ImplDX11_NewFrame();
197          ImGui_ImplWin32_NewFrame();
198          ImGui::NewFrame();
199 
200          // Any application code here
201          ImGui::Text("Hello, world!");
202 
203          // Render dear imgui into screen
204          ImGui::Render();
205          ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
206          g_pSwapChain->Present(1, 0);
207      }
208 
209      // Shutdown
210      ImGui_ImplDX11_Shutdown();
211      ImGui_ImplWin32_Shutdown();
212      ImGui::DestroyContext();
213 
214  HOW A SIMPLE APPLICATION MAY LOOK LIKE:
215  EXHIBIT 2: IMPLEMENTING CUSTOM BINDING / CUSTOM ENGINE.
216 
217      // Application init: create a dear imgui context, setup some options, load fonts
218      ImGui::CreateContext();
219      ImGuiIO& io = ImGui::GetIO();
220      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
221      // TODO: Fill optional fields of the io structure later.
222      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
223 
224      // Build and load the texture atlas into a texture
225      // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
226      int width, height;
227      unsigned char* pixels = NULL;
228      io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
229 
230      // At this point you've got the texture data and you need to upload that your your graphic system:
231      // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
232      // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ below for details about ImTextureID.
233      MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
234      io.Fonts->TexID = (void*)texture;
235 
236      // Application main loop
237      while (true)
238      {
239         // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
240         // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform bindings)
241         io.DeltaTime = 1.0f/60.0f;              // set the time elapsed since the previous frame (in seconds)
242         io.DisplaySize.x = 1920.0f;             // set the current display width
243         io.DisplaySize.y = 1280.0f;             // set the current display height here
244         io.MousePos = my_mouse_pos;             // set the mouse position
245         io.MouseDown[0] = my_mouse_buttons[0];  // set the mouse button states
246         io.MouseDown[1] = my_mouse_buttons[1];
247 
248         // Call NewFrame(), after this point you can use ImGui::* functions anytime
249         // (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use imgui everywhere)
250         ImGui::NewFrame();
251 
252         // Most of your application code here
253         ImGui::Text("Hello, world!");
254         MyGameUpdate(); // may use any ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
255         MyGameRender(); // may use any ImGui functions as well!
256 
257         // Render imgui, swap buffers
258         // (You want to try calling EndFrame/Render as late as you can, to be able to use imgui in your own game rendering code)
259         ImGui::EndFrame();
260         ImGui::Render();
261         ImDrawData* draw_data = ImGui::GetDrawData();
262         MyImGuiRenderFunction(draw_data);
263         SwapBuffers();
264      }
265 
266      // Shutdown
267      ImGui::DestroyContext();
268 
269  HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE:
270 
271     void void MyImGuiRenderFunction(ImDrawData* draw_data)
272     {
273        // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
274        // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
275        // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
276        // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
277        for (int n = 0; n < draw_data->CmdListsCount; n++)
278        {
279           const ImDrawList* cmd_list = draw_data->CmdLists[n];
280           const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;  // vertex buffer generated by ImGui
281           const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;   // index buffer generated by ImGui
282           for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
283           {
284              const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
285              if (pcmd->UserCallback)
286              {
287                  pcmd->UserCallback(cmd_list, pcmd);
288              }
289              else
290              {
291                  // The texture for the draw call is specified by pcmd->TextureId.
292                  // The vast majority of draw calls will use the imgui texture atlas, which value you have set yourself during initialization.
293                  MyEngineBindTexture((MyTexture*)pcmd->TextureId);
294 
295                  // We are using scissoring to clip some objects. All low-level graphics API should supports it.
296                  // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
297                  //   (some elements visible outside their bounds) but you can fix that once everything else works!
298                  // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize)
299                  //   In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize.
300                  //   However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github),
301                  //   always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
302                  // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
303                  ImVec2 pos = draw_data->DisplayPos;
304                  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));
305 
306                  // Render 'pcmd->ElemCount/3' indexed triangles.
307                  // By default the indices ImDrawIdx are 16-bits, you can change them to 32-bits in imconfig.h if your engine doesn't support 16-bits indices.
308                  MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
309              }
310              idx_buffer += pcmd->ElemCount;
311           }
312        }
313     }
314 
315  - The examples/ folders contains many actual implementation of the pseudo-codes above.
316  - When calling NewFrame(), the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags are updated.
317    They tell you if Dear ImGui intends to use your inputs. When a flag is set you want to hide the corresponding inputs
318    from the rest of your application. In every cases you need to pass on the inputs to imgui. Refer to the FAQ for more information.
319  - Please read the FAQ below!. Amusingly, it is called a FAQ because people frequently run into the same issues!
320 
321  USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
322 
323  - The gamepad/keyboard navigation is fairly functional and keeps being improved.
324  - Gamepad support is particularly useful to use dear imgui on a console system (e.g. PS4, Switch, XB1) without a mouse!
325  - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787
326  - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
327  - Gamepad:
328     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
329     - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
330       Note that io.NavInputs[] is cleared by EndFrame().
331     - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
332          0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
333     - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
334       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.).
335     - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://goo.gl/9LgVZW.
336     - 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
337       to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
338  - Keyboard:
339     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
340       NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
341     - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
342       will be set. For more advanced uses, you may want to read from:
343        - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
344        - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
345        - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions.
346       Please reach out if you think the game vs navigation input sharing could be improved.
347  - Mouse:
348     - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
349     - 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.
350     - 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.
351       Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
352       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.
353       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.
354       (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!)
355       (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
356        to set a boolean to ignore your other external mouse positions until the external source is moved again.)
357 
358 
359  API BREAKING CHANGES
360  ====================
361 
362  Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
363  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.
364  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.
365  You can read releases logs https://github.com/ocornut/imgui/releases for more details.
366 
367  - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
368  - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Keep redirection typedef (will obsolete).
369  - 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.
370  - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
371  - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
372  - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
373  - 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.
374                        If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
375  - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
376  - 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.
377                        NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
378                        Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
379  - 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).
380  - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
381  - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
382  - 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.
383  - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
384  - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
385  - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
386  - 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.).
387                        old binding will still work as is, however prefer using the separated bindings as they will be updated to be multi-viewport conformant.
388                        when adopting new bindings follow the main.cpp code of your preferred examples/ folder to know which functions to call.
389  - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
390  - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
391  - 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.
392                        If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
393                        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.
394                        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.
395  - 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",
396                        consistent with other functions. Kept redirection functions (will obsolete).
397  - 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.
398  - 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).
399  - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
400  - 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.
401  - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
402  - 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.
403  - 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.
404  - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
405                        - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
406                        - removed Shutdown() function, as DestroyContext() serve this purpose.
407                        - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
408                        - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
409                        - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
410  - 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.
411  - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
412  - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
413  - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
414  - 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.
415  - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
416  - 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
417  - 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.
418  - 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.
419  - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
420  - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
421                      - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
422  - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
423  - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
424  - 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.
425  - 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.
426                        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.
427  - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
428  - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
429  - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
430  - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
431  - 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.
432  - 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.
433  - 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.
434                        removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
435  - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
436  - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
437  - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Keep redirection typedef (will obsolete).
438  - 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).
439  - 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)".
440  - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
441                      - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
442                      - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
443  - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
444  - 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.
445  - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame.
446  - 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.
447  - 2017/08/13 (1.51) - renamed ImGuiCol_Columns*** to ImGuiCol_Separator***. Kept redirection enums (will obsolete).
448  - 2017/08/11 (1.51) - renamed ImGuiSetCond_*** types and flags to ImGuiCond_***. Kept redirection enums (will obsolete).
449  - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
450  - 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.
451                      - 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.
452                      - 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))'
453  - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
454  - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
455  - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
456  - 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().
457  - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
458  - 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.
459  - 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.
460  - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
461                        If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you.
462                        If your TitleBg/TitleBgActive alpha was <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
463                        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.
464                            ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col)
465                            {
466                                float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a;
467                                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);
468                            }
469                        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.
470  - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
471  - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
472  - 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).
473  - 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.
474  - 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).
475  - 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)
476  - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
477  - 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.
478  - 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.
479  - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
480  - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
481  - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
482                        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.
483                        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!
484  - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
485  - 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.
486  - 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
487  - 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.
488                        you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
489  - 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.
490                        this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
491                      - 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.
492                      - the signature of the io.RenderDrawListsFn handler has changed!
493                        old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
494                        new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
495                          parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
496                          ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
497                          ImDrawCmd:  'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
498                      - 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.
499                      - 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!
500                      - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
501  - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
502  - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
503  - 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.
504  - 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
505  - 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!
506  - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
507  - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
508  - 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.
509  - 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.
510  - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
511  - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
512  - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
513  - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
514  - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
515  - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
516  - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
517  - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
518  - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
519  - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
520  - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
521  - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
522  - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
523  - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
524  - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
525  - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
526  - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
527               (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
528                        font init:  { const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); <..Upload texture to GPU..>; }
529                        became:     { unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); <..Upload texture to GPU>; io.Fonts->TexId = YourTextureIdentifier; }
530                        you now have more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs.
531                        it is now recommended that you sample the font texture with bilinear interpolation.
532               (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
533               (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
534               (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
535  - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
536  - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
537  - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
538  - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
539  - 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)
540  - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
541  - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
542  - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
543  - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
544  - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
545  - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
546 
547 
548  FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
549  ======================================
550 
551  Q: How can I tell whether to dispatch mouse/keyboard to imgui or to my application?
552  A: You can read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags from the ImGuiIO structure (e.g. if (ImGui::GetIO().WantCaptureMouse) { ... } )
553     - When 'io.WantCaptureMouse' is set, imgui wants to use your mouse state, and you may want to discard/hide the inputs from the rest of your application.
554     - When 'io.WantCaptureKeyboard' is set, imgui wants to use your keyboard state, and you may want to discard/hide the inputs from the rest of your application.
555     - When 'io.WantTextInput' is set to may want to notify your OS to popup an on-screen keyboard, if available (e.g. on a mobile phone, or console OS).
556     Note: you should always pass your mouse/keyboard inputs to imgui, even when the io.WantCaptureXXX flag are set false.
557      This is because imgui needs to detect that you clicked in the void to unfocus its own windows.
558     Note: The 'io.WantCaptureMouse' is more accurate that any attempt to "check if the mouse is hovering a window" (don't do that!).
559      It handle mouse dragging correctly (both dragging that started over your application or over an imgui window) and handle e.g. modal windows blocking inputs.
560      Those flags are updated by ImGui::NewFrame(). Preferably read the flags after calling NewFrame() if you can afford it, but reading them before is also
561      perfectly fine, as the bool toggle fairly rarely. If you have on a touch device, you might find use for an early call to UpdateHoveredWindowAndCaptureFlags().
562     Note: Text input widget releases focus on "Return KeyDown", so the subsequent "Return KeyUp" event that your application receive will typically
563      have 'io.WantCaptureKeyboard=false'. Depending on your application logic it may or not be inconvenient. You might want to track which key-downs
564      were targeted for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.)
565 
566  Q: How can I display an image? What is ImTextureID, how does it works?
567  A: Short explanation:
568     - You may use functions such as ImGui::Image(), ImGui::ImageButton() or lower-level ImDrawList::AddImage() to emit draw calls that will use your own textures.
569     - Actual textures are identified in a way that is up to the user/engine. Those identifiers are stored and passed as ImTextureID (void*) value.
570     - Loading image files from the disk and turning them into a texture is not within the scope of Dear ImGui (for a good reason).
571       Please read documentations or tutorials on your graphics API to understand how to display textures on the screen before moving onward.
572 
573     Long explanation:
574     - Dear ImGui's job is to create "meshes", defined in a renderer-agnostic format made of draw commands and vertices.
575       At the end of the frame those meshes (ImDrawList) will be displayed by your rendering function. They are made up of textured polygons and the code
576       to render them is generally fairly short (a few dozen lines). In the examples/ folder we provide functions for popular graphics API (OpenGL, DirectX, etc.).
577     - Each rendering function decides on a data type to represent "textures". The concept of what is a "texture" is entirely tied to your underlying engine/graphics API.
578       We carry the information to identify a "texture" in the ImTextureID type.
579       ImTextureID is nothing more that a void*, aka 4/8 bytes worth of data: just enough to store 1 pointer or 1 integer of your choice.
580       Dear ImGui doesn't know or understand what you are storing in ImTextureID, it merely pass ImTextureID values until they reach your rendering function.
581     - In the examples/ bindings, for each graphics API binding we decided on a type that is likely to be a good representation for specifying
582       an image from the end-user perspective. This is what the _examples_ rendering functions are using:
583 
584          OpenGL:     ImTextureID = GLuint                       (see ImGui_ImplGlfwGL3_RenderDrawData() function in imgui_impl_glfw_gl3.cpp)
585          DirectX9:   ImTextureID = LPDIRECT3DTEXTURE9           (see ImGui_ImplDX9_RenderDrawData()     function in imgui_impl_dx9.cpp)
586          DirectX11:  ImTextureID = ID3D11ShaderResourceView*    (see ImGui_ImplDX11_RenderDrawData()    function in imgui_impl_dx11.cpp)
587          DirectX12:  ImTextureID = D3D12_GPU_DESCRIPTOR_HANDLE  (see ImGui_ImplDX12_RenderDrawData()    function in imgui_impl_dx12.cpp)
588 
589       For example, in the OpenGL example binding we store raw OpenGL texture identifier (GLuint) inside ImTextureID.
590       Whereas in the DirectX11 example binding we store a pointer to ID3D11ShaderResourceView inside ImTextureID, which is a higher-level structure
591       tying together both the texture and information about its format and how to read it.
592     - If you have a custom engine built over e.g. OpenGL, instead of passing GLuint around you may decide to use a high-level data type to carry information about
593       the texture as well as how to display it (shaders, etc.). The decision of what to use as ImTextureID can always be made better knowing how your codebase
594       is designed. If your engine has high-level data types for "textures" and "material" then you may want to use them.
595       If you are starting with OpenGL or DirectX or Vulkan and haven't built much of a rendering engine over them, keeping the default ImTextureID
596       representation suggested by the example bindings is probably the best choice.
597       (Advanced users may also decide to keep a low-level type in ImTextureID, and use ImDrawList callback and pass information to their renderer)
598 
599     User code may do:
600 
601         // Cast our texture type to ImTextureID / void*
602         MyTexture* texture = g_CoffeeTableTexture;
603         ImGui::Image((void*)texture, ImVec2(texture->Width, texture->Height));
604 
605     The renderer function called after ImGui::Render() will receive that same value that the user code passed:
606 
607         // Cast ImTextureID / void* stored in the draw command as our texture type
608         MyTexture* texture = (MyTexture*)pcmd->TextureId;
609         MyEngineBindTexture2D(texture);
610 
611     Once you understand this design you will understand that loading image files and turning them into displayable textures is not within the scope of Dear ImGui.
612     This is by design and is actually a good thing, because it means your code has full control over your data types and how you display them.
613     If you want to display an image file (e.g. PNG file) into the screen, please refer to documentation and tutorials for the graphics API you are using.
614 
615     Here's a simplified OpenGL example using stb_image.h:
616 
617         // Use stb_image.h to load a PNG from disk and turn it into raw RGBA pixel data:
618         #define STB_IMAGE_IMPLEMENTATION
619         #include <stb_image.h>
620         [...]
621         int my_image_width, my_image_height;
622         unsigned char* my_image_data = stbi_load("my_image.png", &my_image_width, &my_image_height, NULL, 4);
623 
624         // Turn the RGBA pixel data into an OpenGL texture:
625         GLuint my_opengl_texture;
626         glGenTextures(1, &my_opengl_texture);
627         glBindTexture(GL_TEXTURE_2D, my_opengl_texture);
628         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
629         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
630         glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
631         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
632 
633         // Now that we have an OpenGL texture, assuming our imgui rendering function (imgui_impl_xxx.cpp file) takes GLuint as ImTextureID, we can display it:
634         ImGui::Image((void*)(intptr_t)my_opengl_texture, ImVec2(my_image_width, my_image_height));
635 
636     C/C++ tip: a void* is pointer-sized storage. You may safely store any pointer or integer into it by casting your value to ImTexture / void*, and vice-versa.
637     Because both end-points (user code and rendering function) are under your control, you know exactly what is stored inside the ImTexture / void*.
638     Examples:
639 
640         GLuint my_tex = XXX;
641         void* my_void_ptr;
642         my_void_ptr = (void*)(intptr_t)my_tex;                  // cast a GLuint into a void* (we don't take its address! we literally store the value inside the pointer)
643         my_tex = (GLuint)(intptr_t)my_void_ptr;                 // cast a void* into a GLuint
644 
645         ID3D11ShaderResourceView* my_dx11_srv = XXX;
646         void* my_void_ptr;
647         my_void_ptr = (void*)my_dx11_srv;                       // cast a ID3D11ShaderResourceView* into an opaque void*
648         my_dx11_srv = (ID3D11ShaderResourceView*)my_void_ptr;   // cast a void* into a ID3D11ShaderResourceView*
649 
650     Finally, you may call ImGui::ShowMetricsWindow() to explore/visualize/understand how the ImDrawList are generated.
651 
652  Q: How can I have multiple widgets with the same label or with an empty label?
653  Q: I have multiple widgets with the same label, and only the first one works. Why is that?
654  A: A primer on labels and the ID Stack...
655 
656     Dear ImGui internally need to uniquely identify UI elements.
657     Elements that are typically not clickable (such as calls to the Text functions) don't need an ID.
658     Interactive widgets (such as calls to Button buttons) need a unique ID.
659     Unique ID are used internally to track active widgets and occasionally associate state to widgets.
660     Unique ID are implicitly built from the hash of multiple elements that identify the "path" to the UI element.
661 
662    - Unique ID are often derived from a string label:
663 
664        Button("OK");          // Label = "OK",     ID = hash of (..., "OK")
665        Button("Cancel");      // Label = "Cancel", ID = hash of (..., "Cancel")
666 
667    - ID are uniquely scoped within windows, tree nodes, etc. which all pushes to the ID stack. Having
668      two buttons labeled "OK" in different windows or different tree locations is fine.
669      We used "..." above to signify whatever was already pushed to the ID stack previously:
670 
671        Begin("MyWindow");
672        Button("OK");          // Label = "OK",     ID = hash of ("MyWindow", "OK")
673        End();
674        Begin("MyOtherWindow");
675        Button("OK");          // Label = "OK",     ID = hash of ("MyOtherWindow", "OK")
676        End();
677 
678    - If you have a same ID twice in the same location, you'll have a conflict:
679 
680        Button("OK");
681        Button("OK");          // ID collision! Interacting with either button will trigger the first one.
682 
683      Fear not! this is easy to solve and there are many ways to solve it!
684 
685    - Solving ID conflict in a simple/local context:
686      When passing a label you can optionally specify extra ID information within string itself.
687      Use "##" to pass a complement to the ID that won't be visible to the end-user.
688      This helps solving the simple collision cases when you know e.g. at compilation time which items
689      are going to be created:
690 
691        Begin("MyWindow");
692        Button("Play");        // Label = "Play",   ID = hash of ("MyWindow", "Play")
693        Button("Play##foo1");  // Label = "Play",   ID = hash of ("MyWindow", "Play##foo1")  // Different from above
694        Button("Play##foo2");  // Label = "Play",   ID = hash of ("MyWindow", "Play##foo2")  // Different from above
695        End();
696 
697    - If you want to completely hide the label, but still need an ID:
698 
699        Checkbox("##On", &b);  // Label = "",       ID = hash of (..., "##On")   // No visible label, just a checkbox!
700 
701    - Occasionally/rarely you might want change a label while preserving a constant ID. This allows
702      you to animate labels. For example you may want to include varying information in a window title bar,
703      but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID:
704 
705        Button("Hello###ID");  // Label = "Hello",  ID = hash of (..., "ID")
706        Button("World###ID");  // Label = "World",  ID = hash of (..., "ID")     // Same as above, even though the label looks different
707 
708        sprintf(buf, "My game (%f FPS)###MyGame", fps);
709        Begin(buf);            // Variable title,   ID = hash of "MyGame"
710 
711    - Solving ID conflict in a more general manner:
712      Use PushID() / PopID() to create scopes and manipulate the ID stack, as to avoid ID conflicts
713      within the same window. This is the most convenient way of distinguishing ID when iterating and
714      creating many UI elements programmatically.
715      You can push a pointer, a string or an integer value into the ID stack.
716      Remember that ID are formed from the concatenation of _everything_ pushed into the ID stack.
717      At each level of the stack we store the seed used for items at this level of the ID stack.
718 
719      Begin("Window");
720        for (int i = 0; i < 100; i++)
721        {
722          PushID(i);           // Push i to the id tack
723          Button("Click");     // Label = "Click",  ID = hash of ("Window", i, "Click")
724          PopID();
725        }
726        for (int i = 0; i < 100; i++)
727        {
728          MyObject* obj = Objects[i];
729          PushID(obj);
730          Button("Click");     // Label = "Click",  ID = hash of ("Window", obj pointer, "Click")
731          PopID();
732        }
733        for (int i = 0; i < 100; i++)
734        {
735          MyObject* obj = Objects[i];
736          PushID(obj->Name);
737          Button("Click");     // Label = "Click",  ID = hash of ("Window", obj->Name, "Click")
738          PopID();
739        }
740        End();
741 
742    - You can stack multiple prefixes into the ID stack:
743 
744        Button("Click");       // Label = "Click",  ID = hash of (..., "Click")
745        PushID("node");
746        Button("Click");       // Label = "Click",  ID = hash of (..., "node", "Click")
747          PushID(my_ptr);
748            Button("Click");   // Label = "Click",  ID = hash of (..., "node", my_ptr, "Click")
749          PopID();
750        PopID();
751 
752    - Tree nodes implicitly creates a scope for you by calling PushID().
753 
754        Button("Click");       // Label = "Click",  ID = hash of (..., "Click")
755        if (TreeNode("node"))  // <-- this function call will do a PushID() for you (unless instructed not to, with a special flag)
756        {
757          Button("Click");     // Label = "Click",  ID = hash of (..., "node", "Click")
758          TreePop();
759        }
760 
761    - When working with trees, ID are used to preserve the open/close state of each tree node.
762      Depending on your use cases you may want to use strings, indices or pointers as ID.
763       e.g. when following a single pointer that may change over time, using a static string as ID
764        will preserve your node open/closed state when the targeted object change.
765       e.g. when displaying a list of objects, using indices or pointers as ID will preserve the
766        node open/closed state differently. See what makes more sense in your situation!
767 
768  Q: How can I use my own math types instead of ImVec2/ImVec4?
769  A: You can edit imconfig.h and setup the IM_VEC2_CLASS_EXTRA/IM_VEC4_CLASS_EXTRA macros to add implicit type conversions.
770     This way you'll be able to use your own types everywhere, e.g. passsing glm::vec2 to ImGui functions instead of ImVec2.
771 
772  Q: How can I load a different font than the default?
773  A: Use the font atlas to load the TTF/OTF file you want:
774       ImGuiIO& io = ImGui::GetIO();
775       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
776       io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
777     Default is ProggyClean.ttf, monospace, rendered at size 13, embedded in dear imgui's source code.
778     (Tip: monospace fonts are convenient because they allow to facilitate horizontal alignment directly at the string level.)
779     (Read the 'misc/fonts/README.txt' file for more details about font loading.)
780 
781     New programmers: remember that in C/C++ and most programming languages if you want to use a
782     backslash \ within a string literal, you need to write it double backslash "\\":
783       io.Fonts->AddFontFromFileTTF("MyDataFolder\MyFontFile.ttf", size_in_pixels);   // WRONG (you are escape the M here!)
784       io.Fonts->AddFontFromFileTTF("MyDataFolder\\MyFontFile.ttf", size_in_pixels);  // CORRECT
785       io.Fonts->AddFontFromFileTTF("MyDataFolder/MyFontFile.ttf", size_in_pixels);   // ALSO CORRECT
786 
787  Q: How can I easily use icons in my application?
788  A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you
789     main font. Then you can refer to icons within your strings.
790     You may want to see ImFontConfig::GlyphMinAdvanceX to make your icon look monospace to facilitate alignment.
791     (Read the 'misc/fonts/README.txt' file for more details about icons font loading.)
792 
793  Q: How can I load multiple fonts?
794  A: Use the font atlas to pack them into a single texture:
795     (Read the 'misc/fonts/README.txt' file and the code in ImFontAtlas for more details.)
796 
797       ImGuiIO& io = ImGui::GetIO();
798       ImFont* font0 = io.Fonts->AddFontDefault();
799       ImFont* font1 = io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
800       ImFont* font2 = io.Fonts->AddFontFromFileTTF("myfontfile2.ttf", size_in_pixels);
801       io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
802       // the first loaded font gets used by default
803       // use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime
804 
805       // Options
806       ImFontConfig config;
807       config.OversampleH = 3;
808       config.OversampleV = 1;
809       config.GlyphOffset.y -= 2.0f;      // Move everything by 2 pixels up
810       config.GlyphExtraSpacing.x = 1.0f; // Increase spacing between characters
811       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_pixels, &config);
812 
813       // Combine multiple fonts into one (e.g. for icon fonts)
814       static ImWchar ranges[] = { 0xf000, 0xf3ff, 0 };
815       ImFontConfig config;
816       config.MergeMode = true;
817       io.Fonts->AddFontDefault();
818       io.Fonts->AddFontFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); // Merge icon font
819       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge japanese glyphs
820 
821  Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
822  A: When loading a font, pass custom Unicode ranges to specify the glyphs to load.
823 
824       // Add default Japanese ranges
825       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese());
826 
827       // Or create your own custom ranges (e.g. for a game you can feed your entire game script and only build the characters the game need)
828       ImVector<ImWchar> ranges;
829       ImFontGlyphRangesBuilder builder;
830       builder.AddText("Hello world");                        // Add a string (here "Hello world" contains 7 unique characters)
831       builder.AddChar(0x7262);                               // Add a specific character
832       builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Add one of the default ranges
833       builder.BuildRanges(&ranges);                          // Build the final result (ordered ranges with all the unique characters submitted)
834       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, ranges.Data);
835 
836     All your strings needs to use UTF-8 encoding. In C++11 you can encode a string literal in UTF-8
837     by using the u8"hello" syntax. Specifying literal in your source code using a local code page
838     (such as CP-923 for Japanese or CP-1251 for Cyrillic) will NOT work!
839     Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8.
840 
841     Text input: it is up to your application to pass the right character code by calling io.AddInputCharacter().
842     The applications in examples/ are doing that.
843     Windows: you can use the WM_CHAR or WM_UNICHAR or WM_IME_CHAR message (depending if your app is built using Unicode or MultiByte mode).
844     You may also use MultiByteToWideChar() or ToUnicode() to retrieve Unicode codepoints from MultiByte characters or keyboard state.
845     Windows: if your language is relying on an Input Method Editor (IME), you copy the HWND of your window to io.ImeWindowHandle in order for
846     the default implementation of io.ImeSetInputScreenPosFn() to set your Microsoft IME position correctly.
847 
848  Q: How can I interact with standard C++ types (such as std::string and std::vector)?
849  A: - Being highly portable (bindings for several languages, frameworks, programming style, obscure or older platforms/compilers),
850       and aiming for compatibility & performance suitable for every modern real-time game engines, dear imgui does not use
851       any of std C++ types. We use raw types (e.g. char* instead of std::string) because they adapt to more use cases.
852     - To use ImGui::InputText() with a std::string or any resizable string class, see misc/cpp/imgui_stdlib.h.
853     - To use combo boxes and list boxes with std::vector or any other data structure: the BeginCombo()/EndCombo() API
854       lets you iterate and submit items yourself, so does the ListBoxHeader()/ListBoxFooter() API.
855       Prefer using them over the old and awkward Combo()/ListBox() api.
856     - Generally for most high-level types you should be able to access the underlying data type.
857       You may write your own one-liner wrappers to facilitate user code (tip: add new functions in ImGui:: namespace from your code).
858     - Dear ImGui applications often need to make intensive use of strings. It is expected that many of the strings you will pass
859       to the API are raw literals (free in C/C++) or allocated in a manner that won't incur a large cost on your application.
860       Please bear in mind that using std::string on applications with large amount of UI may incur unsatisfactory performances.
861       Modern implementations of std::string often include small-string optimization (which is often a local buffer) but those
862       are not configurable and not the same across implementations.
863     - If you are finding your UI traversal cost to be too large, make sure your string usage is not leading to excessive amount
864       of heap allocations. Consider using literals, statically sized buffers and your own helper functions. A common pattern
865       is that you will need to build lots of strings on the fly, and their maximum length can be easily be scoped ahead.
866       One possible implementation of a helper to facilitate printf-style building of strings: https://github.com/ocornut/Str
867       This is a small helper where you can instance strings with configurable local buffers length. Many game engines will
868       provide similar or better string helpers.
869 
870  Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
871  A: - You can create a dummy window. Call Begin() with the NoBackground | NoDecoration | NoSavedSettings | NoInputs flags.
872       (The ImGuiWindowFlags_NoDecoration flag itself is a shortcut for NoTitleBar | NoResize | NoScrollbar | NoCollapse)
873       Then you can retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like.
874     - You can call ImGui::GetOverlayDrawList() and use this draw list to display contents over every other imgui windows.
875     - You can create your own ImDrawList instance. You'll need to initialize them ImGui::GetDrawListSharedData(), or create
876       your own ImDrawListSharedData, and then call your rendered code with your own ImDrawList or ImDrawData data.
877 
878  Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display)
879  A: - You can control Dear ImGui with a gamepad. Read about navigation in "Using gamepad/keyboard navigation controls".
880       (short version: map gamepad inputs into the io.NavInputs[] array + set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad)
881     - You can share your computer mouse seamlessly with your console/tablet/phone using Synergy (https://symless.com/synergy)
882       This is the preferred solution for developer productivity.
883       In particular, the "micro-synergy-client" repository (https://github.com/symless/micro-synergy-client) has simple
884       and portable source code (uSynergy.c/.h) for a small embeddable client that you can use on any platform to connect
885       to your host computer, based on the Synergy 1.x protocol. Make sure you download the Synergy 1 server on your computer.
886       Console SDK also sometimes provide equivalent tooling or wrapper for Synergy-like protocols.
887     - You may also use a third party solution such as Remote ImGui (https://github.com/JordiRos/remoteimgui) which sends
888       the vertices to render over the local network, allowing you to use Dear ImGui even on a screen-less machine.
889     - For touch inputs, you can increase the hit box of widgets (via the style.TouchPadding setting) to accommodate
890       for the lack of precision of touch inputs, but it is recommended you use a mouse or gamepad to allow optimizing
891       for screen real-estate and precision.
892 
893  Q: I integrated Dear ImGui in my engine and the text or lines are blurry..
894  A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f).
895     Also make sure your orthographic projection matrix and io.DisplaySize matches your actual framebuffer dimension.
896 
897  Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
898  A: You are probably mishandling the clipping rectangles in your render function.
899     Rectangles provided by ImGui are defined as (x1=left,y1=top,x2=right,y2=bottom) and NOT as (x1,y1,width,height).
900 
901  Q: How can I help?
902  A: - If you are experienced with Dear ImGui and C++, look at the github issues, look at the Wiki, read docs/TODO.txt
903       and see how you want to help and can help!
904     - Businesses: convince your company to fund development via support contracts/sponsoring! This is among the most useful thing you can do for dear imgui.
905     - Individuals: you can also become a Patron (http://www.patreon.com/imgui) or donate on PayPal! See README.
906     - Disclose your usage of dear imgui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
907       You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/1902). Visuals are ideal as they inspire other programmers.
908       But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions.
909     - 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).
910 
911  - tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window.
912         this is also useful to set yourself in the context of another window (to get/set other settings)
913  - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug".
914  - tip: the ImGuiOnceUponAFrame helper will allow run the block of code only once a frame. You can use it to quickly add custom UI in the middle
915         of a deep nested inner loop in your code.
916  - tip: you can call Render() multiple times (e.g for VR renders).
917  - tip: call and read the ShowDemoWindow() code in imgui_demo.cpp for more example of how to use ImGui!
918 
919 */
920 
921 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
922 #define _CRT_SECURE_NO_WARNINGS
923 #endif
924 
925 #include "imgui.h"
926 #ifndef IMGUI_DEFINE_MATH_OPERATORS
927 #define IMGUI_DEFINE_MATH_OPERATORS
928 #endif
929 #include "imgui_internal.h"
930 
931 #include <ctype.h>      // toupper, isprint
932 #include <stdio.h>      // vsnprintf, sscanf, printf
933 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
934 #include <stddef.h>     // intptr_t
935 #else
936 #include <stdint.h>     // intptr_t
937 #endif
938 
939 // Debug options
940 #define IMGUI_DEBUG_NAV_SCORING     0
941 #define IMGUI_DEBUG_NAV_RECTS       0
942 
943 // Visual Studio warnings
944 #ifdef _MSC_VER
945 #pragma warning (disable: 4127)     // condition expression is constant
946 #pragma warning (disable: 4996)     // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
947 #endif
948 
949 // Clang/GCC warnings with -Weverything
950 #ifdef __clang__
951 #pragma clang diagnostic ignored "-Wunknown-pragmas"        // warning : unknown warning group '-Wformat-pedantic *'        // not all warnings are known by all clang versions.. so ignoring warnings triggers new warnings on some configuration. great!
952 #pragma clang diagnostic ignored "-Wold-style-cast"         // warning : use of old-style cast                              // yes, they are more terse.
953 #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.
954 #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.
955 #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.
956 #pragma clang diagnostic ignored "-Wglobal-constructors"    // warning : declaration requires a global destructor           // similar to above, not sure what the exact difference it.
957 #pragma clang diagnostic ignored "-Wsign-conversion"        // warning : implicit conversion changes signedness             //
958 #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.
959 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast"       // warning : cast to 'void *' from smaller integer type 'int'
960 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"  // warning : zero as null pointer constant              // some standard header variations use #define NULL 0
961 #if __has_warning("-Wdouble-promotion")
962 #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.
963 #endif
964 #elif defined(__GNUC__)
965 #pragma GCC diagnostic ignored "-Wunused-function"          // warning: 'xxxx' defined but not used
966 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"      // warning: cast to pointer from integer of different size
967 #pragma GCC diagnostic ignored "-Wformat"                   // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
968 #pragma GCC diagnostic ignored "-Wdouble-promotion"         // warning: implicit conversion from 'float' to 'double' when passing argument to function
969 #pragma GCC diagnostic ignored "-Wconversion"               // warning: conversion to 'xxxx' from 'xxxx' may alter its value
970 #pragma GCC diagnostic ignored "-Wformat-nonliteral"        // warning: format not a string literal, format string not checked
971 #pragma GCC diagnostic ignored "-Wstrict-overflow"          // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
972 #if __GNUC__ >= 8
973 #pragma GCC diagnostic ignored "-Wclass-memaccess"          // warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
974 #endif
975 #endif
976 
977 // 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.
978 static const float NAV_WINDOWING_HIGHLIGHT_DELAY            = 0.20f;    // Time before the highlight and screen dimming starts fading in
979 static const float NAV_WINDOWING_LIST_APPEAR_DELAY          = 0.15f;    // Time before the window list starts to appear
980 
981 // Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by back-end)
982 static const float WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS = 4.0f;     // Extend outside and inside windows. Affect FindHoveredWindow().
983 static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f;    // Reduce visual noise by only highlighting the border after a certain time.
984 
985 //-------------------------------------------------------------------------
986 // [SECTION] FORWARD DECLARATIONS
987 //-------------------------------------------------------------------------
988 
989 static void             SetCurrentWindow(ImGuiWindow* window);
990 static void             SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond);
991 static void             SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond);
992 static void             SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond);
993 static void             FindHoveredWindow();
994 static ImGuiWindow*     CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
995 static void             CheckStacksSize(ImGuiWindow* window, bool write);
996 static ImVec2           CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges);
997 
998 static void             AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
999 static void             AddWindowToDrawData(ImVector<ImDrawList*>* out_list, ImGuiWindow* window);
1000 static void             AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
1001 
1002 static ImRect           GetViewportRect();
1003 
1004 // Settings
1005 static void*            SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
1006 static void             SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
1007 static void             SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf);
1008 
1009 // Platform Dependents default implementation for IO functions
1010 static const char*      GetClipboardTextFn_DefaultImpl(void* user_data);
1011 static void             SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
1012 static void             ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
1013 
1014 namespace ImGui
1015 {
1016 static bool             BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags);
1017 
1018 // Navigation
1019 static void             NavUpdate();
1020 static void             NavUpdateWindowing();
1021 static void             NavUpdateWindowingList();
1022 static void             NavUpdateMoveResult();
1023 static float            NavUpdatePageUpPageDown(int allowed_dir_flags);
1024 static inline void      NavUpdateAnyRequestFlag();
1025 static void             NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id);
1026 static ImVec2           NavCalcPreferredRefPos();
1027 static void             NavSaveLastChildNavWindow(ImGuiWindow* nav_window);
1028 static ImGuiWindow*     NavRestoreLastChildNavWindow(ImGuiWindow* window);
1029 
1030 // Misc
1031 static void             UpdateMouseInputs();
1032 static void             UpdateMouseWheel();
1033 static void             UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]);
1034 }
1035 
1036 //-----------------------------------------------------------------------------
1037 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
1038 //-----------------------------------------------------------------------------
1039 
1040 // Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
1041 // CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext().
1042 // If you use DLL hotreloading you might need to call SetCurrentContext() after reloading code from this file.
1043 // ImGui functions are not thread-safe because of this pointer. If you want thread-safety to allow N threads to access N different contexts, you can:
1044 // - Change this variable to use thread local storage. You may #define GImGui in imconfig.h for that purpose. Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
1045 // - Having multiple instances of the ImGui code compiled inside different namespace (easiest/safest, if you have a finite number of contexts)
1046 #ifndef GImGui
1047 ImGuiContext*   GImGui = NULL;
1048 #endif
1049 
1050 // Memory Allocator functions. Use SetAllocatorFunctions() to change them.
1051 // If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file.
1052 // 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.
1053 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
MallocWrapper(size_t size,void * user_data)1054 static void*   MallocWrapper(size_t size, void* user_data)    { (void)user_data; return malloc(size); }
FreeWrapper(void * ptr,void * user_data)1055 static void    FreeWrapper(void* ptr, void* user_data)        { (void)user_data; free(ptr); }
1056 #else
MallocWrapper(size_t size,void * user_data)1057 static void*   MallocWrapper(size_t size, void* user_data)    { (void)user_data; (void)size; IM_ASSERT(0); return NULL; }
FreeWrapper(void * ptr,void * user_data)1058 static void    FreeWrapper(void* ptr, void* user_data)        { (void)user_data; (void)ptr; IM_ASSERT(0); }
1059 #endif
1060 
1061 static void*  (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper;
1062 static void   (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper;
1063 static void*    GImAllocatorUserData = NULL;
1064 
1065 //-----------------------------------------------------------------------------
1066 // [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
1067 //-----------------------------------------------------------------------------
1068 
ImGuiStyle()1069 ImGuiStyle::ImGuiStyle()
1070 {
1071     Alpha                   = 1.0f;             // Global alpha applies to everything in ImGui
1072     WindowPadding           = ImVec2(8,8);      // Padding within a window
1073     WindowRounding          = 7.0f;             // Radius of window corners rounding. Set to 0.0f to have rectangular windows
1074     WindowBorderSize        = 1.0f;             // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1075     WindowMinSize           = ImVec2(32,32);    // Minimum window size
1076     WindowTitleAlign        = ImVec2(0.0f,0.5f);// Alignment for title bar text
1077     ChildRounding           = 0.0f;             // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
1078     ChildBorderSize         = 1.0f;             // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1079     PopupRounding           = 0.0f;             // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
1080     PopupBorderSize         = 1.0f;             // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1081     FramePadding            = ImVec2(4,3);      // Padding within a framed rectangle (used by most widgets)
1082     FrameRounding           = 0.0f;             // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
1083     FrameBorderSize         = 0.0f;             // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
1084     ItemSpacing             = ImVec2(8,4);      // Horizontal and vertical spacing between widgets/lines
1085     ItemInnerSpacing        = ImVec2(4,4);      // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
1086     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!
1087     IndentSpacing           = 21.0f;            // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
1088     ColumnsMinSpacing       = 6.0f;             // Minimum horizontal spacing between two columns
1089     ScrollbarSize           = 16.0f;            // Width of the vertical scrollbar, Height of the horizontal scrollbar
1090     ScrollbarRounding       = 9.0f;             // Radius of grab corners rounding for scrollbar
1091     GrabMinSize             = 10.0f;            // Minimum width/height of a grab box for slider/scrollbar
1092     GrabRounding            = 0.0f;             // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
1093     TabRounding             = 4.0f;             // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
1094     TabBorderSize           = 0.0f;             // Thickness of border around tabs.
1095     ButtonTextAlign         = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
1096     DisplayWindowPadding    = ImVec2(19,19);    // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows.
1097     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.
1098     MouseCursorScale        = 1.0f;             // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
1099     AntiAliasedLines        = true;             // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
1100     AntiAliasedFill         = true;             // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
1101     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.
1102 
1103     // Default theme
1104     ImGui::StyleColorsDark(this);
1105 }
1106 
1107 // 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.
1108 // 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)1109 void ImGuiStyle::ScaleAllSizes(float scale_factor)
1110 {
1111     WindowPadding = ImFloor(WindowPadding * scale_factor);
1112     WindowRounding = ImFloor(WindowRounding * scale_factor);
1113     WindowMinSize = ImFloor(WindowMinSize * scale_factor);
1114     ChildRounding = ImFloor(ChildRounding * scale_factor);
1115     PopupRounding = ImFloor(PopupRounding * scale_factor);
1116     FramePadding = ImFloor(FramePadding * scale_factor);
1117     FrameRounding = ImFloor(FrameRounding * scale_factor);
1118     TabRounding = ImFloor(TabRounding * scale_factor);
1119     ItemSpacing = ImFloor(ItemSpacing * scale_factor);
1120     ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
1121     TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
1122     IndentSpacing = ImFloor(IndentSpacing * scale_factor);
1123     ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
1124     ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
1125     ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
1126     GrabMinSize = ImFloor(GrabMinSize * scale_factor);
1127     GrabRounding = ImFloor(GrabRounding * scale_factor);
1128     DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
1129     DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
1130     MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
1131 }
1132 
ImGuiIO()1133 ImGuiIO::ImGuiIO()
1134 {
1135     // Most fields are initialized with zero
1136     memset(this, 0, sizeof(*this));
1137 
1138     // Settings
1139     ConfigFlags = ImGuiConfigFlags_None;
1140     BackendFlags = ImGuiBackendFlags_None;
1141     DisplaySize = ImVec2(-1.0f, -1.0f);
1142     DeltaTime = 1.0f/60.0f;
1143     IniSavingRate = 5.0f;
1144     IniFilename = "imgui.ini";
1145     LogFilename = "imgui_log.txt";
1146     MouseDoubleClickTime = 0.30f;
1147     MouseDoubleClickMaxDist = 6.0f;
1148     for (int i = 0; i < ImGuiKey_COUNT; i++)
1149         KeyMap[i] = -1;
1150     KeyRepeatDelay = 0.250f;
1151     KeyRepeatRate = 0.050f;
1152     UserData = NULL;
1153 
1154     Fonts = NULL;
1155     FontGlobalScale = 1.0f;
1156     FontDefault = NULL;
1157     FontAllowUserScaling = false;
1158     DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1159     DisplayVisibleMin = DisplayVisibleMax = ImVec2(0.0f, 0.0f);
1160 
1161     // Miscellaneous configuration options
1162 #ifdef __APPLE__
1163     ConfigMacOSXBehaviors = true;  // Set Mac OS X style defaults based on __APPLE__ compile time flag
1164 #else
1165     ConfigMacOSXBehaviors = false;
1166 #endif
1167     ConfigInputTextCursorBlink = true;
1168     ConfigWindowsResizeFromEdges = true;
1169     ConfigWindowsMoveFromTitleBarOnly = false;
1170 
1171     // Platform Functions
1172     BackendPlatformName = BackendRendererName = NULL;
1173     BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1174     GetClipboardTextFn = GetClipboardTextFn_DefaultImpl;   // Platform dependent default implementations
1175     SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
1176     ClipboardUserData = NULL;
1177     ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
1178     ImeWindowHandle = NULL;
1179 
1180 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1181     RenderDrawListsFn = NULL;
1182 #endif
1183 
1184     // Input (NB: we already have memset zero the entire structure!)
1185     MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1186     MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1187     MouseDragThreshold = 6.0f;
1188     for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1189     for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i]  = KeysDownDurationPrev[i] = -1.0f;
1190     for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
1191 }
1192 
1193 // Pass in translated ASCII characters for text input.
1194 // - with glfw you can get those from the callback set in glfwSetCharCallback()
1195 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(ImWchar c)1196 void ImGuiIO::AddInputCharacter(ImWchar c)
1197 {
1198     InputQueueCharacters.push_back(c);
1199 }
1200 
AddInputCharactersUTF8(const char * utf8_chars)1201 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1202 {
1203     while (*utf8_chars != 0)
1204     {
1205         unsigned int c = 0;
1206         utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
1207         if (c > 0 && c <= 0xFFFF)
1208             InputQueueCharacters.push_back((ImWchar)c);
1209     }
1210 }
1211 
ClearInputCharacters()1212 void ImGuiIO::ClearInputCharacters()
1213 {
1214     InputQueueCharacters.resize(0);
1215 }
1216 
1217 //-----------------------------------------------------------------------------
1218 // [SECTION] MISC HELPER/UTILITIES (Maths, String, Format, Hash, File functions)
1219 //-----------------------------------------------------------------------------
1220 
ImLineClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & p)1221 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1222 {
1223     ImVec2 ap = p - a;
1224     ImVec2 ab_dir = b - a;
1225     float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1226     if (dot < 0.0f)
1227         return a;
1228     float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1229     if (dot > ab_len_sqr)
1230         return b;
1231     return a + ab_dir * dot / ab_len_sqr;
1232 }
1233 
ImTriangleContainsPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1234 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1235 {
1236     bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1237     bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1238     bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1239     return ((b1 == b2) && (b2 == b3));
1240 }
1241 
ImTriangleBarycentricCoords(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p,float & out_u,float & out_v,float & out_w)1242 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1243 {
1244     ImVec2 v0 = b - a;
1245     ImVec2 v1 = c - a;
1246     ImVec2 v2 = p - a;
1247     const float denom = v0.x * v1.y - v1.x * v0.y;
1248     out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1249     out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1250     out_u = 1.0f - out_v - out_w;
1251 }
1252 
ImTriangleClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1253 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1254 {
1255     ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1256     ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1257     ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1258     float dist2_ab = ImLengthSqr(p - proj_ab);
1259     float dist2_bc = ImLengthSqr(p - proj_bc);
1260     float dist2_ca = ImLengthSqr(p - proj_ca);
1261     float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1262     if (m == dist2_ab)
1263         return proj_ab;
1264     if (m == dist2_bc)
1265         return proj_bc;
1266     return proj_ca;
1267 }
1268 
ImStricmp(const char * str1,const char * str2)1269 int ImStricmp(const char* str1, const char* str2)
1270 {
1271     int d;
1272     while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
1273     return d;
1274 }
1275 
ImStrnicmp(const char * str1,const char * str2,size_t count)1276 int ImStrnicmp(const char* str1, const char* str2, size_t count)
1277 {
1278     int d = 0;
1279     while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
1280     return d;
1281 }
1282 
ImStrncpy(char * dst,const char * src,size_t count)1283 void ImStrncpy(char* dst, const char* src, size_t count)
1284 {
1285     if (count < 1) return;
1286     strncpy(dst, src, count);
1287     dst[count-1] = 0;
1288 }
1289 
ImStrdup(const char * str)1290 char* ImStrdup(const char* str)
1291 {
1292     size_t len = strlen(str);
1293     void* buf = ImGui::MemAlloc(len + 1);
1294     return (char*)memcpy(buf, (const void*)str, len + 1);
1295 }
1296 
ImStrdupcpy(char * dst,size_t * p_dst_size,const char * src)1297 char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
1298 {
1299     size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1;
1300     size_t src_size = strlen(src) + 1;
1301     if (dst_buf_size < src_size)
1302     {
1303         ImGui::MemFree(dst);
1304         dst = (char*)ImGui::MemAlloc(src_size);
1305         if (p_dst_size)
1306             *p_dst_size = src_size;
1307     }
1308     return (char*)memcpy(dst, (const void*)src, src_size);
1309 }
1310 
ImStrchrRange(const char * str,const char * str_end,char c)1311 const char* ImStrchrRange(const char* str, const char* str_end, char c)
1312 {
1313     const char* p = (const char*)memchr(str, (int)c, str_end - str);
1314     return p;
1315 }
1316 
ImStrlenW(const ImWchar * str)1317 int ImStrlenW(const ImWchar* str)
1318 {
1319     //return (int)wcslen((const wchar_t*)str);	// FIXME-OPT: Could use this when wchar_t are 16-bits
1320     int n = 0;
1321     while (*str++) n++;
1322     return n;
1323 }
1324 
1325 // Find end-of-line. Return pointer will point to either first \n, either str_end.
ImStreolRange(const char * str,const char * str_end)1326 const char* ImStreolRange(const char* str, const char* str_end)
1327 {
1328     const char* p = (const char*)memchr(str, '\n', str_end - str);
1329     return p ? p : str_end;
1330 }
1331 
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)1332 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
1333 {
1334     while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1335         buf_mid_line--;
1336     return buf_mid_line;
1337 }
1338 
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)1339 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1340 {
1341     if (!needle_end)
1342         needle_end = needle + strlen(needle);
1343 
1344     const char un0 = (char)toupper(*needle);
1345     while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1346     {
1347         if (toupper(*haystack) == un0)
1348         {
1349             const char* b = needle + 1;
1350             for (const char* a = haystack + 1; b < needle_end; a++, b++)
1351                 if (toupper(*a) != toupper(*b))
1352                     break;
1353             if (b == needle_end)
1354                 return haystack;
1355         }
1356         haystack++;
1357     }
1358     return NULL;
1359 }
1360 
1361 // 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)1362 void ImStrTrimBlanks(char* buf)
1363 {
1364     char* p = buf;
1365     while (p[0] == ' ' || p[0] == '\t')     // Leading blanks
1366         p++;
1367     char* p_start = p;
1368     while (*p != 0)                         // Find end of string
1369         p++;
1370     while (p > p_start && (p[-1] == ' ' || p[-1] == '\t'))  // Trailing blanks
1371         p--;
1372     if (p_start != buf)                     // Copy memory if we had leading blanks
1373         memmove(buf, p_start, p - p_start);
1374     buf[p - p_start] = 0;                   // Zero terminate
1375 }
1376 
1377 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1378 // 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.
1379 // B) When buf==NULL vsnprintf() will return the output size.
1380 #ifndef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1381 
1382 //#define IMGUI_USE_STB_SPRINTF
1383 #ifdef IMGUI_USE_STB_SPRINTF
1384 #define STB_SPRINTF_IMPLEMENTATION
1385 #include "imstb_sprintf.h"
1386 #endif
1387 
1388 #if defined(_MSC_VER) && !defined(vsnprintf)
1389 #define vsnprintf _vsnprintf
1390 #endif
1391 
ImFormatString(char * buf,size_t buf_size,const char * fmt,...)1392 int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1393 {
1394     va_list args;
1395     va_start(args, fmt);
1396 #ifdef IMGUI_USE_STB_SPRINTF
1397     int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1398 #else
1399     int w = vsnprintf(buf, buf_size, fmt, args);
1400 #endif
1401     va_end(args);
1402     if (buf == NULL)
1403         return w;
1404     if (w == -1 || w >= (int)buf_size)
1405         w = (int)buf_size - 1;
1406     buf[w] = 0;
1407     return w;
1408 }
1409 
ImFormatStringV(char * buf,size_t buf_size,const char * fmt,va_list args)1410 int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1411 {
1412 #ifdef IMGUI_USE_STB_SPRINTF
1413     int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1414 #else
1415     int w = vsnprintf(buf, buf_size, fmt, args);
1416 #endif
1417     if (buf == NULL)
1418         return w;
1419     if (w == -1 || w >= (int)buf_size)
1420         w = (int)buf_size - 1;
1421     buf[w] = 0;
1422     return w;
1423 }
1424 #endif // #ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1425 
1426 // Pass data_size == 0 for zero-terminated strings, data_size > 0 for non-string data.
1427 // Pay attention that data_size==0 will yield different results than passing strlen(data) because the zero-terminated codepath handles ###.
1428 // This should technically be split into two distinct functions (ImHashData/ImHashStr), perhaps once we remove the silly static variable.
1429 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
ImHash(const void * data,int data_size,ImU32 seed)1430 ImU32 ImHash(const void* data, int data_size, ImU32 seed)
1431 {
1432     static ImU32 crc32_lut[256] = { 0 };
1433     if (!crc32_lut[1])
1434     {
1435         const ImU32 polynomial = 0xEDB88320;
1436         for (ImU32 i = 0; i < 256; i++)
1437         {
1438             ImU32 crc = i;
1439             for (ImU32 j = 0; j < 8; j++)
1440                 crc = (crc >> 1) ^ (ImU32(-int(crc & 1)) & polynomial);
1441             crc32_lut[i] = crc;
1442         }
1443     }
1444 
1445     seed = ~seed;
1446     ImU32 crc = seed;
1447     const unsigned char* current = (const unsigned char*)data;
1448 
1449     if (data_size > 0)
1450     {
1451         // Known size
1452         while (data_size--)
1453             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *current++];
1454     }
1455     else
1456     {
1457         // Zero-terminated string
1458         while (unsigned char c = *current++)
1459         {
1460             // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1461             // Because this syntax is rarely used we are optimizing for the common case.
1462             // - If we reach ### in the string we discard the hash so far and reset to the seed.
1463             // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller.
1464             if (c == '#' && current[0] == '#' && current[1] == '#')
1465                 crc = seed;
1466             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1467         }
1468     }
1469     return ~crc;
1470 }
1471 
ImFileOpen(const char * filename,const char * mode)1472 FILE* ImFileOpen(const char* filename, const char* mode)
1473 {
1474 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__GNUC__)
1475     // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. Converting both strings from UTF-8 to wchar format (using a single allocation, because we can)
1476     const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1;
1477     const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1;
1478     ImVector<ImWchar> buf;
1479     buf.resize(filename_wsize + mode_wsize);
1480     ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL);
1481     ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL);
1482     return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]);
1483 #else
1484     return fopen(filename, mode);
1485 #endif
1486 }
1487 
1488 // Load file content into memory
1489 // Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree()
ImFileLoadToMemory(const char * filename,const char * file_open_mode,size_t * out_file_size,int padding_bytes)1490 void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, size_t* out_file_size, int padding_bytes)
1491 {
1492     IM_ASSERT(filename && file_open_mode);
1493     if (out_file_size)
1494         *out_file_size = 0;
1495 
1496     FILE* f;
1497     if ((f = ImFileOpen(filename, file_open_mode)) == NULL)
1498         return NULL;
1499 
1500     long file_size_signed;
1501     if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET))
1502     {
1503         fclose(f);
1504         return NULL;
1505     }
1506 
1507     size_t file_size = (size_t)file_size_signed;
1508     void* file_data = ImGui::MemAlloc(file_size + padding_bytes);
1509     if (file_data == NULL)
1510     {
1511         fclose(f);
1512         return NULL;
1513     }
1514     if (fread(file_data, 1, file_size, f) != file_size)
1515     {
1516         fclose(f);
1517         ImGui::MemFree(file_data);
1518         return NULL;
1519     }
1520     if (padding_bytes > 0)
1521         memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
1522 
1523     fclose(f);
1524     if (out_file_size)
1525         *out_file_size = file_size;
1526 
1527     return file_data;
1528 }
1529 
1530 //-----------------------------------------------------------------------------
1531 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
1532 //-----------------------------------------------------------------------------
1533 
1534 // Convert UTF-8 to 32-bits character, process single character input.
1535 // Based on stb_from_utf8() from github.com/nothings/stb/
1536 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)1537 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1538 {
1539     unsigned int c = (unsigned int)-1;
1540     const unsigned char* str = (const unsigned char*)in_text;
1541     if (!(*str & 0x80))
1542     {
1543         c = (unsigned int)(*str++);
1544         *out_char = c;
1545         return 1;
1546     }
1547     if ((*str & 0xe0) == 0xc0)
1548     {
1549         *out_char = 0xFFFD; // will be invalid but not end of string
1550         if (in_text_end && in_text_end - (const char*)str < 2) return 1;
1551         if (*str < 0xc2) return 2;
1552         c = (unsigned int)((*str++ & 0x1f) << 6);
1553         if ((*str & 0xc0) != 0x80) return 2;
1554         c += (*str++ & 0x3f);
1555         *out_char = c;
1556         return 2;
1557     }
1558     if ((*str & 0xf0) == 0xe0)
1559     {
1560         *out_char = 0xFFFD; // will be invalid but not end of string
1561         if (in_text_end && in_text_end - (const char*)str < 3) return 1;
1562         if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3;
1563         if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below
1564         c = (unsigned int)((*str++ & 0x0f) << 12);
1565         if ((*str & 0xc0) != 0x80) return 3;
1566         c += (unsigned int)((*str++ & 0x3f) << 6);
1567         if ((*str & 0xc0) != 0x80) return 3;
1568         c += (*str++ & 0x3f);
1569         *out_char = c;
1570         return 3;
1571     }
1572     if ((*str & 0xf8) == 0xf0)
1573     {
1574         *out_char = 0xFFFD; // will be invalid but not end of string
1575         if (in_text_end && in_text_end - (const char*)str < 4) return 1;
1576         if (*str > 0xf4) return 4;
1577         if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4;
1578         if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below
1579         c = (unsigned int)((*str++ & 0x07) << 18);
1580         if ((*str & 0xc0) != 0x80) return 4;
1581         c += (unsigned int)((*str++ & 0x3f) << 12);
1582         if ((*str & 0xc0) != 0x80) return 4;
1583         c += (unsigned int)((*str++ & 0x3f) << 6);
1584         if ((*str & 0xc0) != 0x80) return 4;
1585         c += (*str++ & 0x3f);
1586         // utf-8 encodings of values used in surrogate pairs are invalid
1587         if ((c & 0xFFFFF800) == 0xD800) return 4;
1588         *out_char = c;
1589         return 4;
1590     }
1591     *out_char = 0;
1592     return 0;
1593 }
1594 
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1595 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1596 {
1597     ImWchar* buf_out = buf;
1598     ImWchar* buf_end = buf + buf_size;
1599     while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1600     {
1601         unsigned int c;
1602         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1603         if (c == 0)
1604             break;
1605         if (c < 0x10000)    // FIXME: Losing characters that don't fit in 2 bytes
1606             *buf_out++ = (ImWchar)c;
1607     }
1608     *buf_out = 0;
1609     if (in_text_remaining)
1610         *in_text_remaining = in_text;
1611     return (int)(buf_out - buf);
1612 }
1613 
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1614 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1615 {
1616     int char_count = 0;
1617     while ((!in_text_end || in_text < in_text_end) && *in_text)
1618     {
1619         unsigned int c;
1620         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1621         if (c == 0)
1622             break;
1623         if (c < 0x10000)
1624             char_count++;
1625     }
1626     return char_count;
1627 }
1628 
1629 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8(char * buf,int buf_size,unsigned int c)1630 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1631 {
1632     if (c < 0x80)
1633     {
1634         buf[0] = (char)c;
1635         return 1;
1636     }
1637     if (c < 0x800)
1638     {
1639         if (buf_size < 2) return 0;
1640         buf[0] = (char)(0xc0 + (c >> 6));
1641         buf[1] = (char)(0x80 + (c & 0x3f));
1642         return 2;
1643     }
1644     if (c >= 0xdc00 && c < 0xe000)
1645     {
1646         return 0;
1647     }
1648     if (c >= 0xd800 && c < 0xdc00)
1649     {
1650         if (buf_size < 4) return 0;
1651         buf[0] = (char)(0xf0 + (c >> 18));
1652         buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1653         buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1654         buf[3] = (char)(0x80 + ((c ) & 0x3f));
1655         return 4;
1656     }
1657     //else if (c < 0x10000)
1658     {
1659         if (buf_size < 3) return 0;
1660         buf[0] = (char)(0xe0 + (c >> 12));
1661         buf[1] = (char)(0x80 + ((c>> 6) & 0x3f));
1662         buf[2] = (char)(0x80 + ((c ) & 0x3f));
1663         return 3;
1664     }
1665 }
1666 
1667 // Not optimal but we very rarely use this function.
ImTextCountUtf8BytesFromChar(const char * in_text,const char * in_text_end)1668 int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
1669 {
1670     unsigned int dummy = 0;
1671     return ImTextCharFromUtf8(&dummy, in_text, in_text_end);
1672 }
1673 
ImTextCountUtf8BytesFromChar(unsigned int c)1674 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1675 {
1676     if (c < 0x80) return 1;
1677     if (c < 0x800) return 2;
1678     if (c >= 0xdc00 && c < 0xe000) return 0;
1679     if (c >= 0xd800 && c < 0xdc00) return 4;
1680     return 3;
1681 }
1682 
ImTextStrToUtf8(char * buf,int buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1683 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1684 {
1685     char* buf_out = buf;
1686     const char* buf_end = buf + buf_size;
1687     while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1688     {
1689         unsigned int c = (unsigned int)(*in_text++);
1690         if (c < 0x80)
1691             *buf_out++ = (char)c;
1692         else
1693             buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c);
1694     }
1695     *buf_out = 0;
1696     return (int)(buf_out - buf);
1697 }
1698 
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1699 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1700 {
1701     int bytes_count = 0;
1702     while ((!in_text_end || in_text < in_text_end) && *in_text)
1703     {
1704         unsigned int c = (unsigned int)(*in_text++);
1705         if (c < 0x80)
1706             bytes_count++;
1707         else
1708             bytes_count += ImTextCountUtf8BytesFromChar(c);
1709     }
1710     return bytes_count;
1711 }
1712 
1713 //-----------------------------------------------------------------------------
1714 // [SECTION] MISC HELPER/UTILTIES (Color functions)
1715 // Note: The Convert functions are early design which are not consistent with other API.
1716 //-----------------------------------------------------------------------------
1717 
ColorConvertU32ToFloat4(ImU32 in)1718 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1719 {
1720     float s = 1.0f/255.0f;
1721     return ImVec4(
1722         ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1723         ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1724         ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1725         ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1726 }
1727 
ColorConvertFloat4ToU32(const ImVec4 & in)1728 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1729 {
1730     ImU32 out;
1731     out  = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1732     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1733     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1734     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1735     return out;
1736 }
1737 
1738 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1739 // 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)1740 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1741 {
1742     float K = 0.f;
1743     if (g < b)
1744     {
1745         ImSwap(g, b);
1746         K = -1.f;
1747     }
1748     if (r < g)
1749     {
1750         ImSwap(r, g);
1751         K = -2.f / 6.f - K;
1752     }
1753 
1754     const float chroma = r - (g < b ? g : b);
1755     out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
1756     out_s = chroma / (r + 1e-20f);
1757     out_v = r;
1758 }
1759 
1760 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1761 // 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)1762 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1763 {
1764     if (s == 0.0f)
1765     {
1766         // gray
1767         out_r = out_g = out_b = v;
1768         return;
1769     }
1770 
1771     h = ImFmod(h, 1.0f) / (60.0f/360.0f);
1772     int   i = (int)h;
1773     float f = h - (float)i;
1774     float p = v * (1.0f - s);
1775     float q = v * (1.0f - s * f);
1776     float t = v * (1.0f - s * (1.0f - f));
1777 
1778     switch (i)
1779     {
1780     case 0: out_r = v; out_g = t; out_b = p; break;
1781     case 1: out_r = q; out_g = v; out_b = p; break;
1782     case 2: out_r = p; out_g = v; out_b = t; break;
1783     case 3: out_r = p; out_g = q; out_b = v; break;
1784     case 4: out_r = t; out_g = p; out_b = v; break;
1785     case 5: default: out_r = v; out_g = p; out_b = q; break;
1786     }
1787 }
1788 
GetColorU32(ImGuiCol idx,float alpha_mul)1789 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
1790 {
1791     ImGuiStyle& style = GImGui->Style;
1792     ImVec4 c = style.Colors[idx];
1793     c.w *= style.Alpha * alpha_mul;
1794     return ColorConvertFloat4ToU32(c);
1795 }
1796 
GetColorU32(const ImVec4 & col)1797 ImU32 ImGui::GetColorU32(const ImVec4& col)
1798 {
1799     ImGuiStyle& style = GImGui->Style;
1800     ImVec4 c = col;
1801     c.w *= style.Alpha;
1802     return ColorConvertFloat4ToU32(c);
1803 }
1804 
GetStyleColorVec4(ImGuiCol idx)1805 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
1806 {
1807     ImGuiStyle& style = GImGui->Style;
1808     return style.Colors[idx];
1809 }
1810 
GetColorU32(ImU32 col)1811 ImU32 ImGui::GetColorU32(ImU32 col)
1812 {
1813     float style_alpha = GImGui->Style.Alpha;
1814     if (style_alpha >= 1.0f)
1815         return col;
1816     ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
1817     a = (ImU32)(a * style_alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
1818     return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
1819 }
1820 
1821 //-----------------------------------------------------------------------------
1822 // [SECTION] ImGuiStorage
1823 // Helper: Key->value storage
1824 //-----------------------------------------------------------------------------
1825 
1826 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::Pair> & data,ImGuiID key)1827 static ImGuiStorage::Pair* LowerBound(ImVector<ImGuiStorage::Pair>& data, ImGuiID key)
1828 {
1829     ImGuiStorage::Pair* first = data.Data;
1830     ImGuiStorage::Pair* last = data.Data + data.Size;
1831     size_t count = (size_t)(last - first);
1832     while (count > 0)
1833     {
1834         size_t count2 = count >> 1;
1835         ImGuiStorage::Pair* mid = first + count2;
1836         if (mid->key < key)
1837         {
1838             first = ++mid;
1839             count -= count2 + 1;
1840         }
1841         else
1842         {
1843             count = count2;
1844         }
1845     }
1846     return first;
1847 }
1848 
1849 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
BuildSortByKey()1850 void ImGuiStorage::BuildSortByKey()
1851 {
1852     struct StaticFunc
1853     {
1854         static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)
1855         {
1856             // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1857             if (((const Pair*)lhs)->key > ((const Pair*)rhs)->key) return +1;
1858             if (((const Pair*)lhs)->key < ((const Pair*)rhs)->key) return -1;
1859             return 0;
1860         }
1861     };
1862     if (Data.Size > 1)
1863         ImQsort(Data.Data, (size_t)Data.Size, sizeof(Pair), StaticFunc::PairCompareByID);
1864 }
1865 
GetInt(ImGuiID key,int default_val) const1866 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1867 {
1868     ImGuiStorage::Pair* it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1869     if (it == Data.end() || it->key != key)
1870         return default_val;
1871     return it->val_i;
1872 }
1873 
GetBool(ImGuiID key,bool default_val) const1874 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1875 {
1876     return GetInt(key, default_val ? 1 : 0) != 0;
1877 }
1878 
GetFloat(ImGuiID key,float default_val) const1879 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1880 {
1881     ImGuiStorage::Pair* it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1882     if (it == Data.end() || it->key != key)
1883         return default_val;
1884     return it->val_f;
1885 }
1886 
GetVoidPtr(ImGuiID key) const1887 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1888 {
1889     ImGuiStorage::Pair* it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1890     if (it == Data.end() || it->key != key)
1891         return NULL;
1892     return it->val_p;
1893 }
1894 
1895 // 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)1896 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1897 {
1898     ImGuiStorage::Pair* it = LowerBound(Data, key);
1899     if (it == Data.end() || it->key != key)
1900         it = Data.insert(it, Pair(key, default_val));
1901     return &it->val_i;
1902 }
1903 
GetBoolRef(ImGuiID key,bool default_val)1904 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1905 {
1906     return (bool*)GetIntRef(key, default_val ? 1 : 0);
1907 }
1908 
GetFloatRef(ImGuiID key,float default_val)1909 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1910 {
1911     ImGuiStorage::Pair* it = LowerBound(Data, key);
1912     if (it == Data.end() || it->key != key)
1913         it = Data.insert(it, Pair(key, default_val));
1914     return &it->val_f;
1915 }
1916 
GetVoidPtrRef(ImGuiID key,void * default_val)1917 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1918 {
1919     ImGuiStorage::Pair* it = LowerBound(Data, key);
1920     if (it == Data.end() || it->key != key)
1921         it = Data.insert(it, Pair(key, default_val));
1922     return &it->val_p;
1923 }
1924 
1925 // 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)1926 void ImGuiStorage::SetInt(ImGuiID key, int val)
1927 {
1928     ImGuiStorage::Pair* it = LowerBound(Data, key);
1929     if (it == Data.end() || it->key != key)
1930     {
1931         Data.insert(it, Pair(key, val));
1932         return;
1933     }
1934     it->val_i = val;
1935 }
1936 
SetBool(ImGuiID key,bool val)1937 void ImGuiStorage::SetBool(ImGuiID key, bool val)
1938 {
1939     SetInt(key, val ? 1 : 0);
1940 }
1941 
SetFloat(ImGuiID key,float val)1942 void ImGuiStorage::SetFloat(ImGuiID key, float val)
1943 {
1944     ImGuiStorage::Pair* it = LowerBound(Data, key);
1945     if (it == Data.end() || it->key != key)
1946     {
1947         Data.insert(it, Pair(key, val));
1948         return;
1949     }
1950     it->val_f = val;
1951 }
1952 
SetVoidPtr(ImGuiID key,void * val)1953 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1954 {
1955     ImGuiStorage::Pair* it = LowerBound(Data, key);
1956     if (it == Data.end() || it->key != key)
1957     {
1958         Data.insert(it, Pair(key, val));
1959         return;
1960     }
1961     it->val_p = val;
1962 }
1963 
SetAllInt(int v)1964 void ImGuiStorage::SetAllInt(int v)
1965 {
1966     for (int i = 0; i < Data.Size; i++)
1967         Data[i].val_i = v;
1968 }
1969 
1970 //-----------------------------------------------------------------------------
1971 // [SECTION] ImGuiTextFilter
1972 //-----------------------------------------------------------------------------
1973 
1974 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)1975 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
1976 {
1977     if (default_filter)
1978     {
1979         ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
1980         Build();
1981     }
1982     else
1983     {
1984         InputBuf[0] = 0;
1985         CountGrep = 0;
1986     }
1987 }
1988 
Draw(const char * label,float width)1989 bool ImGuiTextFilter::Draw(const char* label, float width)
1990 {
1991     if (width != 0.0f)
1992         ImGui::PushItemWidth(width);
1993     bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
1994     if (width != 0.0f)
1995         ImGui::PopItemWidth();
1996     if (value_changed)
1997         Build();
1998     return value_changed;
1999 }
2000 
split(char separator,ImVector<TextRange> * out) const2001 void ImGuiTextFilter::TextRange::split(char separator, ImVector<TextRange>* out) const
2002 {
2003     out->resize(0);
2004     const char* wb = b;
2005     const char* we = wb;
2006     while (we < e)
2007     {
2008         if (*we == separator)
2009         {
2010             out->push_back(TextRange(wb, we));
2011             wb = we + 1;
2012         }
2013         we++;
2014     }
2015     if (wb != we)
2016         out->push_back(TextRange(wb, we));
2017 }
2018 
Build()2019 void ImGuiTextFilter::Build()
2020 {
2021     Filters.resize(0);
2022     TextRange input_range(InputBuf, InputBuf+strlen(InputBuf));
2023     input_range.split(',', &Filters);
2024 
2025     CountGrep = 0;
2026     for (int i = 0; i != Filters.Size; i++)
2027     {
2028         TextRange& f = Filters[i];
2029         while (f.b < f.e && ImCharIsBlankA(f.b[0]))
2030             f.b++;
2031         while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
2032             f.e--;
2033         if (f.empty())
2034             continue;
2035         if (Filters[i].b[0] != '-')
2036             CountGrep += 1;
2037     }
2038 }
2039 
PassFilter(const char * text,const char * text_end) const2040 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
2041 {
2042     if (Filters.empty())
2043         return true;
2044 
2045     if (text == NULL)
2046         text = "";
2047 
2048     for (int i = 0; i != Filters.Size; i++)
2049     {
2050         const TextRange& f = Filters[i];
2051         if (f.empty())
2052             continue;
2053         if (f.b[0] == '-')
2054         {
2055             // Subtract
2056             if (ImStristr(text, text_end, f.begin()+1, f.end()) != NULL)
2057                 return false;
2058         }
2059         else
2060         {
2061             // Grep
2062             if (ImStristr(text, text_end, f.begin(), f.end()) != NULL)
2063                 return true;
2064         }
2065     }
2066 
2067     // Implicit * grep
2068     if (CountGrep == 0)
2069         return true;
2070 
2071     return false;
2072 }
2073 
2074 //-----------------------------------------------------------------------------
2075 // [SECTION] ImGuiTextBuffer
2076 //-----------------------------------------------------------------------------
2077 
2078 // On some platform vsnprintf() takes va_list by reference and modifies it.
2079 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
2080 #ifndef va_copy
2081 #if defined(__GNUC__) || defined(__clang__)
2082 #define va_copy(dest, src) __builtin_va_copy(dest, src)
2083 #else
2084 #define va_copy(dest, src) (dest = src)
2085 #endif
2086 #endif
2087 
2088 char ImGuiTextBuffer::EmptyString[1] = { 0 };
2089 
2090 // Helper: Text buffer for logging/accumulating text
appendfv(const char * fmt,va_list args)2091 void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
2092 {
2093     va_list args_copy;
2094     va_copy(args_copy, args);
2095 
2096     int len = ImFormatStringV(NULL, 0, fmt, args);         // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
2097     if (len <= 0)
2098     {
2099         va_end(args_copy);
2100         return;
2101     }
2102 
2103     // Add zero-terminator the first time
2104     const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2105     const int needed_sz = write_off + len;
2106     if (write_off + len >= Buf.Capacity)
2107     {
2108         int double_capacity = Buf.Capacity * 2;
2109         Buf.reserve(needed_sz > double_capacity ? needed_sz : double_capacity);
2110     }
2111 
2112     Buf.resize(needed_sz);
2113     ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
2114     va_end(args_copy);
2115 }
2116 
appendf(const char * fmt,...)2117 void ImGuiTextBuffer::appendf(const char* fmt, ...)
2118 {
2119     va_list args;
2120     va_start(args, fmt);
2121     appendfv(fmt, args);
2122     va_end(args);
2123 }
2124 
2125 //-----------------------------------------------------------------------------
2126 // [SECTION] ImGuiListClipper
2127 // This is currently not as flexible/powerful as it should be, needs some rework (see TODO)
2128 //-----------------------------------------------------------------------------
2129 
SetCursorPosYAndSetupDummyPrevLine(float pos_y,float line_height)2130 static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height)
2131 {
2132     // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
2133     // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
2134     // The clipper should probably have a 4th step to display the last item in a regular manner.
2135     ImGui::SetCursorPosY(pos_y);
2136     ImGuiWindow* window = ImGui::GetCurrentWindow();
2137     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.
2138     window->DC.PrevLineSize.y = (line_height - GImGui->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.
2139     if (window->DC.ColumnsSet)
2140         window->DC.ColumnsSet->LineMinY = window->DC.CursorPos.y;           // Setting this so that cell Y position are set properly
2141 }
2142 
2143 // Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
2144 // Use case B: Begin() called from constructor with items_height>0
2145 // 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)2146 void ImGuiListClipper::Begin(int count, float items_height)
2147 {
2148     StartPosY = ImGui::GetCursorPosY();
2149     ItemsHeight = items_height;
2150     ItemsCount = count;
2151     StepNo = 0;
2152     DisplayEnd = DisplayStart = -1;
2153     if (ItemsHeight > 0.0f)
2154     {
2155         ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display
2156         if (DisplayStart > 0)
2157             SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor
2158         StepNo = 2;
2159     }
2160 }
2161 
End()2162 void ImGuiListClipper::End()
2163 {
2164     if (ItemsCount < 0)
2165         return;
2166     // 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.
2167     if (ItemsCount < INT_MAX)
2168         SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor
2169     ItemsCount = -1;
2170     StepNo = 3;
2171 }
2172 
Step()2173 bool ImGuiListClipper::Step()
2174 {
2175     if (ItemsCount == 0 || ImGui::GetCurrentWindowRead()->SkipItems)
2176     {
2177         ItemsCount = -1;
2178         return false;
2179     }
2180     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.
2181     {
2182         DisplayStart = 0;
2183         DisplayEnd = 1;
2184         StartPosY = ImGui::GetCursorPosY();
2185         StepNo = 1;
2186         return true;
2187     }
2188     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.
2189     {
2190         if (ItemsCount == 1) { ItemsCount = -1; return false; }
2191         float items_height = ImGui::GetCursorPosY() - StartPosY;
2192         IM_ASSERT(items_height > 0.0f);   // If this triggers, it means Item 0 hasn't moved the cursor vertically
2193         Begin(ItemsCount-1, items_height);
2194         DisplayStart++;
2195         DisplayEnd++;
2196         StepNo = 3;
2197         return true;
2198     }
2199     if (StepNo == 2) // Step 2: dummy step only required if an explicit items_height was passed to constructor or Begin() and user still call Step(). Does nothing and switch to Step 3.
2200     {
2201         IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0);
2202         StepNo = 3;
2203         return true;
2204     }
2205     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.
2206         End();
2207     return false;
2208 }
2209 
2210 //-----------------------------------------------------------------------------
2211 // [SECTION] RENDER HELPERS
2212 // Those (internal) functions are currently quite a legacy mess - their signature and behavior will change.
2213 // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: state.
2214 //-----------------------------------------------------------------------------
2215 
FindRenderedTextEnd(const char * text,const char * text_end)2216 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2217 {
2218     const char* text_display_end = text;
2219     if (!text_end)
2220         text_end = (const char*)-1;
2221 
2222     while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2223         text_display_end++;
2224     return text_display_end;
2225 }
2226 
2227 // Internal ImGui functions to render text
2228 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)2229 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2230 {
2231     ImGuiContext& g = *GImGui;
2232     ImGuiWindow* window = g.CurrentWindow;
2233 
2234     // Hide anything after a '##' string
2235     const char* text_display_end;
2236     if (hide_text_after_hash)
2237     {
2238         text_display_end = FindRenderedTextEnd(text, text_end);
2239     }
2240     else
2241     {
2242         if (!text_end)
2243             text_end = text + strlen(text); // FIXME-OPT
2244         text_display_end = text_end;
2245     }
2246 
2247     if (text != text_display_end)
2248     {
2249         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2250         if (g.LogEnabled)
2251             LogRenderedText(&pos, text, text_display_end);
2252     }
2253 }
2254 
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)2255 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2256 {
2257     ImGuiContext& g = *GImGui;
2258     ImGuiWindow* window = g.CurrentWindow;
2259 
2260     if (!text_end)
2261         text_end = text + strlen(text); // FIXME-OPT
2262 
2263     if (text != text_end)
2264     {
2265         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2266         if (g.LogEnabled)
2267             LogRenderedText(&pos, text, text_end);
2268     }
2269 }
2270 
2271 // Default clip_rect uses (pos_min,pos_max)
2272 // 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)2273 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)
2274 {
2275     // Perform CPU side clipping for single clipped element to avoid using scissor state
2276     ImVec2 pos = pos_min;
2277     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
2278 
2279     const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
2280     const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
2281     bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2282     if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
2283         need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2284 
2285     // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
2286     if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
2287     if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
2288 
2289     // Render
2290     if (need_clipping)
2291     {
2292         ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2293         draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2294     }
2295     else
2296     {
2297         draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2298     }
2299 }
2300 
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)2301 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)
2302 {
2303     // Hide anything after a '##' string
2304     const char* text_display_end = FindRenderedTextEnd(text, text_end);
2305     const int text_len = (int)(text_display_end - text);
2306     if (text_len == 0)
2307         return;
2308 
2309     ImGuiContext& g = *GImGui;
2310     ImGuiWindow* window = g.CurrentWindow;
2311     RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
2312     if (g.LogEnabled)
2313         LogRenderedText(&pos_min, text, text_display_end);
2314 }
2315 
2316 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)2317 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2318 {
2319     ImGuiContext& g = *GImGui;
2320     ImGuiWindow* window = g.CurrentWindow;
2321     window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2322     const float border_size = g.Style.FrameBorderSize;
2323     if (border && border_size > 0.0f)
2324     {
2325         window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2326         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2327     }
2328 }
2329 
RenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding)2330 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
2331 {
2332     ImGuiContext& g = *GImGui;
2333     ImGuiWindow* window = g.CurrentWindow;
2334     const float border_size = g.Style.FrameBorderSize;
2335     if (border_size > 0.0f)
2336     {
2337         window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2338         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2339     }
2340 }
2341 
2342 // Render an arrow aimed to be aligned with text (p_min is a position in the same space text would be positioned). To e.g. denote expanded/collapsed state
RenderArrow(ImVec2 p_min,ImGuiDir dir,float scale)2343 void ImGui::RenderArrow(ImVec2 p_min, ImGuiDir dir, float scale)
2344 {
2345     ImGuiContext& g = *GImGui;
2346 
2347     const float h = g.FontSize * 1.00f;
2348     float r = h * 0.40f * scale;
2349     ImVec2 center = p_min + ImVec2(h * 0.50f, h * 0.50f * scale);
2350 
2351     ImVec2 a, b, c;
2352     switch (dir)
2353     {
2354     case ImGuiDir_Up:
2355     case ImGuiDir_Down:
2356         if (dir == ImGuiDir_Up) r = -r;
2357         a = ImVec2(+0.000f,+0.750f) * r;
2358         b = ImVec2(-0.866f,-0.750f) * r;
2359         c = ImVec2(+0.866f,-0.750f) * r;
2360         break;
2361     case ImGuiDir_Left:
2362     case ImGuiDir_Right:
2363         if (dir == ImGuiDir_Left) r = -r;
2364         a = ImVec2(+0.750f,+0.000f) * r;
2365         b = ImVec2(-0.750f,+0.866f) * r;
2366         c = ImVec2(-0.750f,-0.866f) * r;
2367         break;
2368     case ImGuiDir_None:
2369     case ImGuiDir_COUNT:
2370         IM_ASSERT(0);
2371         break;
2372     }
2373 
2374     g.CurrentWindow->DrawList->AddTriangleFilled(center + a, center + b, center + c, GetColorU32(ImGuiCol_Text));
2375 }
2376 
RenderBullet(ImVec2 pos)2377 void ImGui::RenderBullet(ImVec2 pos)
2378 {
2379     ImGuiContext& g = *GImGui;
2380     ImGuiWindow* window = g.CurrentWindow;
2381     window->DrawList->AddCircleFilled(pos, g.FontSize*0.20f, GetColorU32(ImGuiCol_Text), 8);
2382 }
2383 
RenderCheckMark(ImVec2 pos,ImU32 col,float sz)2384 void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz)
2385 {
2386     ImGuiContext& g = *GImGui;
2387     ImGuiWindow* window = g.CurrentWindow;
2388 
2389     float thickness = ImMax(sz / 5.0f, 1.0f);
2390     sz -= thickness*0.5f;
2391     pos += ImVec2(thickness*0.25f, thickness*0.25f);
2392 
2393     float third = sz / 3.0f;
2394     float bx = pos.x + third;
2395     float by = pos.y + sz - third*0.5f;
2396     window->DrawList->PathLineTo(ImVec2(bx - third, by - third));
2397     window->DrawList->PathLineTo(ImVec2(bx, by));
2398     window->DrawList->PathLineTo(ImVec2(bx + third*2, by - third*2));
2399     window->DrawList->PathStroke(col, false, thickness);
2400 }
2401 
RenderNavHighlight(const ImRect & bb,ImGuiID id,ImGuiNavHighlightFlags flags)2402 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
2403 {
2404     ImGuiContext& g = *GImGui;
2405     if (id != g.NavId)
2406         return;
2407     if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
2408         return;
2409     ImGuiWindow* window = g.CurrentWindow;
2410     if (window->DC.NavHideHighlightOneFrame)
2411         return;
2412 
2413     float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
2414     ImRect display_rect = bb;
2415     display_rect.ClipWith(window->ClipRect);
2416     if (flags & ImGuiNavHighlightFlags_TypeDefault)
2417     {
2418         const float THICKNESS = 2.0f;
2419         const float DISTANCE = 3.0f + THICKNESS * 0.5f;
2420         display_rect.Expand(ImVec2(DISTANCE,DISTANCE));
2421         bool fully_visible = window->ClipRect.Contains(display_rect);
2422         if (!fully_visible)
2423             window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
2424         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);
2425         if (!fully_visible)
2426             window->DrawList->PopClipRect();
2427     }
2428     if (flags & ImGuiNavHighlightFlags_TypeThin)
2429     {
2430         window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f);
2431     }
2432 }
2433 
2434 //-----------------------------------------------------------------------------
2435 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
2436 //-----------------------------------------------------------------------------
2437 
2438 // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
ImGuiWindow(ImGuiContext * context,const char * name)2439 ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
2440     : DrawListInst(&context->DrawListSharedData)
2441 {
2442     Name = ImStrdup(name);
2443     ID = ImHash(name, 0);
2444     IDStack.push_back(ID);
2445     Flags = ImGuiWindowFlags_None;
2446     Pos = ImVec2(0.0f, 0.0f);
2447     Size = SizeFull = ImVec2(0.0f, 0.0f);
2448     SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f);
2449     WindowPadding = ImVec2(0.0f, 0.0f);
2450     WindowRounding = 0.0f;
2451     WindowBorderSize = 0.0f;
2452     NameBufLen = (int)strlen(name) + 1;
2453     MoveId = GetID("#MOVE");
2454     ChildId = 0;
2455     Scroll = ImVec2(0.0f, 0.0f);
2456     ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
2457     ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
2458     ScrollbarSizes = ImVec2(0.0f, 0.0f);
2459     ScrollbarX = ScrollbarY = false;
2460     Active = WasActive = false;
2461     WriteAccessed = false;
2462     Collapsed = false;
2463     WantCollapseToggle = false;
2464     SkipItems = false;
2465     Appearing = false;
2466     Hidden = false;
2467     HasCloseButton = false;
2468     BeginCount = 0;
2469     BeginOrderWithinParent = -1;
2470     BeginOrderWithinContext = -1;
2471     PopupId = 0;
2472     AutoFitFramesX = AutoFitFramesY = -1;
2473     AutoFitOnlyGrows = false;
2474     AutoFitChildAxises = 0x00;
2475     AutoPosLastDirection = ImGuiDir_None;
2476     HiddenFramesRegular = HiddenFramesForResize = 0;
2477     SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
2478     SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
2479 
2480     LastFrameActive = -1;
2481     ItemWidthDefault = 0.0f;
2482     FontWindowScale = 1.0f;
2483     SettingsIdx = -1;
2484 
2485     DrawList = &DrawListInst;
2486     DrawList->_OwnerName = Name;
2487     ParentWindow = NULL;
2488     RootWindow = NULL;
2489     RootWindowForTitleBarHighlight = NULL;
2490     RootWindowForNav = NULL;
2491 
2492     NavLastIds[0] = NavLastIds[1] = 0;
2493     NavRectRel[0] = NavRectRel[1] = ImRect();
2494     NavLastChildNavWindow = NULL;
2495 
2496     FocusIdxAllCounter = FocusIdxTabCounter = -1;
2497     FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX;
2498     FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX;
2499 }
2500 
~ImGuiWindow()2501 ImGuiWindow::~ImGuiWindow()
2502 {
2503     IM_ASSERT(DrawList == &DrawListInst);
2504     IM_DELETE(Name);
2505     for (int i = 0; i != ColumnsStorage.Size; i++)
2506         ColumnsStorage[i].~ImGuiColumnsSet();
2507 }
2508 
GetID(const char * str,const char * str_end)2509 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
2510 {
2511     ImGuiID seed = IDStack.back();
2512     ImGuiID id = ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
2513     ImGui::KeepAliveID(id);
2514     return id;
2515 }
2516 
GetID(const void * ptr)2517 ImGuiID ImGuiWindow::GetID(const void* ptr)
2518 {
2519     ImGuiID seed = IDStack.back();
2520     ImGuiID id = ImHash(&ptr, sizeof(void*), seed);
2521     ImGui::KeepAliveID(id);
2522     return id;
2523 }
2524 
GetIDNoKeepAlive(const char * str,const char * str_end)2525 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
2526 {
2527     ImGuiID seed = IDStack.back();
2528     return ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
2529 }
2530 
GetIDNoKeepAlive(const void * ptr)2531 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
2532 {
2533     ImGuiID seed = IDStack.back();
2534     return ImHash(&ptr, sizeof(void*), seed);
2535 }
2536 
2537 // This is only used in rare/specific situations to manufacture an ID out of nowhere.
GetIDFromRectangle(const ImRect & r_abs)2538 ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
2539 {
2540     ImGuiID seed = IDStack.back();
2541     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) };
2542     ImGuiID id = ImHash(&r_rel, sizeof(r_rel), seed);
2543     ImGui::KeepAliveID(id);
2544     return id;
2545 }
2546 
SetCurrentWindow(ImGuiWindow * window)2547 static void SetCurrentWindow(ImGuiWindow* window)
2548 {
2549     ImGuiContext& g = *GImGui;
2550     g.CurrentWindow = window;
2551     if (window)
2552         g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
2553 }
2554 
SetNavID(ImGuiID id,int nav_layer)2555 void ImGui::SetNavID(ImGuiID id, int nav_layer)
2556 {
2557     ImGuiContext& g = *GImGui;
2558     IM_ASSERT(g.NavWindow);
2559     IM_ASSERT(nav_layer == 0 || nav_layer == 1);
2560     g.NavId = id;
2561     g.NavWindow->NavLastIds[nav_layer] = id;
2562 }
2563 
SetNavIDWithRectRel(ImGuiID id,int nav_layer,const ImRect & rect_rel)2564 void ImGui::SetNavIDWithRectRel(ImGuiID id, int nav_layer, const ImRect& rect_rel)
2565 {
2566     ImGuiContext& g = *GImGui;
2567     SetNavID(id, nav_layer);
2568     g.NavWindow->NavRectRel[nav_layer] = rect_rel;
2569     g.NavMousePosDirty = true;
2570     g.NavDisableHighlight = false;
2571     g.NavDisableMouseHover = true;
2572 }
2573 
SetActiveID(ImGuiID id,ImGuiWindow * window)2574 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
2575 {
2576     ImGuiContext& g = *GImGui;
2577     g.ActiveIdIsJustActivated = (g.ActiveId != id);
2578     if (g.ActiveIdIsJustActivated)
2579     {
2580         g.ActiveIdTimer = 0.0f;
2581         g.ActiveIdHasBeenEdited = false;
2582         if (id != 0)
2583         {
2584             g.LastActiveId = id;
2585             g.LastActiveIdTimer = 0.0f;
2586         }
2587     }
2588     g.ActiveId = id;
2589     g.ActiveIdAllowNavDirFlags = 0;
2590     g.ActiveIdAllowOverlap = false;
2591     g.ActiveIdWindow = window;
2592     if (id)
2593     {
2594         g.ActiveIdIsAlive = id;
2595         g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
2596     }
2597 }
2598 
2599 // FIXME-NAV: The existence of SetNavID/SetNavIDWithRectRel/SetFocusID is incredibly messy and confusing and needs some explanation or refactoring.
SetFocusID(ImGuiID id,ImGuiWindow * window)2600 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
2601 {
2602     ImGuiContext& g = *GImGui;
2603     IM_ASSERT(id != 0);
2604 
2605     // Assume that SetFocusID() is called in the context where its NavLayer is the current layer, which is the case everywhere we call it.
2606     const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
2607     if (g.NavWindow != window)
2608         g.NavInitRequest = false;
2609     g.NavId = id;
2610     g.NavWindow = window;
2611     g.NavLayer = nav_layer;
2612     window->NavLastIds[nav_layer] = id;
2613     if (window->DC.LastItemId == id)
2614         window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos);
2615 
2616     if (g.ActiveIdSource == ImGuiInputSource_Nav)
2617         g.NavDisableMouseHover = true;
2618     else
2619         g.NavDisableHighlight = true;
2620 }
2621 
ClearActiveID()2622 void ImGui::ClearActiveID()
2623 {
2624     SetActiveID(0, NULL);
2625 }
2626 
SetHoveredID(ImGuiID id)2627 void ImGui::SetHoveredID(ImGuiID id)
2628 {
2629     ImGuiContext& g = *GImGui;
2630     g.HoveredId = id;
2631     g.HoveredIdAllowOverlap = false;
2632     if (id != 0 && g.HoveredIdPreviousFrame != id)
2633         g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
2634 }
2635 
GetHoveredID()2636 ImGuiID ImGui::GetHoveredID()
2637 {
2638     ImGuiContext& g = *GImGui;
2639     return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
2640 }
2641 
KeepAliveID(ImGuiID id)2642 void ImGui::KeepAliveID(ImGuiID id)
2643 {
2644     ImGuiContext& g = *GImGui;
2645     if (g.ActiveId == id)
2646         g.ActiveIdIsAlive = id;
2647     if (g.ActiveIdPreviousFrame == id)
2648         g.ActiveIdPreviousFrameIsAlive = true;
2649 }
2650 
MarkItemEdited(ImGuiID id)2651 void ImGui::MarkItemEdited(ImGuiID id)
2652 {
2653     // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
2654     // 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.
2655     (void)id; // Avoid unused variable warnings when asserts are compiled out.
2656     ImGuiContext& g = *GImGui;
2657     IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive);
2658     //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
2659     g.ActiveIdHasBeenEdited = true;
2660     g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
2661 }
2662 
IsWindowContentHoverable(ImGuiWindow * window,ImGuiHoveredFlags flags)2663 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
2664 {
2665     // An active popup disable hovering on other windows (apart from its own children)
2666     // FIXME-OPT: This could be cached/stored within the window.
2667     ImGuiContext& g = *GImGui;
2668     if (g.NavWindow)
2669         if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
2670             if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
2671             {
2672                 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
2673                 // NB: The order of those two tests is important because Modal windows are also Popups.
2674                 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
2675                     return false;
2676                 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
2677                     return false;
2678             }
2679 
2680     return true;
2681 }
2682 
2683 // Advance cursor given item size for layout.
ItemSize(const ImVec2 & size,float text_offset_y)2684 void ImGui::ItemSize(const ImVec2& size, float text_offset_y)
2685 {
2686     ImGuiContext& g = *GImGui;
2687     ImGuiWindow* window = g.CurrentWindow;
2688     if (window->SkipItems)
2689         return;
2690 
2691     // Always align ourselves on pixel boundaries
2692     const float line_height = ImMax(window->DC.CurrentLineSize.y, size.y);
2693     const float text_base_offset = ImMax(window->DC.CurrentLineTextBaseOffset, text_offset_y);
2694     //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]
2695     window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y);
2696     window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
2697     window->DC.CursorPos.y = (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y);
2698     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
2699     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
2700     //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
2701 
2702     window->DC.PrevLineSize.y = line_height;
2703     window->DC.PrevLineTextBaseOffset = text_base_offset;
2704     window->DC.CurrentLineSize.y = window->DC.CurrentLineTextBaseOffset = 0.0f;
2705 
2706     // Horizontal layout mode
2707     if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
2708         SameLine();
2709 }
2710 
ItemSize(const ImRect & bb,float text_offset_y)2711 void ImGui::ItemSize(const ImRect& bb, float text_offset_y)
2712 {
2713     ItemSize(bb.GetSize(), text_offset_y);
2714 }
2715 
2716 // Declare item bounding box for clipping and interaction.
2717 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
2718 // declare their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd().
ItemAdd(const ImRect & bb,ImGuiID id,const ImRect * nav_bb_arg)2719 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg)
2720 {
2721     ImGuiContext& g = *GImGui;
2722     ImGuiWindow* window = g.CurrentWindow;
2723 
2724     if (id != 0)
2725     {
2726         // Navigation processing runs prior to clipping early-out
2727         //  (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
2728         //  (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests unfortunately, but it is still limited to one window.
2729         //      it may not scale very well for windows with ten of thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
2730         //      We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick)
2731         window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask;
2732         if (g.NavId == id || g.NavAnyRequest)
2733             if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
2734                 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
2735                     NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
2736     }
2737 
2738     window->DC.LastItemId = id;
2739     window->DC.LastItemRect = bb;
2740     window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None;
2741 
2742 #ifdef IMGUI_ENABLE_TEST_ENGINE
2743     if (id != 0)
2744         ImGuiTestEngineHook_ItemAdd(&g, bb, id);
2745 #endif
2746 
2747     // Clipping test
2748     const bool is_clipped = IsClippedEx(bb, id, false);
2749     if (is_clipped)
2750         return false;
2751     //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
2752 
2753     // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
2754     if (IsMouseHoveringRect(bb.Min, bb.Max))
2755         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect;
2756     return true;
2757 }
2758 
2759 // This is roughly matching the behavior of internal-facing ItemHoverable()
2760 // - 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()
2761 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
IsItemHovered(ImGuiHoveredFlags flags)2762 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
2763 {
2764     ImGuiContext& g = *GImGui;
2765     ImGuiWindow* window = g.CurrentWindow;
2766     if (g.NavDisableMouseHover && !g.NavDisableHighlight)
2767         return IsItemFocused();
2768 
2769     // Test for bounding box overlap, as updated as ItemAdd()
2770     if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
2771         return false;
2772     IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0);   // Flags not supported by this function
2773 
2774     // Test if we are hovering the right window (our window could be behind another window)
2775     // [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.
2776     // 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.
2777     //if (g.HoveredWindow != window)
2778     //    return false;
2779     if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
2780         return false;
2781 
2782     // Test if another item is active (e.g. being dragged)
2783     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
2784         if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
2785             return false;
2786 
2787     // Test if interactions on this window are blocked by an active popup or modal
2788     if (!IsWindowContentHoverable(window, flags))
2789         return false;
2790 
2791     // Test if the item is disabled
2792     if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
2793         return false;
2794 
2795     // Special handling for the 1st item after Begin() which represent the title bar. When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect tht case.
2796     if (window->DC.LastItemId == window->MoveId && window->WriteAccessed)
2797         return false;
2798     return true;
2799 }
2800 
2801 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
ItemHoverable(const ImRect & bb,ImGuiID id)2802 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
2803 {
2804     ImGuiContext& g = *GImGui;
2805     if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
2806         return false;
2807 
2808     ImGuiWindow* window = g.CurrentWindow;
2809     if (g.HoveredWindow != window)
2810         return false;
2811     if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
2812         return false;
2813     if (!IsMouseHoveringRect(bb.Min, bb.Max))
2814         return false;
2815     if (g.NavDisableMouseHover || !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
2816         return false;
2817     if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
2818         return false;
2819 
2820     SetHoveredID(id);
2821     return true;
2822 }
2823 
IsClippedEx(const ImRect & bb,ImGuiID id,bool clip_even_when_logged)2824 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
2825 {
2826     ImGuiContext& g = *GImGui;
2827     ImGuiWindow* window = g.CurrentWindow;
2828     if (!bb.Overlaps(window->ClipRect))
2829         if (id == 0 || id != g.ActiveId)
2830             if (clip_even_when_logged || !g.LogEnabled)
2831                 return true;
2832     return false;
2833 }
2834 
FocusableItemRegister(ImGuiWindow * window,ImGuiID id,bool tab_stop)2835 bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop)
2836 {
2837     ImGuiContext& g = *GImGui;
2838 
2839     const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0;
2840     window->FocusIdxAllCounter++;
2841     if (is_tab_stop)
2842         window->FocusIdxTabCounter++;
2843 
2844     // Process keyboard input at this point: TAB/Shift-TAB to tab out of the currently focused item.
2845     // Note that we can always TAB out of a widget that doesn't allow tabbing in.
2846     if (tab_stop && (g.ActiveId == id) && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab))
2847         window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (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.
2848 
2849     if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent)
2850         return true;
2851     if (is_tab_stop && window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent)
2852     {
2853         g.NavJustTabbedId = id;
2854         return true;
2855     }
2856 
2857     return false;
2858 }
2859 
FocusableItemUnregister(ImGuiWindow * window)2860 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
2861 {
2862     window->FocusIdxAllCounter--;
2863     window->FocusIdxTabCounter--;
2864 }
2865 
CalcItemSize(ImVec2 size,float default_x,float default_y)2866 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_x, float default_y)
2867 {
2868     ImGuiContext& g = *GImGui;
2869     ImVec2 content_max;
2870     if (size.x < 0.0f || size.y < 0.0f)
2871         content_max = g.CurrentWindow->Pos + GetContentRegionMax();
2872     if (size.x <= 0.0f)
2873         size.x = (size.x == 0.0f) ? default_x : ImMax(content_max.x - g.CurrentWindow->DC.CursorPos.x, 4.0f) + size.x;
2874     if (size.y <= 0.0f)
2875         size.y = (size.y == 0.0f) ? default_y : ImMax(content_max.y - g.CurrentWindow->DC.CursorPos.y, 4.0f) + size.y;
2876     return size;
2877 }
2878 
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)2879 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
2880 {
2881     if (wrap_pos_x < 0.0f)
2882         return 0.0f;
2883 
2884     ImGuiWindow* window = GetCurrentWindowRead();
2885     if (wrap_pos_x == 0.0f)
2886         wrap_pos_x = GetContentRegionMax().x + window->Pos.x;
2887     else if (wrap_pos_x > 0.0f)
2888         wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
2889 
2890     return ImMax(wrap_pos_x - pos.x, 1.0f);
2891 }
2892 
MemAlloc(size_t size)2893 void* ImGui::MemAlloc(size_t size)
2894 {
2895     if (ImGuiContext* ctx = GImGui)
2896         ctx->IO.MetricsActiveAllocations++;
2897     return GImAllocatorAllocFunc(size, GImAllocatorUserData);
2898 }
2899 
MemFree(void * ptr)2900 void ImGui::MemFree(void* ptr)
2901 {
2902     if (ptr)
2903         if (ImGuiContext* ctx = GImGui)
2904             ctx->IO.MetricsActiveAllocations--;
2905     return GImAllocatorFreeFunc(ptr, GImAllocatorUserData);
2906 }
2907 
GetClipboardText()2908 const char* ImGui::GetClipboardText()
2909 {
2910     return GImGui->IO.GetClipboardTextFn ? GImGui->IO.GetClipboardTextFn(GImGui->IO.ClipboardUserData) : "";
2911 }
2912 
SetClipboardText(const char * text)2913 void ImGui::SetClipboardText(const char* text)
2914 {
2915     if (GImGui->IO.SetClipboardTextFn)
2916         GImGui->IO.SetClipboardTextFn(GImGui->IO.ClipboardUserData, text);
2917 }
2918 
GetVersion()2919 const char* ImGui::GetVersion()
2920 {
2921     return IMGUI_VERSION;
2922 }
2923 
2924 // Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself
2925 // 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()2926 ImGuiContext* ImGui::GetCurrentContext()
2927 {
2928     return GImGui;
2929 }
2930 
SetCurrentContext(ImGuiContext * ctx)2931 void ImGui::SetCurrentContext(ImGuiContext* ctx)
2932 {
2933 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
2934     IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
2935 #else
2936     GImGui = ctx;
2937 #endif
2938 }
2939 
2940 // Helper function to verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
2941 // If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. you may see different structures from what imgui.cpp sees which is highly problematic.
DebugCheckVersionAndDataLayout(const char * version,size_t sz_io,size_t sz_style,size_t sz_vec2,size_t sz_vec4,size_t sz_vert)2942 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)
2943 {
2944     bool error = false;
2945     if (strcmp(version, IMGUI_VERSION)!=0) { error = true; IM_ASSERT(strcmp(version,IMGUI_VERSION)==0 && "Mismatched version string!");  }
2946     if (sz_io    != sizeof(ImGuiIO))       { error = true; IM_ASSERT(sz_io    == sizeof(ImGuiIO)      && "Mismatched struct layout!"); }
2947     if (sz_style != sizeof(ImGuiStyle))    { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle)   && "Mismatched struct layout!"); }
2948     if (sz_vec2  != sizeof(ImVec2))        { error = true; IM_ASSERT(sz_vec2  == sizeof(ImVec2)       && "Mismatched struct layout!"); }
2949     if (sz_vec4  != sizeof(ImVec4))        { error = true; IM_ASSERT(sz_vec4  == sizeof(ImVec4)       && "Mismatched struct layout!"); }
2950     if (sz_vert  != sizeof(ImDrawVert))    { error = true; IM_ASSERT(sz_vert  == sizeof(ImDrawVert)   && "Mismatched struct layout!"); }
2951     return !error;
2952 }
2953 
SetAllocatorFunctions(void * (* alloc_func)(size_t sz,void * user_data),void (* free_func)(void * ptr,void * user_data),void * user_data)2954 void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void(*free_func)(void* ptr, void* user_data), void* user_data)
2955 {
2956     GImAllocatorAllocFunc = alloc_func;
2957     GImAllocatorFreeFunc = free_func;
2958     GImAllocatorUserData = user_data;
2959 }
2960 
CreateContext(ImFontAtlas * shared_font_atlas)2961 ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
2962 {
2963     ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
2964     if (GImGui == NULL)
2965         SetCurrentContext(ctx);
2966     Initialize(ctx);
2967     return ctx;
2968 }
2969 
DestroyContext(ImGuiContext * ctx)2970 void ImGui::DestroyContext(ImGuiContext* ctx)
2971 {
2972     if (ctx == NULL)
2973         ctx = GImGui;
2974     Shutdown(ctx);
2975     if (GImGui == ctx)
2976         SetCurrentContext(NULL);
2977     IM_DELETE(ctx);
2978 }
2979 
GetIO()2980 ImGuiIO& ImGui::GetIO()
2981 {
2982     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
2983     return GImGui->IO;
2984 }
2985 
GetStyle()2986 ImGuiStyle& ImGui::GetStyle()
2987 {
2988     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
2989     return GImGui->Style;
2990 }
2991 
2992 // Same value as passed to the old io.RenderDrawListsFn function. Valid after Render() and until the next call to NewFrame()
GetDrawData()2993 ImDrawData* ImGui::GetDrawData()
2994 {
2995     ImGuiContext& g = *GImGui;
2996     return g.DrawData.Valid ? &g.DrawData : NULL;
2997 }
2998 
GetTime()2999 double ImGui::GetTime()
3000 {
3001     return GImGui->Time;
3002 }
3003 
GetFrameCount()3004 int ImGui::GetFrameCount()
3005 {
3006     return GImGui->FrameCount;
3007 }
3008 
GetOverlayDrawList(ImGuiWindow *)3009 static ImDrawList* GetOverlayDrawList(ImGuiWindow*)
3010 {
3011     // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'viewport' branches.
3012     return &GImGui->OverlayDrawList;
3013 }
3014 
GetOverlayDrawList()3015 ImDrawList* ImGui::GetOverlayDrawList()
3016 {
3017     return &GImGui->OverlayDrawList;
3018 }
3019 
GetDrawListSharedData()3020 ImDrawListSharedData* ImGui::GetDrawListSharedData()
3021 {
3022     return &GImGui->DrawListSharedData;
3023 }
3024 
StartMouseMovingWindow(ImGuiWindow * window)3025 void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
3026 {
3027     // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
3028     // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
3029     // This is because we want ActiveId to be set even when the window is stuck from moving.
3030     ImGuiContext& g = *GImGui;
3031     FocusWindow(window);
3032     SetActiveID(window->MoveId, window);
3033     g.NavDisableHighlight = true;
3034     g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos;
3035     if (!(window->Flags & ImGuiWindowFlags_NoMove) && !(window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
3036         g.MovingWindow = window;
3037 }
3038 
3039 // Handle mouse moving window
3040 // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
UpdateMouseMovingWindowNewFrame()3041 void ImGui::UpdateMouseMovingWindowNewFrame()
3042 {
3043     ImGuiContext& g = *GImGui;
3044     if (g.MovingWindow != NULL)
3045     {
3046         // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
3047         // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
3048         KeepAliveID(g.ActiveId);
3049         IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
3050         ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
3051         if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
3052         {
3053             ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
3054             if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
3055             {
3056                 MarkIniSettingsDirty(moving_window);
3057                 SetWindowPos(moving_window, pos, ImGuiCond_Always);
3058             }
3059             FocusWindow(g.MovingWindow);
3060         }
3061         else
3062         {
3063             ClearActiveID();
3064             g.MovingWindow = NULL;
3065         }
3066     }
3067     else
3068     {
3069         // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
3070         if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
3071         {
3072             KeepAliveID(g.ActiveId);
3073             if (!g.IO.MouseDown[0])
3074                 ClearActiveID();
3075         }
3076     }
3077 }
3078 
3079 // Initiate moving window, handle left-click and right-click focus
UpdateMouseMovingWindowEndFrame()3080 void ImGui::UpdateMouseMovingWindowEndFrame()
3081 {
3082     // Initiate moving window
3083     ImGuiContext& g = *GImGui;
3084     if (g.ActiveId != 0 || g.HoveredId != 0)
3085         return;
3086 
3087     // Unless we just made a window/popup appear
3088     if (g.NavWindow && g.NavWindow->Appearing)
3089         return;
3090 
3091     // Click to focus window and start moving (after we're done with all our widgets)
3092     if (g.IO.MouseClicked[0])
3093     {
3094         if (g.HoveredRootWindow != NULL)
3095         {
3096             StartMouseMovingWindow(g.HoveredWindow);
3097             if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoTitleBar))
3098                 if (!g.HoveredRootWindow->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
3099                     g.MovingWindow = NULL;
3100         }
3101         else if (g.NavWindow != NULL && GetFrontMostPopupModal() == NULL)
3102         {
3103             FocusWindow(NULL);  // Clicking on void disable focus
3104         }
3105     }
3106 
3107     // With right mouse button we close popups without changing focus
3108     // (The left mouse button path calls FocusWindow which will lead NewFrame->ClosePopupsOverWindow to trigger)
3109     if (g.IO.MouseClicked[1])
3110     {
3111         // Find the top-most window between HoveredWindow and the front most Modal Window.
3112         // This is where we can trim the popup stack.
3113         ImGuiWindow* modal = GetFrontMostPopupModal();
3114         bool hovered_window_above_modal = false;
3115         if (modal == NULL)
3116             hovered_window_above_modal = true;
3117         for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--)
3118         {
3119             ImGuiWindow* window = g.Windows[i];
3120             if (window == modal)
3121                 break;
3122             if (window == g.HoveredWindow)
3123                 hovered_window_above_modal = true;
3124         }
3125         ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal);
3126     }
3127 }
3128 
IsWindowActiveAndVisible(ImGuiWindow * window)3129 static bool IsWindowActiveAndVisible(ImGuiWindow* window)
3130 {
3131     return (window->Active) && (!window->Hidden);
3132 }
3133 
UpdateMouseInputs()3134 static void ImGui::UpdateMouseInputs()
3135 {
3136     ImGuiContext& g = *GImGui;
3137 
3138     // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
3139     if (IsMousePosValid(&g.IO.MousePos))
3140         g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos);
3141 
3142     // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
3143     if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
3144         g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
3145     else
3146         g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
3147     if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
3148         g.NavDisableMouseHover = false;
3149 
3150     g.IO.MousePosPrev = g.IO.MousePos;
3151     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3152     {
3153         g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
3154         g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
3155         g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
3156         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;
3157         g.IO.MouseDoubleClicked[i] = false;
3158         if (g.IO.MouseClicked[i])
3159         {
3160             if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime)
3161             {
3162                 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3163                 if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
3164                     g.IO.MouseDoubleClicked[i] = true;
3165                 g.IO.MouseClickedTime[i] = -FLT_MAX;    // so the third click isn't turned into a double-click
3166             }
3167             else
3168             {
3169                 g.IO.MouseClickedTime[i] = g.Time;
3170             }
3171             g.IO.MouseClickedPos[i] = g.IO.MousePos;
3172             g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
3173             g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
3174         }
3175         else if (g.IO.MouseDown[i])
3176         {
3177             // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
3178             ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3179             g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
3180             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);
3181             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);
3182         }
3183         if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
3184             g.NavDisableMouseHover = false;
3185     }
3186 }
3187 
UpdateMouseWheel()3188 void ImGui::UpdateMouseWheel()
3189 {
3190     ImGuiContext& g = *GImGui;
3191     if (!g.HoveredWindow || g.HoveredWindow->Collapsed)
3192         return;
3193     if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f)
3194         return;
3195 
3196     // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent (unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set).
3197     ImGuiWindow* window = g.HoveredWindow;
3198     ImGuiWindow* scroll_window = window;
3199     while ((scroll_window->Flags & ImGuiWindowFlags_ChildWindow) && (scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoScrollbar) && !(scroll_window->Flags & ImGuiWindowFlags_NoMouseInputs) && scroll_window->ParentWindow)
3200         scroll_window = scroll_window->ParentWindow;
3201     const bool scroll_allowed = !(scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoMouseInputs);
3202 
3203     if (g.IO.MouseWheel != 0.0f)
3204     {
3205         if (g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
3206         {
3207             // Zoom / Scale window
3208             const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
3209             const float scale = new_font_scale / window->FontWindowScale;
3210             window->FontWindowScale = new_font_scale;
3211 
3212             const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
3213             window->Pos += offset;
3214             window->Size *= scale;
3215             window->SizeFull *= scale;
3216         }
3217         else if (!g.IO.KeyCtrl && scroll_allowed)
3218         {
3219             // Mouse wheel vertical scrolling
3220             float scroll_amount = 5 * scroll_window->CalcFontSize();
3221             scroll_amount = (float)(int)ImMin(scroll_amount, (scroll_window->ContentsRegionRect.GetHeight() + scroll_window->WindowPadding.y * 2.0f) * 0.67f);
3222             SetWindowScrollY(scroll_window, scroll_window->Scroll.y - g.IO.MouseWheel * scroll_amount);
3223         }
3224     }
3225     if (g.IO.MouseWheelH != 0.0f && scroll_allowed && !g.IO.KeyCtrl)
3226     {
3227         // Mouse wheel horizontal scrolling (for hardware that supports it)
3228         float scroll_amount = scroll_window->CalcFontSize();
3229         SetWindowScrollX(scroll_window, scroll_window->Scroll.x - g.IO.MouseWheelH * scroll_amount);
3230     }
3231 }
3232 
3233 // 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()3234 void ImGui::UpdateHoveredWindowAndCaptureFlags()
3235 {
3236     ImGuiContext& g = *GImGui;
3237 
3238     // Find the window hovered by mouse:
3239     // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3240     // - 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.
3241     // - 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.
3242     FindHoveredWindow();
3243 
3244     // Modal windows prevents cursor from hovering behind them.
3245     ImGuiWindow* modal_window = GetFrontMostPopupModal();
3246     if (modal_window)
3247         if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window))
3248             g.HoveredRootWindow = g.HoveredWindow = NULL;
3249 
3250     // Disabled mouse?
3251     if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
3252         g.HoveredWindow = g.HoveredRootWindow = NULL;
3253 
3254     // 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.
3255     int mouse_earliest_button_down = -1;
3256     bool mouse_any_down = false;
3257     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3258     {
3259         if (g.IO.MouseClicked[i])
3260             g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty());
3261         mouse_any_down |= g.IO.MouseDown[i];
3262         if (g.IO.MouseDown[i])
3263             if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
3264                 mouse_earliest_button_down = i;
3265     }
3266     const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
3267 
3268     // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
3269     // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
3270     const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
3271     if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
3272         g.HoveredWindow = g.HoveredRootWindow = NULL;
3273 
3274     // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to imgui + app)
3275     if (g.WantCaptureMouseNextFrame != -1)
3276         g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
3277     else
3278         g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty());
3279 
3280     // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to imgui + app)
3281     if (g.WantCaptureKeyboardNextFrame != -1)
3282         g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
3283     else
3284         g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
3285     if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
3286         g.IO.WantCaptureKeyboard = true;
3287 
3288     // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
3289     g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
3290 }
3291 
NewFrame()3292 void ImGui::NewFrame()
3293 {
3294     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
3295     ImGuiContext& g = *GImGui;
3296 
3297 #ifdef IMGUI_ENABLE_TEST_ENGINE
3298     ImGuiTestEngineHook_PreNewFrame(&g);
3299 #endif
3300 
3301     // Check user data
3302     // (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)
3303     IM_ASSERT(g.Initialized);
3304     IM_ASSERT(g.IO.DeltaTime >= 0.0f                                    && "Need a positive DeltaTime (zero is tolerated but will cause some timing issues)");
3305     IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f  && "Invalid DisplaySize value");
3306     IM_ASSERT(g.IO.Fonts->Fonts.Size > 0                                && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3307     IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded()                          && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3308     IM_ASSERT(g.Style.CurveTessellationTol > 0.0f                       && "Invalid style setting");
3309     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)");
3310     IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount)  && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
3311     for (int n = 0; n < ImGuiKey_COUNT; n++)
3312         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)");
3313 
3314     // 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)
3315     if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
3316         IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
3317 
3318     // Perform simple check: the beta io.ConfigWindowsResizeFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
3319     if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
3320         g.IO.ConfigWindowsResizeFromEdges = false;
3321 
3322     // Load settings on first frame (if not explicitly loaded manually before)
3323     if (!g.SettingsLoaded)
3324     {
3325         IM_ASSERT(g.SettingsWindows.empty());
3326         if (g.IO.IniFilename)
3327             LoadIniSettingsFromDisk(g.IO.IniFilename);
3328         g.SettingsLoaded = true;
3329     }
3330 
3331     // Save settings (with a delay after the last modification, so we don't spam disk too much)
3332     if (g.SettingsDirtyTimer > 0.0f)
3333     {
3334         g.SettingsDirtyTimer -= g.IO.DeltaTime;
3335         if (g.SettingsDirtyTimer <= 0.0f)
3336         {
3337             if (g.IO.IniFilename != NULL)
3338                 SaveIniSettingsToDisk(g.IO.IniFilename);
3339             else
3340                 g.IO.WantSaveIniSettings = true;  // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
3341             g.SettingsDirtyTimer = 0.0f;
3342         }
3343     }
3344 
3345     g.Time += g.IO.DeltaTime;
3346     g.FrameScopeActive = true;
3347     g.FrameCount += 1;
3348     g.TooltipOverrideCount = 0;
3349     g.WindowsActiveCount = 0;
3350 
3351     // Setup current font and draw list
3352     g.IO.Fonts->Locked = true;
3353     SetCurrentFont(GetDefaultFont());
3354     IM_ASSERT(g.Font->IsLoaded());
3355     g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
3356     g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
3357 
3358     g.OverlayDrawList.Clear();
3359     g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID);
3360     g.OverlayDrawList.PushClipRectFullScreen();
3361     g.OverlayDrawList.Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0);
3362 
3363     // Mark rendering data as invalid to prevent user who may have a handle on it to use it
3364     g.DrawData.Clear();
3365 
3366     // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
3367     if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
3368         KeepAliveID(g.DragDropPayload.SourceId);
3369 
3370     // Clear reference to active widget if the widget isn't alive anymore
3371     if (!g.HoveredIdPreviousFrame)
3372         g.HoveredIdTimer = 0.0f;
3373     if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
3374         g.HoveredIdNotActiveTimer = 0.0f;
3375     if (g.HoveredId)
3376         g.HoveredIdTimer += g.IO.DeltaTime;
3377     if (g.HoveredId && g.ActiveId != g.HoveredId)
3378         g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
3379     g.HoveredIdPreviousFrame = g.HoveredId;
3380     g.HoveredId = 0;
3381     g.HoveredIdAllowOverlap = false;
3382     if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
3383         ClearActiveID();
3384     if (g.ActiveId)
3385         g.ActiveIdTimer += g.IO.DeltaTime;
3386     g.LastActiveIdTimer += g.IO.DeltaTime;
3387     g.ActiveIdPreviousFrame = g.ActiveId;
3388     g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
3389     g.ActiveIdPreviousFrameHasBeenEdited = g.ActiveIdHasBeenEdited;
3390     g.ActiveIdIsAlive = 0;
3391     g.ActiveIdPreviousFrameIsAlive = false;
3392     g.ActiveIdIsJustActivated = false;
3393     if (g.ScalarAsInputTextId && g.ActiveId != g.ScalarAsInputTextId)
3394         g.ScalarAsInputTextId = 0;
3395 
3396     // Drag and drop
3397     g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
3398     g.DragDropAcceptIdCurr = 0;
3399     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
3400     g.DragDropWithinSourceOrTarget = false;
3401 
3402     // Update keyboard input state
3403     memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
3404     for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
3405         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;
3406 
3407     // Update gamepad/keyboard directional navigation
3408     NavUpdate();
3409 
3410     // Update mouse input state
3411     UpdateMouseInputs();
3412 
3413     // Calculate frame-rate for the user, as a purely luxurious feature
3414     g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
3415     g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
3416     g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
3417     g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX;
3418 
3419     // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
3420     UpdateMouseMovingWindowNewFrame();
3421     UpdateHoveredWindowAndCaptureFlags();
3422 
3423     // Background darkening/whitening
3424     if (GetFrontMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
3425         g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
3426     else
3427         g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
3428 
3429     g.MouseCursor = ImGuiMouseCursor_Arrow;
3430     g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
3431     g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
3432 
3433     // Mouse wheel scrolling, scale
3434     UpdateMouseWheel();
3435 
3436     // Pressing TAB activate widget focus
3437     if (g.ActiveId == 0 && g.NavWindow != NULL && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab, false))
3438     {
3439         if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
3440             g.NavWindow->FocusIdxTabRequestNext = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1);
3441         else
3442             g.NavWindow->FocusIdxTabRequestNext = g.IO.KeyShift ? -1 : 0;
3443     }
3444     g.NavIdTabCounter = INT_MAX;
3445 
3446     // Mark all windows as not visible
3447     IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size);
3448     for (int i = 0; i != g.Windows.Size; i++)
3449     {
3450         ImGuiWindow* window = g.Windows[i];
3451         window->WasActive = window->Active;
3452         window->Active = false;
3453         window->WriteAccessed = false;
3454     }
3455 
3456     // Closing the focused window restore focus to the first active root window in descending z-order
3457     if (g.NavWindow && !g.NavWindow->WasActive)
3458         FocusPreviousWindowIgnoringOne(NULL);
3459 
3460     // No window should be open at the beginning of the frame.
3461     // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
3462     g.CurrentWindowStack.resize(0);
3463     g.BeginPopupStack.resize(0);
3464     ClosePopupsOverWindow(g.NavWindow);
3465 
3466     // Create implicit/fallback window - which we will only render it if the user has added something to it.
3467     // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
3468     // This fallback is particularly important as it avoid ImGui:: calls from crashing.
3469     SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver);
3470     Begin("Debug##Default");
3471     g.FrameScopePushedImplicitWindow = true;
3472 
3473 #ifdef IMGUI_ENABLE_TEST_ENGINE
3474     ImGuiTestEngineHook_PostNewFrame(&g);
3475 #endif
3476 }
3477 
Initialize(ImGuiContext * context)3478 void ImGui::Initialize(ImGuiContext* context)
3479 {
3480     ImGuiContext& g = *context;
3481     IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
3482 
3483     // Add .ini handle for ImGuiWindow type
3484     ImGuiSettingsHandler ini_handler;
3485     ini_handler.TypeName = "Window";
3486     ini_handler.TypeHash = ImHash("Window", 0, 0);
3487     ini_handler.ReadOpenFn = SettingsHandlerWindow_ReadOpen;
3488     ini_handler.ReadLineFn = SettingsHandlerWindow_ReadLine;
3489     ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll;
3490     g.SettingsHandlers.push_front(ini_handler);
3491 
3492     g.Initialized = true;
3493 }
3494 
3495 // This function is merely here to free heap allocations.
Shutdown(ImGuiContext * context)3496 void ImGui::Shutdown(ImGuiContext* context)
3497 {
3498     // 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)
3499     ImGuiContext& g = *context;
3500     if (g.IO.Fonts && g.FontAtlasOwnedByContext)
3501     {
3502         g.IO.Fonts->Locked = false;
3503         IM_DELETE(g.IO.Fonts);
3504     }
3505     g.IO.Fonts = NULL;
3506 
3507     // Cleanup of other data are conditional on actually having initialized ImGui.
3508     if (!g.Initialized)
3509         return;
3510 
3511     // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
3512     if (g.SettingsLoaded && g.IO.IniFilename != NULL)
3513     {
3514         ImGuiContext* backup_context = GImGui;
3515         SetCurrentContext(context);
3516         SaveIniSettingsToDisk(g.IO.IniFilename);
3517         SetCurrentContext(backup_context);
3518     }
3519 
3520     // Clear everything else
3521     for (int i = 0; i < g.Windows.Size; i++)
3522         IM_DELETE(g.Windows[i]);
3523     g.Windows.clear();
3524     g.WindowsFocusOrder.clear();
3525     g.WindowsSortBuffer.clear();
3526     g.CurrentWindow = NULL;
3527     g.CurrentWindowStack.clear();
3528     g.WindowsById.Clear();
3529     g.NavWindow = NULL;
3530     g.HoveredWindow = NULL;
3531     g.HoveredRootWindow = NULL;
3532     g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
3533     g.MovingWindow = NULL;
3534     g.ColorModifiers.clear();
3535     g.StyleModifiers.clear();
3536     g.FontStack.clear();
3537     g.OpenPopupStack.clear();
3538     g.BeginPopupStack.clear();
3539     g.DrawDataBuilder.ClearFreeMemory();
3540     g.OverlayDrawList.ClearFreeMemory();
3541     g.PrivateClipboard.clear();
3542     g.InputTextState.TextW.clear();
3543     g.InputTextState.InitialText.clear();
3544     g.InputTextState.TempBuffer.clear();
3545 
3546     for (int i = 0; i < g.SettingsWindows.Size; i++)
3547         IM_DELETE(g.SettingsWindows[i].Name);
3548     g.SettingsWindows.clear();
3549     g.SettingsHandlers.clear();
3550 
3551     if (g.LogFile && g.LogFile != stdout)
3552     {
3553         fclose(g.LogFile);
3554         g.LogFile = NULL;
3555     }
3556     g.LogClipboard.clear();
3557 
3558     g.Initialized = false;
3559 }
3560 
3561 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)3562 static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
3563 {
3564     const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
3565     const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
3566     if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
3567         return d;
3568     if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
3569         return d;
3570     return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
3571 }
3572 
AddWindowToSortBuffer(ImVector<ImGuiWindow * > * out_sorted_windows,ImGuiWindow * window)3573 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
3574 {
3575     out_sorted_windows->push_back(window);
3576     if (window->Active)
3577     {
3578         int count = window->DC.ChildWindows.Size;
3579         if (count > 1)
3580             ImQsort(window->DC.ChildWindows.begin(), (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
3581         for (int i = 0; i < count; i++)
3582         {
3583             ImGuiWindow* child = window->DC.ChildWindows[i];
3584             if (child->Active)
3585                 AddWindowToSortBuffer(out_sorted_windows, child);
3586         }
3587     }
3588 }
3589 
AddDrawListToDrawData(ImVector<ImDrawList * > * out_list,ImDrawList * draw_list)3590 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
3591 {
3592     if (draw_list->CmdBuffer.empty())
3593         return;
3594 
3595     // Remove trailing command if unused
3596     ImDrawCmd& last_cmd = draw_list->CmdBuffer.back();
3597     if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL)
3598     {
3599         draw_list->CmdBuffer.pop_back();
3600         if (draw_list->CmdBuffer.empty())
3601             return;
3602     }
3603 
3604     // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. May trigger for you if you are using PrimXXX functions incorrectly.
3605     IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
3606     IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
3607     IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
3608 
3609     // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
3610     // If this assert triggers because you are drawing lots of stuff manually:
3611     // A) Make sure you are coarse clipping, because ImDrawList let all your vertices pass. You can use the Metrics window to inspect draw list contents.
3612     // B) If you need/want meshes with more than 64K vertices, uncomment the '#define ImDrawIdx unsigned int' line in imconfig.h to set the index size to 4 bytes.
3613     //    You'll need to handle the 4-bytes indices to your renderer. For example, the OpenGL example code detect index size at compile-time by doing:
3614     //      glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
3615     //    Your own engine or render API may use different parameters or function calls to specify index sizes. 2 and 4 bytes indices are generally supported by most API.
3616     // C) If for some reason you cannot use 4 bytes indices or don't want to, a workaround is to call BeginChild()/EndChild() before reaching the 64K limit to split your draw commands in multiple draw lists.
3617     if (sizeof(ImDrawIdx) == 2)
3618         IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
3619 
3620     out_list->push_back(draw_list);
3621 }
3622 
AddWindowToDrawData(ImVector<ImDrawList * > * out_render_list,ImGuiWindow * window)3623 static void AddWindowToDrawData(ImVector<ImDrawList*>* out_render_list, ImGuiWindow* window)
3624 {
3625     ImGuiContext& g = *GImGui;
3626     g.IO.MetricsRenderWindows++;
3627     AddDrawListToDrawData(out_render_list, window->DrawList);
3628     for (int i = 0; i < window->DC.ChildWindows.Size; i++)
3629     {
3630         ImGuiWindow* child = window->DC.ChildWindows[i];
3631         if (IsWindowActiveAndVisible(child)) // clipped children may have been marked not active
3632             AddWindowToDrawData(out_render_list, child);
3633     }
3634 }
3635 
AddWindowToDrawDataSelectLayer(ImGuiWindow * window)3636 static void AddWindowToDrawDataSelectLayer(ImGuiWindow* window)
3637 {
3638     ImGuiContext& g = *GImGui;
3639     if (window->Flags & ImGuiWindowFlags_Tooltip)
3640         AddWindowToDrawData(&g.DrawDataBuilder.Layers[1], window);
3641     else
3642         AddWindowToDrawData(&g.DrawDataBuilder.Layers[0], window);
3643 }
3644 
FlattenIntoSingleLayer()3645 void ImDrawDataBuilder::FlattenIntoSingleLayer()
3646 {
3647     int n = Layers[0].Size;
3648     int size = n;
3649     for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
3650         size += Layers[i].Size;
3651     Layers[0].resize(size);
3652     for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
3653     {
3654         ImVector<ImDrawList*>& layer = Layers[layer_n];
3655         if (layer.empty())
3656             continue;
3657         memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
3658         n += layer.Size;
3659         layer.resize(0);
3660     }
3661 }
3662 
SetupDrawData(ImVector<ImDrawList * > * draw_lists,ImDrawData * draw_data)3663 static void SetupDrawData(ImVector<ImDrawList*>* draw_lists, ImDrawData* draw_data)
3664 {
3665     ImGuiIO& io = ImGui::GetIO();
3666     draw_data->Valid = true;
3667     draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
3668     draw_data->CmdListsCount = draw_lists->Size;
3669     draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
3670     draw_data->DisplayPos = ImVec2(0.0f, 0.0f);
3671     draw_data->DisplaySize = io.DisplaySize;
3672     for (int n = 0; n < draw_lists->Size; n++)
3673     {
3674         draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size;
3675         draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size;
3676     }
3677 }
3678 
3679 // When using this function it is sane to ensure that float are perfectly rounded to integer values, to that e.g. (int)(max.x-min.x) in user's render produce correct result.
PushClipRect(const ImVec2 & clip_rect_min,const ImVec2 & clip_rect_max,bool intersect_with_current_clip_rect)3680 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
3681 {
3682     ImGuiWindow* window = GetCurrentWindow();
3683     window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
3684     window->ClipRect = window->DrawList->_ClipRectStack.back();
3685 }
3686 
PopClipRect()3687 void ImGui::PopClipRect()
3688 {
3689     ImGuiWindow* window = GetCurrentWindow();
3690     window->DrawList->PopClipRect();
3691     window->ClipRect = window->DrawList->_ClipRectStack.back();
3692 }
3693 
3694 // 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()3695 void ImGui::EndFrame()
3696 {
3697     ImGuiContext& g = *GImGui;
3698     IM_ASSERT(g.Initialized);
3699     if (g.FrameCountEnded == g.FrameCount)          // Don't process EndFrame() multiple times.
3700         return;
3701     IM_ASSERT(g.FrameScopeActive && "Forgot to call ImGui::NewFrame()?");
3702 
3703     // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
3704     if (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f)
3705     {
3706         g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y);
3707         g.PlatformImeLastPos = g.PlatformImePos;
3708     }
3709 
3710     // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you
3711     // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API).
3712     if (g.CurrentWindowStack.Size != 1)
3713     {
3714         if (g.CurrentWindowStack.Size > 1)
3715         {
3716             IM_ASSERT(g.CurrentWindowStack.Size == 1 && "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?");
3717             while (g.CurrentWindowStack.Size > 1) // FIXME-ERRORHANDLING
3718                 End();
3719         }
3720         else
3721         {
3722             IM_ASSERT(g.CurrentWindowStack.Size == 1 && "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?");
3723         }
3724     }
3725 
3726     // Hide implicit/fallback "Debug" window if it hasn't been used
3727     g.FrameScopePushedImplicitWindow = false;
3728     if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
3729         g.CurrentWindow->Active = false;
3730     End();
3731 
3732     // Show CTRL+TAB list window
3733     if (g.NavWindowingTarget)
3734         NavUpdateWindowingList();
3735 
3736     // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
3737     if (g.DragDropActive)
3738     {
3739         bool is_delivered = g.DragDropPayload.Delivery;
3740         bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton));
3741         if (is_delivered || is_elapsed)
3742             ClearDragDrop();
3743     }
3744 
3745     // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.
3746     if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount)
3747     {
3748         g.DragDropWithinSourceOrTarget = true;
3749         SetTooltip("...");
3750         g.DragDropWithinSourceOrTarget = false;
3751     }
3752 
3753     // End frame
3754     g.FrameScopeActive = false;
3755     g.FrameCountEnded = g.FrameCount;
3756 
3757     // Initiate moving window + handle left-click and right-click focus
3758     UpdateMouseMovingWindowEndFrame();
3759 
3760     // Sort the window list so that all child windows are after their parent
3761     // We cannot do that on FocusWindow() because childs may not exist yet
3762     g.WindowsSortBuffer.resize(0);
3763     g.WindowsSortBuffer.reserve(g.Windows.Size);
3764     for (int i = 0; i != g.Windows.Size; i++)
3765     {
3766         ImGuiWindow* window = g.Windows[i];
3767         if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow))       // if a child is active its parent will add it
3768             continue;
3769         AddWindowToSortBuffer(&g.WindowsSortBuffer, window);
3770     }
3771 
3772     IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size);  // we done something wrong
3773     g.Windows.swap(g.WindowsSortBuffer);
3774     g.IO.MetricsActiveWindows = g.WindowsActiveCount;
3775 
3776     // Unlock font atlas
3777     g.IO.Fonts->Locked = false;
3778 
3779     // Clear Input data for next frame
3780     g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
3781     g.IO.InputQueueCharacters.resize(0);
3782     memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
3783 }
3784 
Render()3785 void ImGui::Render()
3786 {
3787     ImGuiContext& g = *GImGui;
3788     IM_ASSERT(g.Initialized);
3789 
3790     if (g.FrameCountEnded != g.FrameCount)
3791         EndFrame();
3792     g.FrameCountRendered = g.FrameCount;
3793 
3794     // Gather ImDrawList to render (for each active window)
3795     g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsRenderWindows = 0;
3796     g.DrawDataBuilder.Clear();
3797     ImGuiWindow* windows_to_render_front_most[2];
3798     windows_to_render_front_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
3799     windows_to_render_front_most[1] = g.NavWindowingTarget ? g.NavWindowingList : NULL;
3800     for (int n = 0; n != g.Windows.Size; n++)
3801     {
3802         ImGuiWindow* window = g.Windows[n];
3803         if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_front_most[0] && window != windows_to_render_front_most[1])
3804             AddWindowToDrawDataSelectLayer(window);
3805     }
3806     for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_front_most); n++)
3807         if (windows_to_render_front_most[n] && IsWindowActiveAndVisible(windows_to_render_front_most[n])) // NavWindowingTarget is always temporarily displayed as the front-most window
3808             AddWindowToDrawDataSelectLayer(windows_to_render_front_most[n]);
3809     g.DrawDataBuilder.FlattenIntoSingleLayer();
3810 
3811     // Draw software mouse cursor if requested
3812     if (g.IO.MouseDrawCursor)
3813         RenderMouseCursor(&g.OverlayDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor);
3814 
3815     if (!g.OverlayDrawList.VtxBuffer.empty())
3816         AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.OverlayDrawList);
3817 
3818     // Setup ImDrawData structure for end-user
3819     SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData);
3820     g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount;
3821     g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount;
3822 
3823     // (Legacy) Call the Render callback function. The current prefer way is to let the user retrieve GetDrawData() and call the render function themselves.
3824 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
3825     if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)
3826         g.IO.RenderDrawListsFn(&g.DrawData);
3827 #endif
3828 }
3829 
3830 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
3831 // CalcTextSize("") should return ImVec2(0.0f, GImGui->FontSize)
CalcTextSize(const char * text,const char * text_end,bool hide_text_after_double_hash,float wrap_width)3832 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
3833 {
3834     ImGuiContext& g = *GImGui;
3835 
3836     const char* text_display_end;
3837     if (hide_text_after_double_hash)
3838         text_display_end = FindRenderedTextEnd(text, text_end);      // Hide anything after a '##' string
3839     else
3840         text_display_end = text_end;
3841 
3842     ImFont* font = g.Font;
3843     const float font_size = g.FontSize;
3844     if (text == text_display_end)
3845         return ImVec2(0.0f, font_size);
3846     ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
3847 
3848     // Cancel out character spacing for the last character of a line (it is baked into glyph->AdvanceX field)
3849     const float font_scale = font_size / font->FontSize;
3850     const float character_spacing_x = 1.0f * font_scale;
3851     if (text_size.x > 0.0f)
3852         text_size.x -= character_spacing_x;
3853     text_size.x = (float)(int)(text_size.x + 0.95f);
3854 
3855     return text_size;
3856 }
3857 
3858 // Helper to calculate coarse clipping of large list of evenly sized items.
3859 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
3860 // 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)3861 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
3862 {
3863     ImGuiContext& g = *GImGui;
3864     ImGuiWindow* window = g.CurrentWindow;
3865     if (g.LogEnabled)
3866     {
3867         // If logging is active, do not perform any clipping
3868         *out_items_display_start = 0;
3869         *out_items_display_end = items_count;
3870         return;
3871     }
3872     if (window->SkipItems)
3873     {
3874         *out_items_display_start = *out_items_display_end = 0;
3875         return;
3876     }
3877 
3878     // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect
3879     ImRect unclipped_rect = window->ClipRect;
3880     if (g.NavMoveRequest)
3881         unclipped_rect.Add(g.NavScoringRectScreen);
3882 
3883     const ImVec2 pos = window->DC.CursorPos;
3884     int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
3885     int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
3886 
3887     // When performing a navigation request, ensure we have one item extra in the direction we are moving to
3888     if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up)
3889         start--;
3890     if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down)
3891         end++;
3892 
3893     start = ImClamp(start, 0, items_count);
3894     end = ImClamp(end + 1, start, items_count);
3895     *out_items_display_start = start;
3896     *out_items_display_end = end;
3897 }
3898 
3899 // Find window given position, search front-to-back
3900 // FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programatically
3901 // with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
3902 // called, aka before the next Begin(). Moving window isn't affected.
FindHoveredWindow()3903 static void FindHoveredWindow()
3904 {
3905     ImGuiContext& g = *GImGui;
3906 
3907     ImGuiWindow* hovered_window = NULL;
3908     if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
3909         hovered_window = g.MovingWindow;
3910 
3911     ImVec2 padding_regular = g.Style.TouchExtraPadding;
3912     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;
3913     for (int i = g.Windows.Size - 1; i >= 0; i--)
3914     {
3915         ImGuiWindow* window = g.Windows[i];
3916         if (!window->Active || window->Hidden)
3917             continue;
3918         if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
3919             continue;
3920 
3921         // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
3922         ImRect bb(window->OuterRectClipped);
3923         if ((window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_NoResize))
3924             bb.Expand(padding_regular);
3925         else
3926             bb.Expand(padding_for_resize_from_edges);
3927         if (!bb.Contains(g.IO.MousePos))
3928             continue;
3929 
3930         // Those seemingly unnecessary extra tests are because the code here is a little different in viewport/docking branches.
3931         if (hovered_window == NULL)
3932             hovered_window = window;
3933         if (hovered_window)
3934             break;
3935     }
3936 
3937     g.HoveredWindow = hovered_window;
3938     g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
3939 
3940 }
3941 
3942 // Test if mouse cursor is hovering given rectangle
3943 // NB- Rectangle is clipped by our current clip setting
3944 // 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)3945 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
3946 {
3947     ImGuiContext& g = *GImGui;
3948 
3949     // Clip
3950     ImRect rect_clipped(r_min, r_max);
3951     if (clip)
3952         rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
3953 
3954     // Expand for touch input
3955     const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
3956     if (!rect_for_touch.Contains(g.IO.MousePos))
3957         return false;
3958     return true;
3959 }
3960 
GetKeyIndex(ImGuiKey imgui_key)3961 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
3962 {
3963     IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
3964     return GImGui->IO.KeyMap[imgui_key];
3965 }
3966 
3967 // Note that imgui doesn't know the semantic of each entry of io.KeysDown[]. Use your own indices/enums according to how your back-end/engine stored them into io.KeysDown[]!
IsKeyDown(int user_key_index)3968 bool ImGui::IsKeyDown(int user_key_index)
3969 {
3970     if (user_key_index < 0) return false;
3971     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(GImGui->IO.KeysDown));
3972     return GImGui->IO.KeysDown[user_key_index];
3973 }
3974 
CalcTypematicPressedRepeatAmount(float t,float t_prev,float repeat_delay,float repeat_rate)3975 int ImGui::CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate)
3976 {
3977     if (t == 0.0f)
3978         return 1;
3979     if (t <= repeat_delay || repeat_rate <= 0.0f)
3980         return 0;
3981     const int count = (int)((t - repeat_delay) / repeat_rate) - (int)((t_prev - repeat_delay) / repeat_rate);
3982     return (count > 0) ? count : 0;
3983 }
3984 
GetKeyPressedAmount(int key_index,float repeat_delay,float repeat_rate)3985 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
3986 {
3987     ImGuiContext& g = *GImGui;
3988     if (key_index < 0) return false;
3989     IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3990     const float t = g.IO.KeysDownDuration[key_index];
3991     return CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, repeat_delay, repeat_rate);
3992 }
3993 
IsKeyPressed(int user_key_index,bool repeat)3994 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
3995 {
3996     ImGuiContext& g = *GImGui;
3997     if (user_key_index < 0) return false;
3998     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3999     const float t = g.IO.KeysDownDuration[user_key_index];
4000     if (t == 0.0f)
4001         return true;
4002     if (repeat && t > g.IO.KeyRepeatDelay)
4003         return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
4004     return false;
4005 }
4006 
IsKeyReleased(int user_key_index)4007 bool ImGui::IsKeyReleased(int user_key_index)
4008 {
4009     ImGuiContext& g = *GImGui;
4010     if (user_key_index < 0) return false;
4011     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4012     return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
4013 }
4014 
IsMouseDown(int button)4015 bool ImGui::IsMouseDown(int button)
4016 {
4017     ImGuiContext& g = *GImGui;
4018     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4019     return g.IO.MouseDown[button];
4020 }
4021 
IsAnyMouseDown()4022 bool ImGui::IsAnyMouseDown()
4023 {
4024     ImGuiContext& g = *GImGui;
4025     for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
4026         if (g.IO.MouseDown[n])
4027             return true;
4028     return false;
4029 }
4030 
IsMouseClicked(int button,bool repeat)4031 bool ImGui::IsMouseClicked(int button, bool repeat)
4032 {
4033     ImGuiContext& g = *GImGui;
4034     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4035     const float t = g.IO.MouseDownDuration[button];
4036     if (t == 0.0f)
4037         return true;
4038 
4039     if (repeat && t > g.IO.KeyRepeatDelay)
4040     {
4041         float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate;
4042         if ((ImFmod(t - delay, rate) > rate*0.5f) != (ImFmod(t - delay - g.IO.DeltaTime, rate) > rate*0.5f))
4043             return true;
4044     }
4045 
4046     return false;
4047 }
4048 
IsMouseReleased(int button)4049 bool ImGui::IsMouseReleased(int button)
4050 {
4051     ImGuiContext& g = *GImGui;
4052     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4053     return g.IO.MouseReleased[button];
4054 }
4055 
IsMouseDoubleClicked(int button)4056 bool ImGui::IsMouseDoubleClicked(int button)
4057 {
4058     ImGuiContext& g = *GImGui;
4059     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4060     return g.IO.MouseDoubleClicked[button];
4061 }
4062 
IsMouseDragging(int button,float lock_threshold)4063 bool ImGui::IsMouseDragging(int button, float lock_threshold)
4064 {
4065     ImGuiContext& g = *GImGui;
4066     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4067     if (!g.IO.MouseDown[button])
4068         return false;
4069     if (lock_threshold < 0.0f)
4070         lock_threshold = g.IO.MouseDragThreshold;
4071     return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
4072 }
4073 
GetMousePos()4074 ImVec2 ImGui::GetMousePos()
4075 {
4076     return GImGui->IO.MousePos;
4077 }
4078 
4079 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()4080 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
4081 {
4082     ImGuiContext& g = *GImGui;
4083     if (g.BeginPopupStack.Size > 0)
4084         return g.OpenPopupStack[g.BeginPopupStack.Size-1].OpenMousePos;
4085     return g.IO.MousePos;
4086 }
4087 
4088 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position
IsMousePosValid(const ImVec2 * mouse_pos)4089 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
4090 {
4091     if (mouse_pos == NULL)
4092         mouse_pos = &GImGui->IO.MousePos;
4093     const float MOUSE_INVALID = -256000.0f;
4094     return mouse_pos->x >= MOUSE_INVALID && mouse_pos->y >= MOUSE_INVALID;
4095 }
4096 
4097 // Return the delta from the initial clicking position.
4098 // This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
4099 // NB: This is only valid if IsMousePosValid(). Back-ends in theory should always keep mouse position valid when dragging even outside the client window.
GetMouseDragDelta(int button,float lock_threshold)4100 ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold)
4101 {
4102     ImGuiContext& g = *GImGui;
4103     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4104     if (lock_threshold < 0.0f)
4105         lock_threshold = g.IO.MouseDragThreshold;
4106     if (g.IO.MouseDown[button])
4107         if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
4108             return g.IO.MousePos - g.IO.MouseClickedPos[button];     // Assume we can only get active with left-mouse button (at the moment).
4109     return ImVec2(0.0f, 0.0f);
4110 }
4111 
ResetMouseDragDelta(int button)4112 void ImGui::ResetMouseDragDelta(int button)
4113 {
4114     ImGuiContext& g = *GImGui;
4115     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4116     // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
4117     g.IO.MouseClickedPos[button] = g.IO.MousePos;
4118 }
4119 
GetMouseCursor()4120 ImGuiMouseCursor ImGui::GetMouseCursor()
4121 {
4122     return GImGui->MouseCursor;
4123 }
4124 
SetMouseCursor(ImGuiMouseCursor cursor_type)4125 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
4126 {
4127     GImGui->MouseCursor = cursor_type;
4128 }
4129 
CaptureKeyboardFromApp(bool capture)4130 void ImGui::CaptureKeyboardFromApp(bool capture)
4131 {
4132     GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
4133 }
4134 
CaptureMouseFromApp(bool capture)4135 void ImGui::CaptureMouseFromApp(bool capture)
4136 {
4137     GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
4138 }
4139 
IsItemActive()4140 bool ImGui::IsItemActive()
4141 {
4142     ImGuiContext& g = *GImGui;
4143     if (g.ActiveId)
4144     {
4145         ImGuiWindow* window = g.CurrentWindow;
4146         return g.ActiveId == window->DC.LastItemId;
4147     }
4148     return false;
4149 }
4150 
IsItemDeactivated()4151 bool ImGui::IsItemDeactivated()
4152 {
4153     ImGuiContext& g = *GImGui;
4154     ImGuiWindow* window = g.CurrentWindow;
4155     return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId);
4156 }
4157 
IsItemDeactivatedAfterEdit()4158 bool ImGui::IsItemDeactivatedAfterEdit()
4159 {
4160     ImGuiContext& g = *GImGui;
4161     return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEdited || (g.ActiveId == 0 && g.ActiveIdHasBeenEdited));
4162 }
4163 
IsItemFocused()4164 bool ImGui::IsItemFocused()
4165 {
4166     ImGuiContext& g = *GImGui;
4167     ImGuiWindow* window = g.CurrentWindow;
4168     return g.NavId && !g.NavDisableHighlight && g.NavId == window->DC.LastItemId;
4169 }
4170 
IsItemClicked(int mouse_button)4171 bool ImGui::IsItemClicked(int mouse_button)
4172 {
4173     return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
4174 }
4175 
IsAnyItemHovered()4176 bool ImGui::IsAnyItemHovered()
4177 {
4178     ImGuiContext& g = *GImGui;
4179     return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
4180 }
4181 
IsAnyItemActive()4182 bool ImGui::IsAnyItemActive()
4183 {
4184     ImGuiContext& g = *GImGui;
4185     return g.ActiveId != 0;
4186 }
4187 
IsAnyItemFocused()4188 bool ImGui::IsAnyItemFocused()
4189 {
4190     ImGuiContext& g = *GImGui;
4191     return g.NavId != 0 && !g.NavDisableHighlight;
4192 }
4193 
IsItemVisible()4194 bool ImGui::IsItemVisible()
4195 {
4196     ImGuiWindow* window = GetCurrentWindowRead();
4197     return window->ClipRect.Overlaps(window->DC.LastItemRect);
4198 }
4199 
IsItemEdited()4200 bool ImGui::IsItemEdited()
4201 {
4202     ImGuiWindow* window = GetCurrentWindowRead();
4203     return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0;
4204 }
4205 
4206 // 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()4207 void ImGui::SetItemAllowOverlap()
4208 {
4209     ImGuiContext& g = *GImGui;
4210     if (g.HoveredId == g.CurrentWindow->DC.LastItemId)
4211         g.HoveredIdAllowOverlap = true;
4212     if (g.ActiveId == g.CurrentWindow->DC.LastItemId)
4213         g.ActiveIdAllowOverlap = true;
4214 }
4215 
GetItemRectMin()4216 ImVec2 ImGui::GetItemRectMin()
4217 {
4218     ImGuiWindow* window = GetCurrentWindowRead();
4219     return window->DC.LastItemRect.Min;
4220 }
4221 
GetItemRectMax()4222 ImVec2 ImGui::GetItemRectMax()
4223 {
4224     ImGuiWindow* window = GetCurrentWindowRead();
4225     return window->DC.LastItemRect.Max;
4226 }
4227 
GetItemRectSize()4228 ImVec2 ImGui::GetItemRectSize()
4229 {
4230     ImGuiWindow* window = GetCurrentWindowRead();
4231     return window->DC.LastItemRect.GetSize();
4232 }
4233 
GetViewportRect()4234 static ImRect GetViewportRect()
4235 {
4236     ImGuiContext& g = *GImGui;
4237     if (g.IO.DisplayVisibleMin.x != g.IO.DisplayVisibleMax.x && g.IO.DisplayVisibleMin.y != g.IO.DisplayVisibleMax.y)
4238         return ImRect(g.IO.DisplayVisibleMin, g.IO.DisplayVisibleMax);
4239     return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
4240 }
4241 
BeginChildEx(const char * name,ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags flags)4242 static bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags)
4243 {
4244     ImGuiContext& g = *GImGui;
4245     ImGuiWindow* parent_window = g.CurrentWindow;
4246 
4247     flags |= ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
4248     flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove);  // Inherit the NoMove flag
4249 
4250     // Size
4251     const ImVec2 content_avail = GetContentRegionAvail();
4252     ImVec2 size = ImFloor(size_arg);
4253     const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
4254     if (size.x <= 0.0f)
4255         size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
4256     if (size.y <= 0.0f)
4257         size.y = ImMax(content_avail.y + size.y, 4.0f);
4258     SetNextWindowSize(size);
4259 
4260     // 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.
4261     char title[256];
4262     if (name)
4263         ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s_%08X", parent_window->Name, name, id);
4264     else
4265         ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id);
4266 
4267     const float backup_border_size = g.Style.ChildBorderSize;
4268     if (!border)
4269         g.Style.ChildBorderSize = 0.0f;
4270     bool ret = Begin(title, NULL, flags);
4271     g.Style.ChildBorderSize = backup_border_size;
4272 
4273     ImGuiWindow* child_window = g.CurrentWindow;
4274     child_window->ChildId = id;
4275     child_window->AutoFitChildAxises = auto_fit_axises;
4276 
4277     // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
4278     // While this is not really documented/defined, it seems that the expected thing to do.
4279     if (child_window->BeginCount == 1)
4280         parent_window->DC.CursorPos = child_window->Pos;
4281 
4282     // Process navigation-in immediately so NavInit can run on first frame
4283     if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll))
4284     {
4285         FocusWindow(child_window);
4286         NavInitWindow(child_window, false);
4287         SetActiveID(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item
4288         g.ActiveIdSource = ImGuiInputSource_Nav;
4289     }
4290     return ret;
4291 }
4292 
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4293 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4294 {
4295     ImGuiWindow* window = GetCurrentWindow();
4296     return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
4297 }
4298 
BeginChild(ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4299 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4300 {
4301     IM_ASSERT(id != 0);
4302     return BeginChildEx(NULL, id, size_arg, border, extra_flags);
4303 }
4304 
EndChild()4305 void ImGui::EndChild()
4306 {
4307     ImGuiContext& g = *GImGui;
4308     ImGuiWindow* window = g.CurrentWindow;
4309 
4310     IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow);   // Mismatched BeginChild()/EndChild() callss
4311     if (window->BeginCount > 1)
4312     {
4313         End();
4314     }
4315     else
4316     {
4317         ImVec2 sz = window->Size;
4318         if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
4319             sz.x = ImMax(4.0f, sz.x);
4320         if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
4321             sz.y = ImMax(4.0f, sz.y);
4322         End();
4323 
4324         ImGuiWindow* parent_window = g.CurrentWindow;
4325         ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
4326         ItemSize(sz);
4327         if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
4328         {
4329             ItemAdd(bb, window->ChildId);
4330             RenderNavHighlight(bb, window->ChildId);
4331 
4332             // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
4333             if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow)
4334                 RenderNavHighlight(ImRect(bb.Min - ImVec2(2,2), bb.Max + ImVec2(2,2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
4335         }
4336         else
4337         {
4338             // Not navigable into
4339             ItemAdd(bb, 0);
4340         }
4341     }
4342 }
4343 
4344 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)4345 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
4346 {
4347     ImGuiContext& g = *GImGui;
4348     const ImGuiStyle& style = g.Style;
4349     PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
4350     PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
4351     PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
4352     PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
4353     bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
4354     PopStyleVar(3);
4355     PopStyleColor();
4356     return ret;
4357 }
4358 
EndChildFrame()4359 void ImGui::EndChildFrame()
4360 {
4361     EndChild();
4362 }
4363 
4364 // Save and compare stack sizes on Begin()/End() to detect usage errors
CheckStacksSize(ImGuiWindow * window,bool write)4365 static void CheckStacksSize(ImGuiWindow* window, bool write)
4366 {
4367     // 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)
4368     ImGuiContext& g = *GImGui;
4369     short* p_backup = &window->DC.StackSizesBackup[0];
4370     { int current = window->IDStack.Size;       if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup == current && "PushID/PopID or TreeNode/TreePop Mismatch!");   p_backup++; }    // Too few or too many PopID()/TreePop()
4371     { int current = window->DC.GroupStack.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup == current && "BeginGroup/EndGroup Mismatch!");                p_backup++; }    // Too few or too many EndGroup()
4372     { int current = g.BeginPopupStack.Size;     if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup == current && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch"); p_backup++;}// Too few or too many EndMenu()/EndPopup()
4373     // 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.
4374     { int current = g.ColorModifiers.Size;      if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup >= current && "PushStyleColor/PopStyleColor Mismatch!");       p_backup++; }    // Too few or too many PopStyleColor()
4375     { int current = g.StyleModifiers.Size;      if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup >= current && "PushStyleVar/PopStyleVar Mismatch!");           p_backup++; }    // Too few or too many PopStyleVar()
4376     { int current = g.FontStack.Size;           if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup >= current && "PushFont/PopFont Mismatch!");                   p_backup++; }    // Too few or too many PopFont()
4377     IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
4378 }
4379 
SetWindowConditionAllowFlags(ImGuiWindow * window,ImGuiCond flags,bool enabled)4380 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
4381 {
4382     window->SetWindowPosAllowFlags       = enabled ? (window->SetWindowPosAllowFlags       | flags) : (window->SetWindowPosAllowFlags       & ~flags);
4383     window->SetWindowSizeAllowFlags      = enabled ? (window->SetWindowSizeAllowFlags      | flags) : (window->SetWindowSizeAllowFlags      & ~flags);
4384     window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
4385 }
4386 
FindWindowByID(ImGuiID id)4387 ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
4388 {
4389     ImGuiContext& g = *GImGui;
4390     return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
4391 }
4392 
FindWindowByName(const char * name)4393 ImGuiWindow* ImGui::FindWindowByName(const char* name)
4394 {
4395     ImGuiID id = ImHash(name, 0);
4396     return FindWindowByID(id);
4397 }
4398 
CreateNewWindow(const char * name,ImVec2 size,ImGuiWindowFlags flags)4399 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
4400 {
4401     ImGuiContext& g = *GImGui;
4402 
4403     // Create window the first time
4404     ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
4405     window->Flags = flags;
4406     g.WindowsById.SetVoidPtr(window->ID, window);
4407 
4408     // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
4409     window->Pos = ImVec2(60, 60);
4410 
4411     // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
4412     if (!(flags & ImGuiWindowFlags_NoSavedSettings))
4413         if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
4414         {
4415             // Retrieve settings from .ini file
4416             window->SettingsIdx = g.SettingsWindows.index_from_ptr(settings);
4417             SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
4418             window->Pos = ImFloor(settings->Pos);
4419             window->Collapsed = settings->Collapsed;
4420             if (ImLengthSqr(settings->Size) > 0.00001f)
4421                 size = ImFloor(settings->Size);
4422         }
4423     window->Size = window->SizeFull = window->SizeFullAtLastBegin = ImFloor(size);
4424     window->DC.CursorMaxPos = window->Pos; // So first call to CalcSizeContents() doesn't return crazy values
4425 
4426     if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
4427     {
4428         window->AutoFitFramesX = window->AutoFitFramesY = 2;
4429         window->AutoFitOnlyGrows = false;
4430     }
4431     else
4432     {
4433         if (window->Size.x <= 0.0f)
4434             window->AutoFitFramesX = 2;
4435         if (window->Size.y <= 0.0f)
4436             window->AutoFitFramesY = 2;
4437         window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
4438     }
4439 
4440     g.WindowsFocusOrder.push_back(window);
4441     if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
4442         g.Windows.push_front(window); // Quite slow but rare and only once
4443     else
4444         g.Windows.push_back(window);
4445     return window;
4446 }
4447 
CalcSizeAfterConstraint(ImGuiWindow * window,ImVec2 new_size)4448 static ImVec2 CalcSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size)
4449 {
4450     ImGuiContext& g = *GImGui;
4451     if (g.NextWindowData.SizeConstraintCond != 0)
4452     {
4453         // Using -1,-1 on either X/Y axis to preserve the current size.
4454         ImRect cr = g.NextWindowData.SizeConstraintRect;
4455         new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
4456         new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
4457         if (g.NextWindowData.SizeCallback)
4458         {
4459             ImGuiSizeCallbackData data;
4460             data.UserData = g.NextWindowData.SizeCallbackUserData;
4461             data.Pos = window->Pos;
4462             data.CurrentSize = window->SizeFull;
4463             data.DesiredSize = new_size;
4464             g.NextWindowData.SizeCallback(&data);
4465             new_size = data.DesiredSize;
4466         }
4467     }
4468 
4469     // Minimum size
4470     if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
4471     {
4472         new_size = ImMax(new_size, g.Style.WindowMinSize);
4473         new_size.y = ImMax(new_size.y, window->TitleBarHeight() + window->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows
4474     }
4475     return new_size;
4476 }
4477 
CalcSizeContents(ImGuiWindow * window)4478 static ImVec2 CalcSizeContents(ImGuiWindow* window)
4479 {
4480     if (window->Collapsed)
4481         return window->SizeContents;
4482     if (window->Hidden && window->HiddenFramesForResize == 0 && window->HiddenFramesRegular > 0)
4483         return window->SizeContents;
4484 
4485     ImVec2 sz;
4486     sz.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : (window->DC.CursorMaxPos.x - window->Pos.x + window->Scroll.x));
4487     sz.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : (window->DC.CursorMaxPos.y - window->Pos.y + window->Scroll.y));
4488     return sz + window->WindowPadding;
4489 }
4490 
CalcSizeAutoFit(ImGuiWindow * window,const ImVec2 & size_contents)4491 static ImVec2 CalcSizeAutoFit(ImGuiWindow* window, const ImVec2& size_contents)
4492 {
4493     ImGuiContext& g = *GImGui;
4494     ImGuiStyle& style = g.Style;
4495     if (window->Flags & ImGuiWindowFlags_Tooltip)
4496     {
4497         // Tooltip always resize
4498         return size_contents;
4499     }
4500     else
4501     {
4502         // When the window cannot fit all contents (either because of constraints, either because screen is too small): we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding.
4503         const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0;
4504         const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0;
4505         ImVec2 size_min = style.WindowMinSize;
4506         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)
4507             size_min = ImMin(size_min, ImVec2(4.0f, 4.0f));
4508         ImVec2 size_auto_fit = ImClamp(size_contents, size_min, ImMax(size_min, g.IO.DisplaySize - style.DisplaySafeAreaPadding * 2.0f));
4509         ImVec2 size_auto_fit_after_constraint = CalcSizeAfterConstraint(window, size_auto_fit);
4510         if (size_auto_fit_after_constraint.x < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar))
4511             size_auto_fit.y += style.ScrollbarSize;
4512         if (size_auto_fit_after_constraint.y < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar))
4513             size_auto_fit.x += style.ScrollbarSize;
4514         return size_auto_fit;
4515     }
4516 }
4517 
CalcWindowExpectedSize(ImGuiWindow * window)4518 ImVec2 ImGui::CalcWindowExpectedSize(ImGuiWindow* window)
4519 {
4520     ImVec2 size_contents = CalcSizeContents(window);
4521     return CalcSizeAfterConstraint(window, CalcSizeAutoFit(window, size_contents));
4522 }
4523 
GetWindowScrollMaxX(ImGuiWindow * window)4524 float ImGui::GetWindowScrollMaxX(ImGuiWindow* window)
4525 {
4526     return ImMax(0.0f, window->SizeContents.x - (window->SizeFull.x - window->ScrollbarSizes.x));
4527 }
4528 
GetWindowScrollMaxY(ImGuiWindow * window)4529 float ImGui::GetWindowScrollMaxY(ImGuiWindow* window)
4530 {
4531     return ImMax(0.0f, window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y));
4532 }
4533 
CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow * window,bool snap_on_edges)4534 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges)
4535 {
4536     ImGuiContext& g = *GImGui;
4537     ImVec2 scroll = window->Scroll;
4538     if (window->ScrollTarget.x < FLT_MAX)
4539     {
4540         float cr_x = window->ScrollTargetCenterRatio.x;
4541         scroll.x = window->ScrollTarget.x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x);
4542     }
4543     if (window->ScrollTarget.y < FLT_MAX)
4544     {
4545         // 'snap_on_edges' allows for a discontinuity at the edge of scrolling limits to take account of WindowPadding so that scrolling to make the last item visible scroll far enough to see the padding.
4546         float cr_y = window->ScrollTargetCenterRatio.y;
4547         float target_y = window->ScrollTarget.y;
4548         if (snap_on_edges && cr_y <= 0.0f && target_y <= window->WindowPadding.y)
4549             target_y = 0.0f;
4550         if (snap_on_edges && cr_y >= 1.0f && target_y >= window->SizeContents.y - window->WindowPadding.y + g.Style.ItemSpacing.y)
4551             target_y = window->SizeContents.y;
4552         scroll.y = target_y - (1.0f - cr_y) * (window->TitleBarHeight() + window->MenuBarHeight()) - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y);
4553     }
4554     scroll = ImMax(scroll, ImVec2(0.0f, 0.0f));
4555     if (!window->Collapsed && !window->SkipItems)
4556     {
4557         scroll.x = ImMin(scroll.x, ImGui::GetWindowScrollMaxX(window));
4558         scroll.y = ImMin(scroll.y, ImGui::GetWindowScrollMaxY(window));
4559     }
4560     return scroll;
4561 }
4562 
GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)4563 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
4564 {
4565     if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
4566         return ImGuiCol_PopupBg;
4567     if (flags & ImGuiWindowFlags_ChildWindow)
4568         return ImGuiCol_ChildBg;
4569     return ImGuiCol_WindowBg;
4570 }
4571 
CalcResizePosSizeFromAnyCorner(ImGuiWindow * window,const ImVec2 & corner_target,const ImVec2 & corner_norm,ImVec2 * out_pos,ImVec2 * out_size)4572 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
4573 {
4574     ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm);                // Expected window upper-left
4575     ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
4576     ImVec2 size_expected = pos_max - pos_min;
4577     ImVec2 size_constrained = CalcSizeAfterConstraint(window, size_expected);
4578     *out_pos = pos_min;
4579     if (corner_norm.x == 0.0f)
4580         out_pos->x -= (size_constrained.x - size_expected.x);
4581     if (corner_norm.y == 0.0f)
4582         out_pos->y -= (size_constrained.y - size_expected.y);
4583     *out_size = size_constrained;
4584 }
4585 
4586 struct ImGuiResizeGripDef
4587 {
4588     ImVec2  CornerPos;
4589     ImVec2  InnerDir;
4590     int     AngleMin12, AngleMax12;
4591 };
4592 
4593 const ImGuiResizeGripDef resize_grip_def[4] =
4594 {
4595     { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower right
4596     { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower left
4597     { ImVec2(0,0), ImVec2(+1,+1), 6, 9 }, // Upper left
4598     { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper right
4599 };
4600 
GetResizeBorderRect(ImGuiWindow * window,int border_n,float perp_padding,float thickness)4601 static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
4602 {
4603     ImRect rect = window->Rect();
4604     if (thickness == 0.0f) rect.Max -= ImVec2(1,1);
4605     if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness,    rect.Max.x - perp_padding, rect.Min.y + thickness);
4606     if (border_n == 1) return ImRect(rect.Max.x - thickness,    rect.Min.y + perp_padding, rect.Max.x + thickness,    rect.Max.y - perp_padding);
4607     if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness,    rect.Max.x - perp_padding, rect.Max.y + thickness);
4608     if (border_n == 3) return ImRect(rect.Min.x - thickness,    rect.Min.y + perp_padding, rect.Min.x + thickness,    rect.Max.y - perp_padding);
4609     IM_ASSERT(0);
4610     return ImRect();
4611 }
4612 
4613 // Handle resize for: Resize Grips, Borders, Gamepad
UpdateManualResize(ImGuiWindow * window,const ImVec2 & size_auto_fit,int * border_held,int resize_grip_count,ImU32 resize_grip_col[4])4614 static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4])
4615 {
4616     ImGuiContext& g = *GImGui;
4617     ImGuiWindowFlags flags = window->Flags;
4618     if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
4619         return;
4620     if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window.
4621         return;
4622 
4623     const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0;
4624     const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f);
4625     const float grip_hover_inner_size = (float)(int)(grip_draw_size * 0.75f);
4626     const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS : 0.0f;
4627 
4628     ImVec2 pos_target(FLT_MAX, FLT_MAX);
4629     ImVec2 size_target(FLT_MAX, FLT_MAX);
4630 
4631     // Manual resize grips
4632     PushID("#RESIZE");
4633     for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
4634     {
4635         const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
4636         const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos);
4637 
4638         // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
4639         ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size);
4640         if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
4641         if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
4642         bool hovered, held;
4643         ButtonBehavior(resize_rect, window->GetID((void*)(intptr_t)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
4644         //GetOverlayDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
4645         if (hovered || held)
4646             g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
4647 
4648         if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
4649         {
4650             // Manual auto-fit when double-clicking
4651             size_target = CalcSizeAfterConstraint(window, size_auto_fit);
4652             ClearActiveID();
4653         }
4654         else if (held)
4655         {
4656             // Resize from any of the four corners
4657             // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
4658             ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(grip.InnerDir * grip_hover_outer_size, grip.InnerDir * -grip_hover_inner_size, grip.CornerPos); // Corner of the window corresponding to our corner grip
4659             CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPos, &pos_target, &size_target);
4660         }
4661         if (resize_grip_n == 0 || held || hovered)
4662             resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
4663     }
4664     for (int border_n = 0; border_n < resize_border_count; border_n++)
4665     {
4666         bool hovered, held;
4667         ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS);
4668         ButtonBehavior(border_rect, window->GetID((void*)(intptr_t)(border_n + 4)), &hovered, &held, ImGuiButtonFlags_FlattenChildren);
4669         //GetOverlayDrawList(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
4670         if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held)
4671         {
4672             g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
4673             if (held) *border_held = border_n;
4674         }
4675         if (held)
4676         {
4677             ImVec2 border_target = window->Pos;
4678             ImVec2 border_posn;
4679             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); }
4680             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); }
4681             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); }
4682             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); }
4683             CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target);
4684         }
4685     }
4686     PopID();
4687 
4688     // Navigation resize (keyboard/gamepad)
4689     if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
4690     {
4691         ImVec2 nav_resize_delta;
4692         if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift)
4693             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
4694         if (g.NavInputSource == ImGuiInputSource_NavGamepad)
4695             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down);
4696         if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f)
4697         {
4698             const float NAV_RESIZE_SPEED = 600.0f;
4699             nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
4700             g.NavWindowingToggleLayer = false;
4701             g.NavDisableMouseHover = true;
4702             resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
4703             // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
4704             size_target = CalcSizeAfterConstraint(window, window->SizeFull + nav_resize_delta);
4705         }
4706     }
4707 
4708     // Apply back modified position/size to window
4709     if (size_target.x != FLT_MAX)
4710     {
4711         window->SizeFull = size_target;
4712         MarkIniSettingsDirty(window);
4713     }
4714     if (pos_target.x != FLT_MAX)
4715     {
4716         window->Pos = ImFloor(pos_target);
4717         MarkIniSettingsDirty(window);
4718     }
4719 
4720     window->Size = window->SizeFull;
4721 }
4722 
UpdateWindowParentAndRootLinks(ImGuiWindow * window,ImGuiWindowFlags flags,ImGuiWindow * parent_window)4723 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
4724 {
4725     window->ParentWindow = parent_window;
4726     window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
4727     if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
4728         window->RootWindow = parent_window->RootWindow;
4729     if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
4730         window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
4731     while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
4732         window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
4733 }
4734 
4735 // Push a new ImGui window to add widgets to.
4736 // - 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.
4737 // - Begin/End can be called multiple times during the frame with the same window name to append content.
4738 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
4739 //   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.
4740 // - 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.
4741 // - 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)4742 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
4743 {
4744     ImGuiContext& g = *GImGui;
4745     const ImGuiStyle& style = g.Style;
4746     IM_ASSERT(name != NULL && name[0] != '\0');     // Window name required
4747     IM_ASSERT(g.FrameScopeActive);                  // Forgot to call ImGui::NewFrame()
4748     IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
4749 
4750     // Find or create
4751     ImGuiWindow* window = FindWindowByName(name);
4752     const bool window_just_created = (window == NULL);
4753     if (window_just_created)
4754     {
4755         ImVec2 size_on_first_use = (g.NextWindowData.SizeCond != 0) ? g.NextWindowData.SizeVal : ImVec2(0.0f, 0.0f); // Any condition flag will do since we are creating a new window here.
4756         window = CreateNewWindow(name, size_on_first_use, flags);
4757     }
4758 
4759     // Automatically disable manual moving/resizing when NoInputs is set
4760     if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
4761         flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
4762 
4763     if (flags & ImGuiWindowFlags_NavFlattened)
4764         IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
4765 
4766     const int current_frame = g.FrameCount;
4767     const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
4768 
4769     // Update Flags, LastFrameActive, BeginOrderXXX fields
4770     if (first_begin_of_the_frame)
4771         window->Flags = (ImGuiWindowFlags)flags;
4772     else
4773         flags = window->Flags;
4774 
4775     // 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
4776     ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
4777     ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
4778     IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
4779     window->HasCloseButton = (p_open != NULL);
4780 
4781     // Update the Appearing flag
4782     bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1);   // Not using !WasActive because the implicit "Debug" window would always toggle off->on
4783     const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesForResize > 0);
4784     if (flags & ImGuiWindowFlags_Popup)
4785     {
4786         ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
4787         window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
4788         window_just_activated_by_user |= (window != popup_ref.Window);
4789     }
4790     window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
4791     if (window->Appearing)
4792         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
4793 
4794     // Add to stack
4795     g.CurrentWindowStack.push_back(window);
4796     SetCurrentWindow(window);
4797     CheckStacksSize(window, true);
4798     if (flags & ImGuiWindowFlags_Popup)
4799     {
4800         ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
4801         popup_ref.Window = window;
4802         g.BeginPopupStack.push_back(popup_ref);
4803         window->PopupId = popup_ref.PopupId;
4804     }
4805 
4806     if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow))
4807         window->NavLastIds[0] = 0;
4808 
4809     // Process SetNextWindow***() calls
4810     bool window_pos_set_by_api = false;
4811     bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
4812     if (g.NextWindowData.PosCond)
4813     {
4814         window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
4815         if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
4816         {
4817             // May be processed on the next frame if this is our first frame and we are measuring size
4818             // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
4819             window->SetWindowPosVal = g.NextWindowData.PosVal;
4820             window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
4821             window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
4822         }
4823         else
4824         {
4825             SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
4826         }
4827     }
4828     if (g.NextWindowData.SizeCond)
4829     {
4830         window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
4831         window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
4832         SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
4833     }
4834     if (g.NextWindowData.ContentSizeCond)
4835     {
4836         // Adjust passed "client size" to become a "window size"
4837         window->SizeContentsExplicit = g.NextWindowData.ContentSizeVal;
4838         if (window->SizeContentsExplicit.y != 0.0f)
4839             window->SizeContentsExplicit.y += window->TitleBarHeight() + window->MenuBarHeight();
4840     }
4841     else if (first_begin_of_the_frame)
4842     {
4843         window->SizeContentsExplicit = ImVec2(0.0f, 0.0f);
4844     }
4845     if (g.NextWindowData.CollapsedCond)
4846         SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
4847     if (g.NextWindowData.FocusCond)
4848         FocusWindow(window);
4849     if (window->Appearing)
4850         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
4851 
4852     // When reusing window again multiple times a frame, just append content (don't need to setup again)
4853     if (first_begin_of_the_frame)
4854     {
4855         // Initialize
4856         const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
4857         UpdateWindowParentAndRootLinks(window, flags, parent_window);
4858 
4859         window->Active = true;
4860         window->BeginOrderWithinParent = 0;
4861         window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
4862         window->BeginCount = 0;
4863         window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX);
4864         window->LastFrameActive = current_frame;
4865         window->IDStack.resize(1);
4866 
4867         // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
4868         // 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.
4869         bool window_title_visible_elsewhere = false;
4870         if (g.NavWindowingList != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0)   // Window titles visible when using CTRL+TAB
4871             window_title_visible_elsewhere = true;
4872         if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
4873         {
4874             size_t buf_len = (size_t)window->NameBufLen;
4875             window->Name = ImStrdupcpy(window->Name, &buf_len, name);
4876             window->NameBufLen = (int)buf_len;
4877         }
4878 
4879         // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
4880 
4881         // Update contents size from last frame for auto-fitting (or use explicit size)
4882         window->SizeContents = CalcSizeContents(window);
4883         if (window->HiddenFramesRegular > 0)
4884             window->HiddenFramesRegular--;
4885         if (window->HiddenFramesForResize > 0)
4886             window->HiddenFramesForResize--;
4887 
4888         // Hide new windows for one frame until they calculate their size
4889         if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
4890             window->HiddenFramesForResize = 1;
4891 
4892         // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
4893         // We reset Size/SizeContents for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
4894         if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
4895         {
4896             window->HiddenFramesForResize = 1;
4897             if (flags & ImGuiWindowFlags_AlwaysAutoResize)
4898             {
4899                 if (!window_size_x_set_by_api)
4900                     window->Size.x = window->SizeFull.x = 0.f;
4901                 if (!window_size_y_set_by_api)
4902                     window->Size.y = window->SizeFull.y = 0.f;
4903                 window->SizeContents = ImVec2(0.f, 0.f);
4904             }
4905         }
4906 
4907         SetCurrentWindow(window);
4908 
4909         // Lock border size and padding for the frame (so that altering them doesn't cause inconsistencies)
4910         window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
4911         window->WindowPadding = style.WindowPadding;
4912         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
4913             window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
4914         window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
4915         window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
4916 
4917         // Collapse window by double-clicking on title bar
4918         // 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
4919         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
4920         {
4921             // 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.
4922             ImRect title_bar_rect = window->TitleBarRect();
4923             if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
4924                 window->WantCollapseToggle = true;
4925             if (window->WantCollapseToggle)
4926             {
4927                 window->Collapsed = !window->Collapsed;
4928                 MarkIniSettingsDirty(window);
4929                 FocusWindow(window);
4930             }
4931         }
4932         else
4933         {
4934             window->Collapsed = false;
4935         }
4936         window->WantCollapseToggle = false;
4937 
4938         // SIZE
4939 
4940         // Calculate auto-fit size, handle automatic resize
4941         const ImVec2 size_auto_fit = CalcSizeAutoFit(window, window->SizeContents);
4942         ImVec2 size_full_modified(FLT_MAX, FLT_MAX);
4943         if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
4944         {
4945             // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
4946             if (!window_size_x_set_by_api)
4947                 window->SizeFull.x = size_full_modified.x = size_auto_fit.x;
4948             if (!window_size_y_set_by_api)
4949                 window->SizeFull.y = size_full_modified.y = size_auto_fit.y;
4950         }
4951         else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
4952         {
4953             // Auto-fit may only grow window during the first few frames
4954             // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
4955             if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
4956                 window->SizeFull.x = size_full_modified.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
4957             if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
4958                 window->SizeFull.y = size_full_modified.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
4959             if (!window->Collapsed)
4960                 MarkIniSettingsDirty(window);
4961         }
4962 
4963         // Apply minimum/maximum window size constraints and final size
4964         window->SizeFull = CalcSizeAfterConstraint(window, window->SizeFull);
4965         window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
4966 
4967         // SCROLLBAR STATUS
4968 
4969         // Update scrollbar status (based on the Size that was effective during last frame or the auto-resized Size).
4970         if (!window->Collapsed)
4971         {
4972             // When reading the current size we need to read it after size constraints have been applied
4973             float size_x_for_scrollbars = size_full_modified.x != FLT_MAX ? window->SizeFull.x : window->SizeFullAtLastBegin.x;
4974             float size_y_for_scrollbars = size_full_modified.y != FLT_MAX ? window->SizeFull.y : window->SizeFullAtLastBegin.y;
4975             window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
4976             window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
4977             if (window->ScrollbarX && !window->ScrollbarY)
4978                 window->ScrollbarY = (window->SizeContents.y > size_y_for_scrollbars - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);
4979             window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
4980         }
4981 
4982         // POSITION
4983 
4984         // Popup latch its initial position, will position itself when it appears next frame
4985         if (window_just_activated_by_user)
4986         {
4987             window->AutoPosLastDirection = ImGuiDir_None;
4988             if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
4989                 window->Pos = g.BeginPopupStack.back().OpenPopupPos;
4990         }
4991 
4992         // Position child window
4993         if (flags & ImGuiWindowFlags_ChildWindow)
4994         {
4995             IM_ASSERT(parent_window->Active);
4996             window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
4997             parent_window->DC.ChildWindows.push_back(window);
4998             if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
4999                 window->Pos = parent_window->DC.CursorPos;
5000         }
5001 
5002         const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesForResize == 0);
5003         if (window_pos_with_pivot)
5004             SetWindowPos(window, ImMax(style.DisplaySafeAreaPadding, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot), 0); // Position given a pivot (e.g. for centering)
5005         else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
5006             window->Pos = FindBestWindowPosForPopup(window);
5007         else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
5008             window->Pos = FindBestWindowPosForPopup(window);
5009         else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
5010             window->Pos = FindBestWindowPosForPopup(window);
5011 
5012         // Clamp position so it stays visible
5013         if (!(flags & ImGuiWindowFlags_ChildWindow))
5014         {
5015             if (!window_pos_set_by_api && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f) // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
5016             {
5017                 ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
5018                 ImVec2 size_for_clamping = ((g.IO.ConfigWindowsMoveFromTitleBarOnly) && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) ? ImVec2(window->Size.x, window->TitleBarHeight()) : window->Size;
5019                 window->Pos = ImMax(window->Pos + size_for_clamping, padding) - size_for_clamping;
5020                 window->Pos = ImMin(window->Pos, g.IO.DisplaySize - padding);
5021             }
5022         }
5023         window->Pos = ImFloor(window->Pos);
5024 
5025         // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
5026         window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
5027 
5028         // Prepare for item focus requests
5029         window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1);
5030         window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter+1)) % (window->FocusIdxTabCounter+1);
5031         window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1;
5032         window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX;
5033 
5034         // Apply scrolling
5035         window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window, true);
5036         window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
5037 
5038         // Apply window focus (new and reactivated windows are moved to front)
5039         bool want_focus = false;
5040         if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
5041             if (!(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup))
5042                 want_focus = true;
5043 
5044         // Handle manual resize: Resize Grips, Borders, Gamepad
5045         int border_held = -1;
5046         ImU32 resize_grip_col[4] = { 0 };
5047         const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // 4
5048         const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f);
5049         if (!window->Collapsed)
5050             UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]);
5051 
5052         // Default item width. Make it proportional to window size if window manually resizes
5053         if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
5054             window->ItemWidthDefault = (float)(int)(window->Size.x * 0.65f);
5055         else
5056             window->ItemWidthDefault = (float)(int)(g.FontSize * 16.0f);
5057 
5058         // DRAWING
5059 
5060         // Setup draw list and outer clipping rectangle
5061         window->DrawList->Clear();
5062         window->DrawList->Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0);
5063         window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
5064         ImRect viewport_rect(GetViewportRect());
5065         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
5066             PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true);
5067         else
5068             PushClipRect(viewport_rect.Min, viewport_rect.Max, true);
5069 
5070         // Draw modal window background (darkens what is behind them, all viewports)
5071         const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetFrontMostPopupModal() && window->HiddenFramesForResize <= 0;
5072         const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow);
5073         if (dim_bg_for_modal || dim_bg_for_window_list)
5074         {
5075             const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
5076             window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col);
5077         }
5078 
5079         // Draw navigation selection/windowing rectangle background
5080         if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim)
5081         {
5082             ImRect bb = window->Rect();
5083             bb.Expand(g.FontSize);
5084             if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway
5085                 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
5086         }
5087 
5088         // Draw window + handle manual resize
5089         const float window_rounding = window->WindowRounding;
5090         const float window_border_size = window->WindowBorderSize;
5091         const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
5092         const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
5093         const ImRect title_bar_rect = window->TitleBarRect();
5094         if (window->Collapsed)
5095         {
5096             // Title bar only
5097             float backup_border_size = style.FrameBorderSize;
5098             g.Style.FrameBorderSize = window->WindowBorderSize;
5099             ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
5100             RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
5101             g.Style.FrameBorderSize = backup_border_size;
5102         }
5103         else
5104         {
5105             // Window background
5106             if (!(flags & ImGuiWindowFlags_NoBackground))
5107             {
5108                 ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
5109                 if (g.NextWindowData.BgAlphaCond != 0)
5110                     bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(g.NextWindowData.BgAlphaVal) << IM_COL32_A_SHIFT);
5111                 window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot);
5112             }
5113             g.NextWindowData.BgAlphaCond = 0;
5114 
5115             // Title bar
5116             if (!(flags & ImGuiWindowFlags_NoTitleBar))
5117             {
5118                 ImU32 title_bar_col = GetColorU32(window->Collapsed ? ImGuiCol_TitleBgCollapsed : title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
5119                 window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top);
5120             }
5121 
5122             // Menu bar
5123             if (flags & ImGuiWindowFlags_MenuBar)
5124             {
5125                 ImRect menu_bar_rect = window->MenuBarRect();
5126                 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.
5127                 window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top);
5128                 if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
5129                     window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
5130             }
5131 
5132             // Scrollbars
5133             if (window->ScrollbarX)
5134                 Scrollbar(ImGuiLayoutType_Horizontal);
5135             if (window->ScrollbarY)
5136                 Scrollbar(ImGuiLayoutType_Vertical);
5137 
5138             // Render resize grips (after their input handling so we don't have a frame of latency)
5139             if (!(flags & ImGuiWindowFlags_NoResize))
5140             {
5141                 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5142                 {
5143                     const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5144                     const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos);
5145                     window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, grip_draw_size) : ImVec2(grip_draw_size, window_border_size)));
5146                     window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(grip_draw_size, window_border_size) : ImVec2(window_border_size, grip_draw_size)));
5147                     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);
5148                     window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]);
5149                 }
5150             }
5151 
5152             // Borders
5153             if (window_border_size > 0.0f && !(flags & ImGuiWindowFlags_NoBackground))
5154                 window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), window_rounding, ImDrawCornerFlags_All, window_border_size);
5155             if (border_held != -1)
5156             {
5157                 ImRect border = GetResizeBorderRect(window, border_held, grip_draw_size, 0.0f);
5158                 window->DrawList->AddLine(border.Min, border.Max, GetColorU32(ImGuiCol_SeparatorActive), ImMax(1.0f, window_border_size));
5159             }
5160             if (style.FrameBorderSize > 0 && !(flags & ImGuiWindowFlags_NoTitleBar))
5161                 window->DrawList->AddLine(title_bar_rect.GetBL() + ImVec2(style.WindowBorderSize, -1), title_bar_rect.GetBR() + ImVec2(-style.WindowBorderSize, -1), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
5162         }
5163 
5164         // Draw navigation selection/windowing rectangle border
5165         if (g.NavWindowingTargetAnim == window)
5166         {
5167             float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
5168             ImRect bb = window->Rect();
5169             bb.Expand(g.FontSize);
5170             if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward
5171             {
5172                 bb.Expand(-g.FontSize - 1.0f);
5173                 rounding = window->WindowRounding;
5174             }
5175             window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f);
5176         }
5177 
5178         // Store a backup of SizeFull which we will use next frame to decide if we need scrollbars.
5179         window->SizeFullAtLastBegin = window->SizeFull;
5180 
5181         // Update various regions. Variables they depends on are set above in this function.
5182         // FIXME: window->ContentsRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
5183         window->ContentsRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
5184         window->ContentsRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight();
5185         window->ContentsRegionRect.Max.x = window->Pos.x - window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x));
5186         window->ContentsRegionRect.Max.y = window->Pos.y - window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y));
5187 
5188         // Setup drawing context
5189         // (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.)
5190         window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x;
5191         window->DC.GroupOffset.x = 0.0f;
5192         window->DC.ColumnsOffset.x = 0.0f;
5193         window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, window->TitleBarHeight() + window->MenuBarHeight() + window->WindowPadding.y - window->Scroll.y);
5194         window->DC.CursorPos = window->DC.CursorStartPos;
5195         window->DC.CursorPosPrevLine = window->DC.CursorPos;
5196         window->DC.CursorMaxPos = window->DC.CursorStartPos;
5197         window->DC.CurrentLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
5198         window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
5199         window->DC.NavHideHighlightOneFrame = false;
5200         window->DC.NavHasScroll = (GetWindowScrollMaxY(window) > 0.0f);
5201         window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext;
5202         window->DC.NavLayerActiveMaskNext = 0x00;
5203         window->DC.MenuBarAppending = false;
5204         window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
5205         window->DC.ChildWindows.resize(0);
5206         window->DC.LayoutType = ImGuiLayoutType_Vertical;
5207         window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
5208         window->DC.ItemFlags = parent_window ? parent_window->DC.ItemFlags : ImGuiItemFlags_Default_;
5209         window->DC.ItemWidth = window->ItemWidthDefault;
5210         window->DC.TextWrapPos = -1.0f; // disabled
5211         window->DC.ItemFlagsStack.resize(0);
5212         window->DC.ItemWidthStack.resize(0);
5213         window->DC.TextWrapPosStack.resize(0);
5214         window->DC.ColumnsSet = NULL;
5215         window->DC.TreeDepth = 0;
5216         window->DC.TreeDepthMayJumpToParentOnPop = 0x00;
5217         window->DC.StateStorage = &window->StateStorage;
5218         window->DC.GroupStack.resize(0);
5219         window->MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user);
5220 
5221         if ((flags & ImGuiWindowFlags_ChildWindow) && (window->DC.ItemFlags != parent_window->DC.ItemFlags))
5222         {
5223             window->DC.ItemFlags = parent_window->DC.ItemFlags;
5224             window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
5225         }
5226 
5227         if (window->AutoFitFramesX > 0)
5228             window->AutoFitFramesX--;
5229         if (window->AutoFitFramesY > 0)
5230             window->AutoFitFramesY--;
5231 
5232         // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
5233         if (want_focus)
5234         {
5235             FocusWindow(window);
5236             NavInitWindow(window, false);
5237         }
5238 
5239         // Title bar
5240         if (!(flags & ImGuiWindowFlags_NoTitleBar))
5241         {
5242             // Close & collapse button are on layer 1 (same as menus) and don't default focus
5243             const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
5244             window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
5245             window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5246             window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu);
5247 
5248             // Collapse button
5249             if (!(flags & ImGuiWindowFlags_NoCollapse))
5250                 if (CollapseButton(window->GetID("#COLLAPSE"), window->Pos))
5251                     window->WantCollapseToggle = true; // Defer collapsing to next frame as we are too far in the Begin() function
5252 
5253             // Close button
5254             if (p_open != NULL)
5255             {
5256                 const float pad = style.FramePadding.y;
5257                 const float rad = g.FontSize * 0.5f;
5258                 if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-pad - rad, pad + rad), rad + 1))
5259                     *p_open = false;
5260             }
5261 
5262             window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5263             window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
5264             window->DC.ItemFlags = item_flags_backup;
5265 
5266             // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
5267             // FIXME: Refactor text alignment facilities along with RenderText helpers, this is too much code..
5268             const char* UNSAVED_DOCUMENT_MARKER = "*";
5269             float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f;
5270             ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
5271             ImRect text_r = title_bar_rect;
5272             float pad_left = (flags & ImGuiWindowFlags_NoCollapse) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x);
5273             float pad_right = (p_open == NULL)                     ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x);
5274             if (style.WindowTitleAlign.x > 0.0f)
5275                 pad_right = ImLerp(pad_right, pad_left, style.WindowTitleAlign.x);
5276             text_r.Min.x += pad_left;
5277             text_r.Max.x -= pad_right;
5278             ImRect clip_rect = text_r;
5279             clip_rect.Max.x = window->Pos.x + window->Size.x - (p_open ? title_bar_rect.GetHeight() - 3 : style.FramePadding.x); // Match the size of CloseButton()
5280             RenderTextClipped(text_r.Min, text_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_rect);
5281             if (flags & ImGuiWindowFlags_UnsavedDocument)
5282             {
5283                 ImVec2 marker_pos = ImVec2(ImMax(text_r.Min.x, text_r.Min.x + (text_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x) + text_size.x, text_r.Min.y) + ImVec2(2 - marker_size_x, 0.0f);
5284                 ImVec2 off = ImVec2(0.0f, (float)(int)(-g.FontSize * 0.25f));
5285                 RenderTextClipped(marker_pos + off, text_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_rect);
5286             }
5287         }
5288 
5289         // Save clipped aabb so we can access it in constant-time in FindHoveredWindow()
5290         window->OuterRectClipped = window->Rect();
5291         window->OuterRectClipped.ClipWith(window->ClipRect);
5292 
5293         // Pressing CTRL+C while holding on a window copy its content to the clipboard
5294         // 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.
5295         // Maybe we can support CTRL+C on every element?
5296         /*
5297         if (g.ActiveId == move_id)
5298             if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
5299                 LogToClipboard();
5300         */
5301 
5302         // Inner rectangle
5303         // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame
5304         // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
5305         window->InnerMainRect.Min.x = title_bar_rect.Min.x + window->WindowBorderSize;
5306         window->InnerMainRect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight() + (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
5307         window->InnerMainRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x - window->WindowBorderSize;
5308         window->InnerMainRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y - window->WindowBorderSize;
5309         //window->DrawList->AddRect(window->InnerRect.Min, window->InnerRect.Max, IM_COL32_WHITE);
5310 
5311         // Inner clipping rectangle
5312         // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
5313         window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerMainRect.Min.x + ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize)));
5314         window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerMainRect.Min.y);
5315         window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerMainRect.Max.x - ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize)));
5316         window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerMainRect.Max.y);
5317 
5318         // After Begin() we fill the last item / hovered data based on title bar data. It is a standard behavior (to allow creation of context menus on title bar only, etc.).
5319         window->DC.LastItemId = window->MoveId;
5320         window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;
5321         window->DC.LastItemRect = title_bar_rect;
5322     }
5323 
5324     PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
5325 
5326     // 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)
5327     if (first_begin_of_the_frame)
5328         window->WriteAccessed = false;
5329 
5330     window->BeginCount++;
5331     g.NextWindowData.Clear();
5332 
5333     if (flags & ImGuiWindowFlags_ChildWindow)
5334     {
5335         // Child window can be out of sight and have "negative" clip windows.
5336         // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
5337         IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
5338 
5339         if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5340             if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
5341                 window->HiddenFramesRegular = 1;
5342 
5343         // Completely hide along with parent or if parent is collapsed
5344         if (parent_window && (parent_window->Collapsed || parent_window->Hidden))
5345             window->HiddenFramesRegular = 1;
5346     }
5347 
5348     // 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)
5349     if (style.Alpha <= 0.0f)
5350         window->HiddenFramesRegular = 1;
5351 
5352     // Update the Hidden flag
5353     window->Hidden = (window->HiddenFramesRegular > 0) || (window->HiddenFramesForResize > 0);
5354 
5355     // Return false if we don't intend to display anything to allow user to perform an early out optimization
5356     window->SkipItems = (window->Collapsed || !window->Active || window->Hidden) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesForResize <= 0;
5357 
5358     return !window->SkipItems;
5359 }
5360 
5361 // Old Begin() API with 5 parameters, avoid calling this version directly! Use SetNextWindowSize()/SetNextWindowBgAlpha() + Begin() instead.
5362 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
Begin(const char * name,bool * p_open,const ImVec2 & size_first_use,float bg_alpha_override,ImGuiWindowFlags flags)5363 bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_first_use, float bg_alpha_override, ImGuiWindowFlags flags)
5364 {
5365     // Old API feature: we could pass the initial window size as a parameter. This was misleading because it only had an effect if the window didn't have data in the .ini file.
5366     if (size_first_use.x != 0.0f || size_first_use.y != 0.0f)
5367         SetNextWindowSize(size_first_use, ImGuiCond_FirstUseEver);
5368 
5369     // Old API feature: override the window background alpha with a parameter.
5370     if (bg_alpha_override >= 0.0f)
5371         SetNextWindowBgAlpha(bg_alpha_override);
5372 
5373     return Begin(name, p_open, flags);
5374 }
5375 #endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS
5376 
End()5377 void ImGui::End()
5378 {
5379     ImGuiContext& g = *GImGui;
5380 
5381     if (g.CurrentWindowStack.Size <= 1 && g.FrameScopePushedImplicitWindow)
5382     {
5383         IM_ASSERT(g.CurrentWindowStack.Size > 1 && "Calling End() too many times!");
5384         return; // FIXME-ERRORHANDLING
5385     }
5386     IM_ASSERT(g.CurrentWindowStack.Size > 0);
5387 
5388     ImGuiWindow* window = g.CurrentWindow;
5389 
5390     if (window->DC.ColumnsSet != NULL)
5391         EndColumns();
5392     PopClipRect();   // Inner window clip rectangle
5393 
5394     // Stop logging
5395     if (!(window->Flags & ImGuiWindowFlags_ChildWindow))    // FIXME: add more options for scope of logging
5396         LogFinish();
5397 
5398     // Pop from window stack
5399     g.CurrentWindowStack.pop_back();
5400     if (window->Flags & ImGuiWindowFlags_Popup)
5401         g.BeginPopupStack.pop_back();
5402     CheckStacksSize(window, false);
5403     SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
5404 }
5405 
BringWindowToFocusFront(ImGuiWindow * window)5406 void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
5407 {
5408     ImGuiContext& g = *GImGui;
5409     if (g.WindowsFocusOrder.back() == window)
5410         return;
5411     for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the front most window
5412         if (g.WindowsFocusOrder[i] == window)
5413         {
5414             memmove(&g.WindowsFocusOrder[i], &g.WindowsFocusOrder[i + 1], (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*));
5415             g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window;
5416             break;
5417         }
5418 }
5419 
BringWindowToDisplayFront(ImGuiWindow * window)5420 void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
5421 {
5422     ImGuiContext& g = *GImGui;
5423     ImGuiWindow* current_front_window = g.Windows.back();
5424     if (current_front_window == window || current_front_window->RootWindow == window)
5425         return;
5426     for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the front most window
5427         if (g.Windows[i] == window)
5428         {
5429             memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
5430             g.Windows[g.Windows.Size - 1] = window;
5431             break;
5432         }
5433 }
5434 
BringWindowToDisplayBack(ImGuiWindow * window)5435 void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
5436 {
5437     ImGuiContext& g = *GImGui;
5438     if (g.Windows[0] == window)
5439         return;
5440     for (int i = 0; i < g.Windows.Size; i++)
5441         if (g.Windows[i] == window)
5442         {
5443             memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
5444             g.Windows[0] = window;
5445             break;
5446         }
5447 }
5448 
5449 // Moving window to front of display and set focus (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)5450 void ImGui::FocusWindow(ImGuiWindow* window)
5451 {
5452     ImGuiContext& g = *GImGui;
5453 
5454     if (g.NavWindow != window)
5455     {
5456         g.NavWindow = window;
5457         if (window && g.NavDisableMouseHover)
5458             g.NavMousePosDirty = true;
5459         g.NavInitRequest = false;
5460         g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
5461         g.NavIdIsAlive = false;
5462         g.NavLayer = ImGuiNavLayer_Main;
5463         //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", g.FrameCount, window ? window->Name : NULL);
5464     }
5465 
5466     // Passing NULL allow to disable keyboard focus
5467     if (!window)
5468         return;
5469 
5470     // Move the root window to the top of the pile
5471     if (window->RootWindow)
5472         window = window->RootWindow;
5473 
5474     // Steal focus on active widgets
5475     if (window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement should be unnecessary. Need further testing before removing it..
5476         if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != window)
5477             ClearActiveID();
5478 
5479     // Bring to front
5480     BringWindowToFocusFront(window);
5481     if (!(window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus))
5482         BringWindowToDisplayFront(window);
5483 }
5484 
FocusPreviousWindowIgnoringOne(ImGuiWindow * ignore_window)5485 void ImGui::FocusPreviousWindowIgnoringOne(ImGuiWindow* ignore_window)
5486 {
5487     ImGuiContext& g = *GImGui;
5488     for (int i = g.WindowsFocusOrder.Size - 1; i >= 0; i--)
5489     {
5490         // 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.
5491         ImGuiWindow* window = g.WindowsFocusOrder[i];
5492         if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow))
5493             if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
5494             {
5495                 ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window);
5496                 FocusWindow(focus_window);
5497                 return;
5498             }
5499     }
5500 }
5501 
PushItemWidth(float item_width)5502 void ImGui::PushItemWidth(float item_width)
5503 {
5504     ImGuiWindow* window = GetCurrentWindow();
5505     window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
5506     window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
5507 }
5508 
PushMultiItemsWidths(int components,float w_full)5509 void ImGui::PushMultiItemsWidths(int components, float w_full)
5510 {
5511     ImGuiWindow* window = GetCurrentWindow();
5512     const ImGuiStyle& style = GImGui->Style;
5513     if (w_full <= 0.0f)
5514         w_full = CalcItemWidth();
5515     const float w_item_one  = ImMax(1.0f, (float)(int)((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
5516     const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
5517     window->DC.ItemWidthStack.push_back(w_item_last);
5518     for (int i = 0; i < components-1; i++)
5519         window->DC.ItemWidthStack.push_back(w_item_one);
5520     window->DC.ItemWidth = window->DC.ItemWidthStack.back();
5521 }
5522 
PopItemWidth()5523 void ImGui::PopItemWidth()
5524 {
5525     ImGuiWindow* window = GetCurrentWindow();
5526     window->DC.ItemWidthStack.pop_back();
5527     window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
5528 }
5529 
CalcItemWidth()5530 float ImGui::CalcItemWidth()
5531 {
5532     ImGuiWindow* window = GetCurrentWindowRead();
5533     float w = window->DC.ItemWidth;
5534     if (w < 0.0f)
5535     {
5536         // Align to a right-side limit. We include 1 frame padding in the calculation because this is how the width is always used (we add 2 frame padding to it), but we could move that responsibility to the widget as well.
5537         float width_to_right_edge = GetContentRegionAvail().x;
5538         w = ImMax(1.0f, width_to_right_edge + w);
5539     }
5540     w = (float)(int)w;
5541     return w;
5542 }
5543 
SetCurrentFont(ImFont * font)5544 void ImGui::SetCurrentFont(ImFont* font)
5545 {
5546     ImGuiContext& g = *GImGui;
5547     IM_ASSERT(font && font->IsLoaded());    // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
5548     IM_ASSERT(font->Scale > 0.0f);
5549     g.Font = font;
5550     g.FontBaseSize = g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale;
5551     g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
5552 
5553     ImFontAtlas* atlas = g.Font->ContainerAtlas;
5554     g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
5555     g.DrawListSharedData.Font = g.Font;
5556     g.DrawListSharedData.FontSize = g.FontSize;
5557 }
5558 
PushFont(ImFont * font)5559 void ImGui::PushFont(ImFont* font)
5560 {
5561     ImGuiContext& g = *GImGui;
5562     if (!font)
5563         font = GetDefaultFont();
5564     SetCurrentFont(font);
5565     g.FontStack.push_back(font);
5566     g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
5567 }
5568 
PopFont()5569 void  ImGui::PopFont()
5570 {
5571     ImGuiContext& g = *GImGui;
5572     g.CurrentWindow->DrawList->PopTextureID();
5573     g.FontStack.pop_back();
5574     SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
5575 }
5576 
PushItemFlag(ImGuiItemFlags option,bool enabled)5577 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
5578 {
5579     ImGuiWindow* window = GetCurrentWindow();
5580     if (enabled)
5581         window->DC.ItemFlags |= option;
5582     else
5583         window->DC.ItemFlags &= ~option;
5584     window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
5585 }
5586 
PopItemFlag()5587 void ImGui::PopItemFlag()
5588 {
5589     ImGuiWindow* window = GetCurrentWindow();
5590     window->DC.ItemFlagsStack.pop_back();
5591     window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back();
5592 }
5593 
5594 // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system.
PushAllowKeyboardFocus(bool allow_keyboard_focus)5595 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
5596 {
5597     PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus);
5598 }
5599 
PopAllowKeyboardFocus()5600 void ImGui::PopAllowKeyboardFocus()
5601 {
5602     PopItemFlag();
5603 }
5604 
PushButtonRepeat(bool repeat)5605 void ImGui::PushButtonRepeat(bool repeat)
5606 {
5607     PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
5608 }
5609 
PopButtonRepeat()5610 void ImGui::PopButtonRepeat()
5611 {
5612     PopItemFlag();
5613 }
5614 
PushTextWrapPos(float wrap_pos_x)5615 void ImGui::PushTextWrapPos(float wrap_pos_x)
5616 {
5617     ImGuiWindow* window = GetCurrentWindow();
5618     window->DC.TextWrapPos = wrap_pos_x;
5619     window->DC.TextWrapPosStack.push_back(wrap_pos_x);
5620 }
5621 
PopTextWrapPos()5622 void ImGui::PopTextWrapPos()
5623 {
5624     ImGuiWindow* window = GetCurrentWindow();
5625     window->DC.TextWrapPosStack.pop_back();
5626     window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
5627 }
5628 
5629 // 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)5630 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
5631 {
5632     ImGuiContext& g = *GImGui;
5633     ImGuiColorMod backup;
5634     backup.Col = idx;
5635     backup.BackupValue = g.Style.Colors[idx];
5636     g.ColorModifiers.push_back(backup);
5637     g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
5638 }
5639 
PushStyleColor(ImGuiCol idx,const ImVec4 & col)5640 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
5641 {
5642     ImGuiContext& g = *GImGui;
5643     ImGuiColorMod backup;
5644     backup.Col = idx;
5645     backup.BackupValue = g.Style.Colors[idx];
5646     g.ColorModifiers.push_back(backup);
5647     g.Style.Colors[idx] = col;
5648 }
5649 
PopStyleColor(int count)5650 void ImGui::PopStyleColor(int count)
5651 {
5652     ImGuiContext& g = *GImGui;
5653     while (count > 0)
5654     {
5655         ImGuiColorMod& backup = g.ColorModifiers.back();
5656         g.Style.Colors[backup.Col] = backup.BackupValue;
5657         g.ColorModifiers.pop_back();
5658         count--;
5659     }
5660 }
5661 
5662 struct ImGuiStyleVarInfo
5663 {
5664     ImGuiDataType   Type;
5665     ImU32           Count;
5666     ImU32           Offset;
GetVarPtrImGuiStyleVarInfo5667     void*           GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
5668 };
5669 
5670 static const ImGuiStyleVarInfo GStyleVarInfo[] =
5671 {
5672     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) },              // ImGuiStyleVar_Alpha
5673     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) },      // ImGuiStyleVar_WindowPadding
5674     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) },     // ImGuiStyleVar_WindowRounding
5675     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) },   // ImGuiStyleVar_WindowBorderSize
5676     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) },      // ImGuiStyleVar_WindowMinSize
5677     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) },   // ImGuiStyleVar_WindowTitleAlign
5678     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) },      // ImGuiStyleVar_ChildRounding
5679     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) },    // ImGuiStyleVar_ChildBorderSize
5680     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) },      // ImGuiStyleVar_PopupRounding
5681     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) },    // ImGuiStyleVar_PopupBorderSize
5682     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) },       // ImGuiStyleVar_FramePadding
5683     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) },      // ImGuiStyleVar_FrameRounding
5684     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) },    // ImGuiStyleVar_FrameBorderSize
5685     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) },        // ImGuiStyleVar_ItemSpacing
5686     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) },   // ImGuiStyleVar_ItemInnerSpacing
5687     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) },      // ImGuiStyleVar_IndentSpacing
5688     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) },      // ImGuiStyleVar_ScrollbarSize
5689     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) },  // ImGuiStyleVar_ScrollbarRounding
5690     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) },        // ImGuiStyleVar_GrabMinSize
5691     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) },       // ImGuiStyleVar_GrabRounding
5692     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) },        // ImGuiStyleVar_TabRounding
5693     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) },    // ImGuiStyleVar_ButtonTextAlign
5694 };
5695 
GetStyleVarInfo(ImGuiStyleVar idx)5696 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
5697 {
5698     IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
5699     IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
5700     return &GStyleVarInfo[idx];
5701 }
5702 
PushStyleVar(ImGuiStyleVar idx,float val)5703 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
5704 {
5705     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
5706     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
5707     {
5708         ImGuiContext& g = *GImGui;
5709         float* pvar = (float*)var_info->GetVarPtr(&g.Style);
5710         g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
5711         *pvar = val;
5712         return;
5713     }
5714     IM_ASSERT(0); // Called function with wrong-type? Variable is not a float.
5715 }
5716 
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)5717 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
5718 {
5719     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
5720     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
5721     {
5722         ImGuiContext& g = *GImGui;
5723         ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
5724         g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
5725         *pvar = val;
5726         return;
5727     }
5728     IM_ASSERT(0); // Called function with wrong-type? Variable is not a ImVec2.
5729 }
5730 
PopStyleVar(int count)5731 void ImGui::PopStyleVar(int count)
5732 {
5733     ImGuiContext& g = *GImGui;
5734     while (count > 0)
5735     {
5736         // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
5737         ImGuiStyleMod& backup = g.StyleModifiers.back();
5738         const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
5739         void* data = info->GetVarPtr(&g.Style);
5740         if (info->Type == ImGuiDataType_Float && info->Count == 1)      { ((float*)data)[0] = backup.BackupFloat[0]; }
5741         else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
5742         g.StyleModifiers.pop_back();
5743         count--;
5744     }
5745 }
5746 
GetStyleColorName(ImGuiCol idx)5747 const char* ImGui::GetStyleColorName(ImGuiCol idx)
5748 {
5749     // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
5750     switch (idx)
5751     {
5752     case ImGuiCol_Text: return "Text";
5753     case ImGuiCol_TextDisabled: return "TextDisabled";
5754     case ImGuiCol_WindowBg: return "WindowBg";
5755     case ImGuiCol_ChildBg: return "ChildBg";
5756     case ImGuiCol_PopupBg: return "PopupBg";
5757     case ImGuiCol_Border: return "Border";
5758     case ImGuiCol_BorderShadow: return "BorderShadow";
5759     case ImGuiCol_FrameBg: return "FrameBg";
5760     case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
5761     case ImGuiCol_FrameBgActive: return "FrameBgActive";
5762     case ImGuiCol_TitleBg: return "TitleBg";
5763     case ImGuiCol_TitleBgActive: return "TitleBgActive";
5764     case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
5765     case ImGuiCol_MenuBarBg: return "MenuBarBg";
5766     case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
5767     case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
5768     case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
5769     case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
5770     case ImGuiCol_CheckMark: return "CheckMark";
5771     case ImGuiCol_SliderGrab: return "SliderGrab";
5772     case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
5773     case ImGuiCol_Button: return "Button";
5774     case ImGuiCol_ButtonHovered: return "ButtonHovered";
5775     case ImGuiCol_ButtonActive: return "ButtonActive";
5776     case ImGuiCol_Header: return "Header";
5777     case ImGuiCol_HeaderHovered: return "HeaderHovered";
5778     case ImGuiCol_HeaderActive: return "HeaderActive";
5779     case ImGuiCol_Separator: return "Separator";
5780     case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
5781     case ImGuiCol_SeparatorActive: return "SeparatorActive";
5782     case ImGuiCol_ResizeGrip: return "ResizeGrip";
5783     case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
5784     case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
5785     case ImGuiCol_Tab: return "Tab";
5786     case ImGuiCol_TabHovered: return "TabHovered";
5787     case ImGuiCol_TabActive: return "TabActive";
5788     case ImGuiCol_TabUnfocused: return "TabUnfocused";
5789     case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive";
5790     case ImGuiCol_PlotLines: return "PlotLines";
5791     case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
5792     case ImGuiCol_PlotHistogram: return "PlotHistogram";
5793     case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
5794     case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
5795     case ImGuiCol_DragDropTarget: return "DragDropTarget";
5796     case ImGuiCol_NavHighlight: return "NavHighlight";
5797     case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
5798     case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
5799     case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
5800     }
5801     IM_ASSERT(0);
5802     return "Unknown";
5803 }
5804 
IsWindowChildOf(ImGuiWindow * window,ImGuiWindow * potential_parent)5805 bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
5806 {
5807     if (window->RootWindow == potential_parent)
5808         return true;
5809     while (window != NULL)
5810     {
5811         if (window == potential_parent)
5812             return true;
5813         window = window->ParentWindow;
5814     }
5815     return false;
5816 }
5817 
IsWindowHovered(ImGuiHoveredFlags flags)5818 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
5819 {
5820     IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0);   // Flags not supported by this function
5821     ImGuiContext& g = *GImGui;
5822 
5823     if (flags & ImGuiHoveredFlags_AnyWindow)
5824     {
5825         if (g.HoveredWindow == NULL)
5826             return false;
5827     }
5828     else
5829     {
5830         switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
5831         {
5832         case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
5833             if (g.HoveredRootWindow != g.CurrentWindow->RootWindow)
5834                 return false;
5835             break;
5836         case ImGuiHoveredFlags_RootWindow:
5837             if (g.HoveredWindow != g.CurrentWindow->RootWindow)
5838                 return false;
5839             break;
5840         case ImGuiHoveredFlags_ChildWindows:
5841             if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow))
5842                 return false;
5843             break;
5844         default:
5845             if (g.HoveredWindow != g.CurrentWindow)
5846                 return false;
5847             break;
5848         }
5849     }
5850 
5851     if (!IsWindowContentHoverable(g.HoveredRootWindow, flags))
5852         return false;
5853     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
5854         if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
5855             return false;
5856     return true;
5857 }
5858 
IsWindowFocused(ImGuiFocusedFlags flags)5859 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
5860 {
5861     ImGuiContext& g = *GImGui;
5862 
5863     if (flags & ImGuiFocusedFlags_AnyWindow)
5864         return g.NavWindow != NULL;
5865 
5866     IM_ASSERT(g.CurrentWindow);     // Not inside a Begin()/End()
5867     switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
5868     {
5869     case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
5870         return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
5871     case ImGuiFocusedFlags_RootWindow:
5872         return g.NavWindow == g.CurrentWindow->RootWindow;
5873     case ImGuiFocusedFlags_ChildWindows:
5874         return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
5875     default:
5876         return g.NavWindow == g.CurrentWindow;
5877     }
5878 }
5879 
5880 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
5881 // Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmaticaly.
5882 // If you want a window to never be focused, you may use the e.g. NoInputs flag.
IsWindowNavFocusable(ImGuiWindow * window)5883 bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
5884 {
5885     return window->Active && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
5886 }
5887 
GetWindowWidth()5888 float ImGui::GetWindowWidth()
5889 {
5890     ImGuiWindow* window = GImGui->CurrentWindow;
5891     return window->Size.x;
5892 }
5893 
GetWindowHeight()5894 float ImGui::GetWindowHeight()
5895 {
5896     ImGuiWindow* window = GImGui->CurrentWindow;
5897     return window->Size.y;
5898 }
5899 
GetWindowPos()5900 ImVec2 ImGui::GetWindowPos()
5901 {
5902     ImGuiContext& g = *GImGui;
5903     ImGuiWindow* window = g.CurrentWindow;
5904     return window->Pos;
5905 }
5906 
SetWindowScrollX(ImGuiWindow * window,float new_scroll_x)5907 void ImGui::SetWindowScrollX(ImGuiWindow* window, float new_scroll_x)
5908 {
5909     window->DC.CursorMaxPos.x += window->Scroll.x; // SizeContents is generally computed based on CursorMaxPos which is affected by scroll position, so we need to apply our change to it.
5910     window->Scroll.x = new_scroll_x;
5911     window->DC.CursorMaxPos.x -= window->Scroll.x;
5912 }
5913 
SetWindowScrollY(ImGuiWindow * window,float new_scroll_y)5914 void ImGui::SetWindowScrollY(ImGuiWindow* window, float new_scroll_y)
5915 {
5916     window->DC.CursorMaxPos.y += window->Scroll.y; // SizeContents is generally computed based on CursorMaxPos which is affected by scroll position, so we need to apply our change to it.
5917     window->Scroll.y = new_scroll_y;
5918     window->DC.CursorMaxPos.y -= window->Scroll.y;
5919 }
5920 
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiCond cond)5921 static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
5922 {
5923     // Test condition (NB: bit 0 is always true) and clear flags for next time
5924     if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
5925         return;
5926 
5927     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
5928     window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5929     window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
5930 
5931     // Set
5932     const ImVec2 old_pos = window->Pos;
5933     window->Pos = ImFloor(pos);
5934     window->DC.CursorPos += (window->Pos - old_pos);    // 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
5935     window->DC.CursorMaxPos += (window->Pos - old_pos); // And more importantly we need to adjust this so size calculation doesn't get affected.
5936 }
5937 
SetWindowPos(const ImVec2 & pos,ImGuiCond cond)5938 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
5939 {
5940     ImGuiWindow* window = GetCurrentWindowRead();
5941     SetWindowPos(window, pos, cond);
5942 }
5943 
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiCond cond)5944 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
5945 {
5946     if (ImGuiWindow* window = FindWindowByName(name))
5947         SetWindowPos(window, pos, cond);
5948 }
5949 
GetWindowSize()5950 ImVec2 ImGui::GetWindowSize()
5951 {
5952     ImGuiWindow* window = GetCurrentWindowRead();
5953     return window->Size;
5954 }
5955 
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiCond cond)5956 static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
5957 {
5958     // Test condition (NB: bit 0 is always true) and clear flags for next time
5959     if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
5960         return;
5961 
5962     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
5963     window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5964 
5965     // Set
5966     if (size.x > 0.0f)
5967     {
5968         window->AutoFitFramesX = 0;
5969         window->SizeFull.x = ImFloor(size.x);
5970     }
5971     else
5972     {
5973         window->AutoFitFramesX = 2;
5974         window->AutoFitOnlyGrows = false;
5975     }
5976     if (size.y > 0.0f)
5977     {
5978         window->AutoFitFramesY = 0;
5979         window->SizeFull.y = ImFloor(size.y);
5980     }
5981     else
5982     {
5983         window->AutoFitFramesY = 2;
5984         window->AutoFitOnlyGrows = false;
5985     }
5986 }
5987 
SetWindowSize(const ImVec2 & size,ImGuiCond cond)5988 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
5989 {
5990     SetWindowSize(GImGui->CurrentWindow, size, cond);
5991 }
5992 
SetWindowSize(const char * name,const ImVec2 & size,ImGuiCond cond)5993 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
5994 {
5995     if (ImGuiWindow* window = FindWindowByName(name))
5996         SetWindowSize(window, size, cond);
5997 }
5998 
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiCond cond)5999 static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
6000 {
6001     // Test condition (NB: bit 0 is always true) and clear flags for next time
6002     if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
6003         return;
6004     window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6005 
6006     // Set
6007     window->Collapsed = collapsed;
6008 }
6009 
SetWindowCollapsed(bool collapsed,ImGuiCond cond)6010 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
6011 {
6012     SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
6013 }
6014 
IsWindowCollapsed()6015 bool ImGui::IsWindowCollapsed()
6016 {
6017     ImGuiWindow* window = GetCurrentWindowRead();
6018     return window->Collapsed;
6019 }
6020 
IsWindowAppearing()6021 bool ImGui::IsWindowAppearing()
6022 {
6023     ImGuiWindow* window = GetCurrentWindowRead();
6024     return window->Appearing;
6025 }
6026 
SetWindowCollapsed(const char * name,bool collapsed,ImGuiCond cond)6027 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
6028 {
6029     if (ImGuiWindow* window = FindWindowByName(name))
6030         SetWindowCollapsed(window, collapsed, cond);
6031 }
6032 
SetWindowFocus()6033 void ImGui::SetWindowFocus()
6034 {
6035     FocusWindow(GImGui->CurrentWindow);
6036 }
6037 
SetWindowFocus(const char * name)6038 void ImGui::SetWindowFocus(const char* name)
6039 {
6040     if (name)
6041     {
6042         if (ImGuiWindow* window = FindWindowByName(name))
6043             FocusWindow(window);
6044     }
6045     else
6046     {
6047         FocusWindow(NULL);
6048     }
6049 }
6050 
SetNextWindowPos(const ImVec2 & pos,ImGuiCond cond,const ImVec2 & pivot)6051 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
6052 {
6053     ImGuiContext& g = *GImGui;
6054     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6055     g.NextWindowData.PosVal = pos;
6056     g.NextWindowData.PosPivotVal = pivot;
6057     g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
6058 }
6059 
SetNextWindowSize(const ImVec2 & size,ImGuiCond cond)6060 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
6061 {
6062     ImGuiContext& g = *GImGui;
6063     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6064     g.NextWindowData.SizeVal = size;
6065     g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
6066 }
6067 
SetNextWindowSizeConstraints(const ImVec2 & size_min,const ImVec2 & size_max,ImGuiSizeCallback custom_callback,void * custom_callback_user_data)6068 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
6069 {
6070     ImGuiContext& g = *GImGui;
6071     g.NextWindowData.SizeConstraintCond = ImGuiCond_Always;
6072     g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
6073     g.NextWindowData.SizeCallback = custom_callback;
6074     g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
6075 }
6076 
SetNextWindowContentSize(const ImVec2 & size)6077 void ImGui::SetNextWindowContentSize(const ImVec2& size)
6078 {
6079     ImGuiContext& g = *GImGui;
6080     g.NextWindowData.ContentSizeVal = size;  // In Begin() we will add the size of window decorations (title bar, menu etc.) to that to form a SizeContents value.
6081     g.NextWindowData.ContentSizeCond = ImGuiCond_Always;
6082 }
6083 
SetNextWindowCollapsed(bool collapsed,ImGuiCond cond)6084 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
6085 {
6086     ImGuiContext& g = *GImGui;
6087     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6088     g.NextWindowData.CollapsedVal = collapsed;
6089     g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
6090 }
6091 
SetNextWindowFocus()6092 void ImGui::SetNextWindowFocus()
6093 {
6094     ImGuiContext& g = *GImGui;
6095     g.NextWindowData.FocusCond = ImGuiCond_Always;   // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op)
6096 }
6097 
SetNextWindowBgAlpha(float alpha)6098 void ImGui::SetNextWindowBgAlpha(float alpha)
6099 {
6100     ImGuiContext& g = *GImGui;
6101     g.NextWindowData.BgAlphaVal = alpha;
6102     g.NextWindowData.BgAlphaCond = ImGuiCond_Always; // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op)
6103 }
6104 
6105 // In window space (not screen space!)
GetContentRegionMax()6106 ImVec2 ImGui::GetContentRegionMax()
6107 {
6108     ImGuiWindow* window = GetCurrentWindowRead();
6109     ImVec2 mx = window->ContentsRegionRect.Max - window->Pos;
6110     if (window->DC.ColumnsSet)
6111         mx.x = GetColumnOffset(window->DC.ColumnsSet->Current + 1) - window->WindowPadding.x;
6112     return mx;
6113 }
6114 
GetContentRegionAvail()6115 ImVec2 ImGui::GetContentRegionAvail()
6116 {
6117     ImGuiWindow* window = GetCurrentWindowRead();
6118     return GetContentRegionMax() - (window->DC.CursorPos - window->Pos);
6119 }
6120 
GetContentRegionAvailWidth()6121 float ImGui::GetContentRegionAvailWidth()
6122 {
6123     return GetContentRegionAvail().x;
6124 }
6125 
6126 // In window space (not screen space!)
GetWindowContentRegionMin()6127 ImVec2 ImGui::GetWindowContentRegionMin()
6128 {
6129     ImGuiWindow* window = GetCurrentWindowRead();
6130     return window->ContentsRegionRect.Min - window->Pos;
6131 }
6132 
GetWindowContentRegionMax()6133 ImVec2 ImGui::GetWindowContentRegionMax()
6134 {
6135     ImGuiWindow* window = GetCurrentWindowRead();
6136     return window->ContentsRegionRect.Max - window->Pos;
6137 }
6138 
GetWindowContentRegionWidth()6139 float ImGui::GetWindowContentRegionWidth()
6140 {
6141     ImGuiWindow* window = GetCurrentWindowRead();
6142     return window->ContentsRegionRect.GetWidth();
6143 }
6144 
GetTextLineHeight()6145 float ImGui::GetTextLineHeight()
6146 {
6147     ImGuiContext& g = *GImGui;
6148     return g.FontSize;
6149 }
6150 
GetTextLineHeightWithSpacing()6151 float ImGui::GetTextLineHeightWithSpacing()
6152 {
6153     ImGuiContext& g = *GImGui;
6154     return g.FontSize + g.Style.ItemSpacing.y;
6155 }
6156 
GetFrameHeight()6157 float ImGui::GetFrameHeight()
6158 {
6159     ImGuiContext& g = *GImGui;
6160     return g.FontSize + g.Style.FramePadding.y * 2.0f;
6161 }
6162 
GetFrameHeightWithSpacing()6163 float ImGui::GetFrameHeightWithSpacing()
6164 {
6165     ImGuiContext& g = *GImGui;
6166     return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
6167 }
6168 
GetWindowDrawList()6169 ImDrawList* ImGui::GetWindowDrawList()
6170 {
6171     ImGuiWindow* window = GetCurrentWindow();
6172     return window->DrawList;
6173 }
6174 
GetFont()6175 ImFont* ImGui::GetFont()
6176 {
6177     return GImGui->Font;
6178 }
6179 
GetFontSize()6180 float ImGui::GetFontSize()
6181 {
6182     return GImGui->FontSize;
6183 }
6184 
GetFontTexUvWhitePixel()6185 ImVec2 ImGui::GetFontTexUvWhitePixel()
6186 {
6187     return GImGui->DrawListSharedData.TexUvWhitePixel;
6188 }
6189 
SetWindowFontScale(float scale)6190 void ImGui::SetWindowFontScale(float scale)
6191 {
6192     ImGuiContext& g = *GImGui;
6193     ImGuiWindow* window = GetCurrentWindow();
6194     window->FontWindowScale = scale;
6195     g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
6196 }
6197 
6198 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
6199 // 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()6200 ImVec2 ImGui::GetCursorPos()
6201 {
6202     ImGuiWindow* window = GetCurrentWindowRead();
6203     return window->DC.CursorPos - window->Pos + window->Scroll;
6204 }
6205 
GetCursorPosX()6206 float ImGui::GetCursorPosX()
6207 {
6208     ImGuiWindow* window = GetCurrentWindowRead();
6209     return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
6210 }
6211 
GetCursorPosY()6212 float ImGui::GetCursorPosY()
6213 {
6214     ImGuiWindow* window = GetCurrentWindowRead();
6215     return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
6216 }
6217 
SetCursorPos(const ImVec2 & local_pos)6218 void ImGui::SetCursorPos(const ImVec2& local_pos)
6219 {
6220     ImGuiWindow* window = GetCurrentWindow();
6221     window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
6222     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
6223 }
6224 
SetCursorPosX(float x)6225 void ImGui::SetCursorPosX(float x)
6226 {
6227     ImGuiWindow* window = GetCurrentWindow();
6228     window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
6229     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
6230 }
6231 
SetCursorPosY(float y)6232 void ImGui::SetCursorPosY(float y)
6233 {
6234     ImGuiWindow* window = GetCurrentWindow();
6235     window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
6236     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
6237 }
6238 
GetCursorStartPos()6239 ImVec2 ImGui::GetCursorStartPos()
6240 {
6241     ImGuiWindow* window = GetCurrentWindowRead();
6242     return window->DC.CursorStartPos - window->Pos;
6243 }
6244 
GetCursorScreenPos()6245 ImVec2 ImGui::GetCursorScreenPos()
6246 {
6247     ImGuiWindow* window = GetCurrentWindowRead();
6248     return window->DC.CursorPos;
6249 }
6250 
SetCursorScreenPos(const ImVec2 & pos)6251 void ImGui::SetCursorScreenPos(const ImVec2& pos)
6252 {
6253     ImGuiWindow* window = GetCurrentWindow();
6254     window->DC.CursorPos = pos;
6255     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
6256 }
6257 
GetScrollX()6258 float ImGui::GetScrollX()
6259 {
6260     return GImGui->CurrentWindow->Scroll.x;
6261 }
6262 
GetScrollY()6263 float ImGui::GetScrollY()
6264 {
6265     return GImGui->CurrentWindow->Scroll.y;
6266 }
6267 
GetScrollMaxX()6268 float ImGui::GetScrollMaxX()
6269 {
6270     return GetWindowScrollMaxX(GImGui->CurrentWindow);
6271 }
6272 
GetScrollMaxY()6273 float ImGui::GetScrollMaxY()
6274 {
6275     return GetWindowScrollMaxY(GImGui->CurrentWindow);
6276 }
6277 
SetScrollX(float scroll_x)6278 void ImGui::SetScrollX(float scroll_x)
6279 {
6280     ImGuiWindow* window = GetCurrentWindow();
6281     window->ScrollTarget.x = scroll_x;
6282     window->ScrollTargetCenterRatio.x = 0.0f;
6283 }
6284 
SetScrollY(float scroll_y)6285 void ImGui::SetScrollY(float scroll_y)
6286 {
6287     ImGuiWindow* window = GetCurrentWindow();
6288     window->ScrollTarget.y = scroll_y + window->TitleBarHeight() + window->MenuBarHeight(); // title bar height canceled out when using ScrollTargetRelY
6289     window->ScrollTargetCenterRatio.y = 0.0f;
6290 }
6291 
SetScrollFromPosY(float local_y,float center_y_ratio)6292 void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
6293 {
6294     // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
6295     ImGuiWindow* window = GetCurrentWindow();
6296     IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
6297     window->ScrollTarget.y = (float)(int)(local_y + window->Scroll.y);
6298     window->ScrollTargetCenterRatio.y = center_y_ratio;
6299 }
6300 
6301 // 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)6302 void ImGui::SetScrollHereY(float center_y_ratio)
6303 {
6304     ImGuiWindow* window = GetCurrentWindow();
6305     float target_y = window->DC.CursorPosPrevLine.y - window->Pos.y; // Top of last item, in window space
6306     target_y += (window->DC.PrevLineSize.y * center_y_ratio) + (GImGui->Style.ItemSpacing.y * (center_y_ratio - 0.5f) * 2.0f); // Precisely aim above, in the middle or below the last line.
6307     SetScrollFromPosY(target_y, center_y_ratio);
6308 }
6309 
ActivateItem(ImGuiID id)6310 void ImGui::ActivateItem(ImGuiID id)
6311 {
6312     ImGuiContext& g = *GImGui;
6313     g.NavNextActivateId = id;
6314 }
6315 
SetKeyboardFocusHere(int offset)6316 void ImGui::SetKeyboardFocusHere(int offset)
6317 {
6318     IM_ASSERT(offset >= -1);    // -1 is allowed but not below
6319     ImGuiWindow* window = GetCurrentWindow();
6320     window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset;
6321     window->FocusIdxTabRequestNext = INT_MAX;
6322 }
6323 
SetItemDefaultFocus()6324 void ImGui::SetItemDefaultFocus()
6325 {
6326     ImGuiContext& g = *GImGui;
6327     ImGuiWindow* window = g.CurrentWindow;
6328     if (!window->Appearing)
6329         return;
6330     if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent)
6331     {
6332         g.NavInitRequest = false;
6333         g.NavInitResultId = g.NavWindow->DC.LastItemId;
6334         g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos);
6335         NavUpdateAnyRequestFlag();
6336         if (!IsItemVisible())
6337             SetScrollHereY();
6338     }
6339 }
6340 
SetStateStorage(ImGuiStorage * tree)6341 void ImGui::SetStateStorage(ImGuiStorage* tree)
6342 {
6343     ImGuiWindow* window = GetCurrentWindow();
6344     window->DC.StateStorage = tree ? tree : &window->StateStorage;
6345 }
6346 
GetStateStorage()6347 ImGuiStorage* ImGui::GetStateStorage()
6348 {
6349     ImGuiWindow* window = GetCurrentWindowRead();
6350     return window->DC.StateStorage;
6351 }
6352 
PushID(const char * str_id)6353 void ImGui::PushID(const char* str_id)
6354 {
6355     ImGuiWindow* window = GetCurrentWindowRead();
6356     window->IDStack.push_back(window->GetIDNoKeepAlive(str_id));
6357 }
6358 
PushID(const char * str_id_begin,const char * str_id_end)6359 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
6360 {
6361     ImGuiWindow* window = GetCurrentWindowRead();
6362     window->IDStack.push_back(window->GetIDNoKeepAlive(str_id_begin, str_id_end));
6363 }
6364 
PushID(const void * ptr_id)6365 void ImGui::PushID(const void* ptr_id)
6366 {
6367     ImGuiWindow* window = GetCurrentWindowRead();
6368     window->IDStack.push_back(window->GetIDNoKeepAlive(ptr_id));
6369 }
6370 
PushID(int int_id)6371 void ImGui::PushID(int int_id)
6372 {
6373     const void* ptr_id = (void*)(intptr_t)int_id;
6374     ImGuiWindow* window = GetCurrentWindowRead();
6375     window->IDStack.push_back(window->GetIDNoKeepAlive(ptr_id));
6376 }
6377 
PopID()6378 void ImGui::PopID()
6379 {
6380     ImGuiWindow* window = GetCurrentWindowRead();
6381     window->IDStack.pop_back();
6382 }
6383 
GetID(const char * str_id)6384 ImGuiID ImGui::GetID(const char* str_id)
6385 {
6386     return GImGui->CurrentWindow->GetID(str_id);
6387 }
6388 
GetID(const char * str_id_begin,const char * str_id_end)6389 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
6390 {
6391     return GImGui->CurrentWindow->GetID(str_id_begin, str_id_end);
6392 }
6393 
GetID(const void * ptr_id)6394 ImGuiID ImGui::GetID(const void* ptr_id)
6395 {
6396     return GImGui->CurrentWindow->GetID(ptr_id);
6397 }
6398 
IsRectVisible(const ImVec2 & size)6399 bool ImGui::IsRectVisible(const ImVec2& size)
6400 {
6401     ImGuiWindow* window = GetCurrentWindowRead();
6402     return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
6403 }
6404 
IsRectVisible(const ImVec2 & rect_min,const ImVec2 & rect_max)6405 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
6406 {
6407     ImGuiWindow* window = GetCurrentWindowRead();
6408     return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
6409 }
6410 
6411 // 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()6412 void ImGui::BeginGroup()
6413 {
6414     ImGuiContext& g = *GImGui;
6415     ImGuiWindow* window = GetCurrentWindow();
6416 
6417     window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
6418     ImGuiGroupData& group_data = window->DC.GroupStack.back();
6419     group_data.BackupCursorPos = window->DC.CursorPos;
6420     group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
6421     group_data.BackupIndent = window->DC.Indent;
6422     group_data.BackupGroupOffset = window->DC.GroupOffset;
6423     group_data.BackupCurrentLineSize = window->DC.CurrentLineSize;
6424     group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset;
6425     group_data.BackupLogLinePosY = window->DC.LogLinePosY;
6426     group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
6427     group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
6428     group_data.AdvanceCursor = true;
6429 
6430     window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
6431     window->DC.Indent = window->DC.GroupOffset;
6432     window->DC.CursorMaxPos = window->DC.CursorPos;
6433     window->DC.CurrentLineSize = ImVec2(0.0f, 0.0f);
6434     window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return
6435 }
6436 
EndGroup()6437 void ImGui::EndGroup()
6438 {
6439     ImGuiContext& g = *GImGui;
6440     ImGuiWindow* window = GetCurrentWindow();
6441     IM_ASSERT(!window->DC.GroupStack.empty());    // Mismatched BeginGroup()/EndGroup() calls
6442 
6443     ImGuiGroupData& group_data = window->DC.GroupStack.back();
6444 
6445     ImRect group_bb(group_data.BackupCursorPos, window->DC.CursorMaxPos);
6446     group_bb.Max = ImMax(group_bb.Min, group_bb.Max);
6447 
6448     window->DC.CursorPos = group_data.BackupCursorPos;
6449     window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
6450     window->DC.Indent = group_data.BackupIndent;
6451     window->DC.GroupOffset = group_data.BackupGroupOffset;
6452     window->DC.CurrentLineSize = group_data.BackupCurrentLineSize;
6453     window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset;
6454     window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return
6455 
6456     if (group_data.AdvanceCursor)
6457     {
6458         window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrentLineTextBaseOffset);      // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now.
6459         ItemSize(group_bb.GetSize(), group_data.BackupCurrentLineTextBaseOffset);
6460         ItemAdd(group_bb, 0);
6461     }
6462 
6463     // 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.
6464     // 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.
6465     // (and if you grep for LastItemId you'll notice it is only used in that context.
6466     if ((group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId) // && g.ActiveIdWindow->RootWindow == window->RootWindow)
6467         window->DC.LastItemId = g.ActiveId;
6468     else if (!group_data.BackupActiveIdPreviousFrameIsAlive && g.ActiveIdPreviousFrameIsAlive) // && g.ActiveIdPreviousFrameWindow->RootWindow == window->RootWindow)
6469         window->DC.LastItemId = g.ActiveIdPreviousFrame;
6470     window->DC.LastItemRect = group_bb;
6471 
6472     window->DC.GroupStack.pop_back();
6473 
6474     //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255));   // [Debug]
6475 }
6476 
6477 // Gets back to previous line and continue with horizontal layout
6478 //      pos_x == 0      : follow right after previous item
6479 //      pos_x != 0      : align to specified x position (relative to window/group left)
6480 //      spacing_w < 0   : use default spacing if pos_x == 0, no spacing if pos_x != 0
6481 //      spacing_w >= 0  : enforce spacing amount
SameLine(float pos_x,float spacing_w)6482 void ImGui::SameLine(float pos_x, float spacing_w)
6483 {
6484     ImGuiWindow* window = GetCurrentWindow();
6485     if (window->SkipItems)
6486         return;
6487 
6488     ImGuiContext& g = *GImGui;
6489     if (pos_x != 0.0f)
6490     {
6491         if (spacing_w < 0.0f) spacing_w = 0.0f;
6492         window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + pos_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
6493         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
6494     }
6495     else
6496     {
6497         if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
6498         window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
6499         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
6500     }
6501     window->DC.CurrentLineSize = window->DC.PrevLineSize;
6502     window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
6503 }
6504 
Indent(float indent_w)6505 void ImGui::Indent(float indent_w)
6506 {
6507     ImGuiContext& g = *GImGui;
6508     ImGuiWindow* window = GetCurrentWindow();
6509     window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
6510     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
6511 }
6512 
Unindent(float indent_w)6513 void ImGui::Unindent(float indent_w)
6514 {
6515     ImGuiContext& g = *GImGui;
6516     ImGuiWindow* window = GetCurrentWindow();
6517     window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
6518     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
6519 }
6520 
6521 //-----------------------------------------------------------------------------
6522 // [SECTION] TOOLTIPS
6523 //-----------------------------------------------------------------------------
6524 
BeginTooltip()6525 void ImGui::BeginTooltip()
6526 {
6527     ImGuiContext& g = *GImGui;
6528     if (g.DragDropWithinSourceOrTarget)
6529     {
6530         // 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)
6531         // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
6532         // 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.
6533         //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
6534         ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale);
6535         SetNextWindowPos(tooltip_pos);
6536         SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
6537         //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
6538         BeginTooltipEx(0, true);
6539     }
6540     else
6541     {
6542         BeginTooltipEx(0, false);
6543     }
6544 }
6545 
6546 // Not exposed publicly as BeginTooltip() because bool parameters are evil. Let's see if other needs arise first.
BeginTooltipEx(ImGuiWindowFlags extra_flags,bool override_previous_tooltip)6547 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip)
6548 {
6549     ImGuiContext& g = *GImGui;
6550     char window_name[16];
6551     ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
6552     if (override_previous_tooltip)
6553         if (ImGuiWindow* window = FindWindowByName(window_name))
6554             if (window->Active)
6555             {
6556                 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
6557                 window->Hidden = true;
6558                 window->HiddenFramesRegular = 1;
6559                 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
6560             }
6561     ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
6562     Begin(window_name, NULL, flags | extra_flags);
6563 }
6564 
EndTooltip()6565 void ImGui::EndTooltip()
6566 {
6567     IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip);   // Mismatched BeginTooltip()/EndTooltip() calls
6568     End();
6569 }
6570 
SetTooltipV(const char * fmt,va_list args)6571 void ImGui::SetTooltipV(const char* fmt, va_list args)
6572 {
6573     ImGuiContext& g = *GImGui;
6574     if (g.DragDropWithinSourceOrTarget)
6575         BeginTooltip();
6576     else
6577         BeginTooltipEx(0, true);
6578     TextV(fmt, args);
6579     EndTooltip();
6580 }
6581 
SetTooltip(const char * fmt,...)6582 void ImGui::SetTooltip(const char* fmt, ...)
6583 {
6584     va_list args;
6585     va_start(args, fmt);
6586     SetTooltipV(fmt, args);
6587     va_end(args);
6588 }
6589 
6590 //-----------------------------------------------------------------------------
6591 // [SECTION] POPUPS
6592 //-----------------------------------------------------------------------------
6593 
IsPopupOpen(ImGuiID id)6594 bool ImGui::IsPopupOpen(ImGuiID id)
6595 {
6596     ImGuiContext& g = *GImGui;
6597     return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
6598 }
6599 
IsPopupOpen(const char * str_id)6600 bool ImGui::IsPopupOpen(const char* str_id)
6601 {
6602     ImGuiContext& g = *GImGui;
6603     return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id);
6604 }
6605 
GetFrontMostPopupModal()6606 ImGuiWindow* ImGui::GetFrontMostPopupModal()
6607 {
6608     ImGuiContext& g = *GImGui;
6609     for (int n = g.OpenPopupStack.Size-1; n >= 0; n--)
6610         if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
6611             if (popup->Flags & ImGuiWindowFlags_Modal)
6612                 return popup;
6613     return NULL;
6614 }
6615 
OpenPopup(const char * str_id)6616 void ImGui::OpenPopup(const char* str_id)
6617 {
6618     ImGuiContext& g = *GImGui;
6619     OpenPopupEx(g.CurrentWindow->GetID(str_id));
6620 }
6621 
6622 // Mark popup as open (toggle toward open state).
6623 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
6624 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
6625 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
OpenPopupEx(ImGuiID id)6626 void ImGui::OpenPopupEx(ImGuiID id)
6627 {
6628     ImGuiContext& g = *GImGui;
6629     ImGuiWindow* parent_window = g.CurrentWindow;
6630     int current_stack_size = g.BeginPopupStack.Size;
6631     ImGuiPopupRef popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
6632     popup_ref.PopupId = id;
6633     popup_ref.Window = NULL;
6634     popup_ref.ParentWindow = parent_window;
6635     popup_ref.OpenFrameCount = g.FrameCount;
6636     popup_ref.OpenParentId = parent_window->IDStack.back();
6637     popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
6638     popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
6639 
6640     //IMGUI_DEBUG_LOG("OpenPopupEx(0x%08X)\n", g.FrameCount, id);
6641     if (g.OpenPopupStack.Size < current_stack_size + 1)
6642     {
6643         g.OpenPopupStack.push_back(popup_ref);
6644     }
6645     else
6646     {
6647         // 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
6648         // 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
6649         // 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.
6650         if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
6651         {
6652             g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
6653         }
6654         else
6655         {
6656             // Close child popups if any, then flag popup for open/reopen
6657             g.OpenPopupStack.resize(current_stack_size + 1);
6658             g.OpenPopupStack[current_stack_size] = popup_ref;
6659         }
6660 
6661         // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
6662         // This is equivalent to what ClosePopupToLevel() does.
6663         //if (g.OpenPopupStack[current_stack_size].PopupId == id)
6664         //    FocusWindow(parent_window);
6665     }
6666 }
6667 
OpenPopupOnItemClick(const char * str_id,int mouse_button)6668 bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button)
6669 {
6670     ImGuiWindow* window = GImGui->CurrentWindow;
6671     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
6672     {
6673         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!
6674         IM_ASSERT(id != 0);                                                  // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
6675         OpenPopupEx(id);
6676         return true;
6677     }
6678     return false;
6679 }
6680 
ClosePopupsOverWindow(ImGuiWindow * ref_window)6681 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window)
6682 {
6683     ImGuiContext& g = *GImGui;
6684     if (g.OpenPopupStack.empty())
6685         return;
6686 
6687     // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
6688     // Don't close our own child popup windows.
6689     int popup_count_to_keep = 0;
6690     if (ref_window)
6691     {
6692         // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
6693         for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
6694         {
6695             ImGuiPopupRef& popup = g.OpenPopupStack[popup_count_to_keep];
6696             if (!popup.Window)
6697                 continue;
6698             IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
6699             if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
6700                 continue;
6701 
6702             // Trim the stack if popups are not direct descendant of the reference window (which is often the NavWindow)
6703             bool popup_or_descendent_has_focus = false;
6704             for (int m = popup_count_to_keep; m < g.OpenPopupStack.Size && !popup_or_descendent_has_focus; m++)
6705                 if (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == ref_window->RootWindow)
6706                     popup_or_descendent_has_focus = true;
6707             if (!popup_or_descendent_has_focus)
6708                 break;
6709         }
6710     }
6711     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
6712     {
6713         //IMGUI_DEBUG_LOG("ClosePopupsOverWindow(%s) -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep);
6714         ClosePopupToLevel(popup_count_to_keep, false);
6715     }
6716 }
6717 
ClosePopupToLevel(int remaining,bool apply_focus_to_window_under)6718 void ImGui::ClosePopupToLevel(int remaining, bool apply_focus_to_window_under)
6719 {
6720     IM_ASSERT(remaining >= 0);
6721     ImGuiContext& g = *GImGui;
6722     ImGuiWindow* focus_window = (remaining > 0) ? g.OpenPopupStack[remaining-1].Window : g.OpenPopupStack[0].ParentWindow;
6723     g.OpenPopupStack.resize(remaining);
6724 
6725     // FIXME: This code is faulty and we may want to eventually to replace or remove the 'apply_focus_to_window_under=true' path completely.
6726     // Instead of using g.OpenPopupStack[remaining-1].Window etc. we should find the highest root window that is behind the popups we are closing.
6727     // The current code will set focus to the parent of the popup window which is incorrect.
6728     // It rarely manifested until now because UpdateMouseMovingWindow() would call FocusWindow() again on the clicked window,
6729     // leading to a chain of focusing A (clicked window) then B (parent window of the popup) then A again.
6730     // However if the clicked window has the _NoMove flag set we would be left with B focused.
6731     // For now, we have disabled this path when called from ClosePopupsOverWindow() because the users of ClosePopupsOverWindow() don't need to alter focus anyway,
6732     // but we should inspect and fix this properly.
6733     if (apply_focus_to_window_under)
6734     {
6735         if (g.NavLayer == 0)
6736             focus_window = NavRestoreLastChildNavWindow(focus_window);
6737         FocusWindow(focus_window);
6738     }
6739 }
6740 
6741 // Close the popup we have begin-ed into.
CloseCurrentPopup()6742 void ImGui::CloseCurrentPopup()
6743 {
6744     ImGuiContext& g = *GImGui;
6745     int popup_idx = g.BeginPopupStack.Size - 1;
6746     if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
6747         return;
6748     while (popup_idx > 0 && g.OpenPopupStack[popup_idx].Window && (g.OpenPopupStack[popup_idx].Window->Flags & ImGuiWindowFlags_ChildMenu))
6749         popup_idx--;
6750     ClosePopupToLevel(popup_idx, true);
6751 
6752     // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
6753     // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
6754     // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
6755     if (ImGuiWindow* window = g.NavWindow)
6756         window->DC.NavHideHighlightOneFrame = true;
6757 }
6758 
BeginPopupEx(ImGuiID id,ImGuiWindowFlags extra_flags)6759 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags)
6760 {
6761     ImGuiContext& g = *GImGui;
6762     if (!IsPopupOpen(id))
6763     {
6764         g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
6765         return false;
6766     }
6767 
6768     char name[20];
6769     if (extra_flags & ImGuiWindowFlags_ChildMenu)
6770         ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth
6771     else
6772         ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
6773 
6774     bool is_open = Begin(name, NULL, extra_flags | ImGuiWindowFlags_Popup);
6775     if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
6776         EndPopup();
6777 
6778     return is_open;
6779 }
6780 
BeginPopup(const char * str_id,ImGuiWindowFlags flags)6781 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
6782 {
6783     ImGuiContext& g = *GImGui;
6784     if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
6785     {
6786         g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
6787         return false;
6788     }
6789     flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
6790     return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags);
6791 }
6792 
6793 // If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
6794 // Note that popup visibility status is owned by 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)6795 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
6796 {
6797     ImGuiContext& g = *GImGui;
6798     ImGuiWindow* window = g.CurrentWindow;
6799     const ImGuiID id = window->GetID(name);
6800     if (!IsPopupOpen(id))
6801     {
6802         g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
6803         return false;
6804     }
6805 
6806     // Center modal windows by default
6807     // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
6808     if (g.NextWindowData.PosCond == 0)
6809         SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
6810 
6811     flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings;
6812     const bool is_open = Begin(name, p_open, flags);
6813     if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
6814     {
6815         EndPopup();
6816         if (is_open)
6817             ClosePopupToLevel(g.BeginPopupStack.Size, true);
6818         return false;
6819     }
6820     return is_open;
6821 }
6822 
EndPopup()6823 void ImGui::EndPopup()
6824 {
6825     ImGuiContext& g = *GImGui; (void)g;
6826     IM_ASSERT(g.CurrentWindow->Flags & ImGuiWindowFlags_Popup);  // Mismatched BeginPopup()/EndPopup() calls
6827     IM_ASSERT(g.BeginPopupStack.Size > 0);
6828 
6829     // Make all menus and popups wrap around for now, may need to expose that policy.
6830     NavMoveRequestTryWrapping(g.CurrentWindow, ImGuiNavMoveFlags_LoopY);
6831 
6832     End();
6833 }
6834 
6835 // This is a helper to handle the simplest case of associating one named popup to one given widget.
6836 // You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
6837 // You can pass a NULL str_id to use the identifier of the last item.
BeginPopupContextItem(const char * str_id,int mouse_button)6838 bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button)
6839 {
6840     ImGuiWindow* window = GImGui->CurrentWindow;
6841     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!
6842     IM_ASSERT(id != 0);                                                  // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
6843     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
6844         OpenPopupEx(id);
6845     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
6846 }
6847 
BeginPopupContextWindow(const char * str_id,int mouse_button,bool also_over_items)6848 bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items)
6849 {
6850     if (!str_id)
6851         str_id = "window_context";
6852     ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
6853     if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
6854         if (also_over_items || !IsAnyItemHovered())
6855             OpenPopupEx(id);
6856     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
6857 }
6858 
BeginPopupContextVoid(const char * str_id,int mouse_button)6859 bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
6860 {
6861     if (!str_id)
6862         str_id = "void_context";
6863     ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
6864     if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
6865         OpenPopupEx(id);
6866     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
6867 }
6868 
GetWindowAllowedExtentRect(ImGuiWindow *)6869 ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow*)
6870 {
6871     ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding;
6872     ImRect r_screen = GetViewportRect();
6873     r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
6874     return r_screen;
6875 }
6876 
6877 // 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.)
6878 // r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it.
FindBestWindowPosForPopupEx(const ImVec2 & ref_pos,const ImVec2 & size,ImGuiDir * last_dir,const ImRect & r_outer,const ImRect & r_avoid,ImGuiPopupPositionPolicy policy)6879 ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
6880 {
6881     ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
6882     //GImGui->OverlayDrawList.AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
6883     //GImGui->OverlayDrawList.AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
6884 
6885     // Combo Box policy (we want a connecting edge)
6886     if (policy == ImGuiPopupPositionPolicy_ComboBox)
6887     {
6888         const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
6889         for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
6890         {
6891             const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
6892             if (n != -1 && dir == *last_dir) // Already tried this direction?
6893                 continue;
6894             ImVec2 pos;
6895             if (dir == ImGuiDir_Down)  pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y);          // Below, Toward Right (default)
6896             if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
6897             if (dir == ImGuiDir_Left)  pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
6898             if (dir == ImGuiDir_Up)    pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
6899             if (!r_outer.Contains(ImRect(pos, pos + size)))
6900                 continue;
6901             *last_dir = dir;
6902             return pos;
6903         }
6904     }
6905 
6906     // Default popup policy
6907     const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
6908     for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
6909     {
6910         const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
6911         if (n != -1 && dir == *last_dir) // Already tried this direction?
6912             continue;
6913         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);
6914         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);
6915         if (avail_w < size.x || avail_h < size.y)
6916             continue;
6917         ImVec2 pos;
6918         pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
6919         pos.y = (dir == ImGuiDir_Up)   ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down)  ? r_avoid.Max.y : base_pos_clamped.y;
6920         *last_dir = dir;
6921         return pos;
6922     }
6923 
6924     // Fallback, try to keep within display
6925     *last_dir = ImGuiDir_None;
6926     ImVec2 pos = ref_pos;
6927     pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
6928     pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
6929     return pos;
6930 }
6931 
FindBestWindowPosForPopup(ImGuiWindow * window)6932 ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
6933 {
6934     ImGuiContext& g = *GImGui;
6935 
6936     ImRect r_outer = GetWindowAllowedExtentRect(window);
6937     if (window->Flags & ImGuiWindowFlags_ChildMenu)
6938     {
6939         // Child menus typically request _any_ position within the parent menu item, and then our FindBestWindowPosForPopup() function will move the new menu outside the parent bounds.
6940         // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
6941         IM_ASSERT(g.CurrentWindow == window);
6942         ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2];
6943         float horizontal_overlap = g.Style.ItemSpacing.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).
6944         ImRect r_avoid;
6945         if (parent_window->DC.MenuBarAppending)
6946             r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight());
6947         else
6948             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);
6949         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
6950     }
6951     if (window->Flags & ImGuiWindowFlags_Popup)
6952     {
6953         ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
6954         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
6955     }
6956     if (window->Flags & ImGuiWindowFlags_Tooltip)
6957     {
6958         // Position tooltip (always follows mouse)
6959         float sc = g.Style.MouseCursorScale;
6960         ImVec2 ref_pos = NavCalcPreferredRefPos();
6961         ImRect r_avoid;
6962         if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
6963             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
6964         else
6965             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.
6966         ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
6967         if (window->AutoPosLastDirection == ImGuiDir_None)
6968             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.
6969         return pos;
6970     }
6971     IM_ASSERT(0);
6972     return window->Pos;
6973 }
6974 
6975 //-----------------------------------------------------------------------------
6976 // [SECTION] VIEWPORTS, PLATFORM WINDOWS
6977 //-----------------------------------------------------------------------------
6978 
6979 // (this section is filled in the 'viewport' and 'docking' branches)
6980 
6981 //-----------------------------------------------------------------------------
6982 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
6983 //-----------------------------------------------------------------------------
6984 
ImGetDirQuadrantFromDelta(float dx,float dy)6985 ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
6986 {
6987     if (ImFabs(dx) > ImFabs(dy))
6988         return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
6989     return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
6990 }
6991 
NavScoreItemDistInterval(float a0,float a1,float b0,float b1)6992 static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
6993 {
6994     if (a1 < b0)
6995         return a1 - b0;
6996     if (b1 < a0)
6997         return a0 - b1;
6998     return 0.0f;
6999 }
7000 
NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir,ImRect & r,const ImRect & clip_rect)7001 static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect)
7002 {
7003     if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
7004     {
7005         r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y);
7006         r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y);
7007     }
7008     else
7009     {
7010         r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x);
7011         r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x);
7012     }
7013 }
7014 
7015 // Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057
NavScoreItem(ImGuiNavMoveResult * result,ImRect cand)7016 static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
7017 {
7018     ImGuiContext& g = *GImGui;
7019     ImGuiWindow* window = g.CurrentWindow;
7020     if (g.NavLayer != window->DC.NavLayerCurrent)
7021         return false;
7022 
7023     const ImRect& curr = g.NavScoringRectScreen; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width)
7024     g.NavScoringCount++;
7025 
7026     // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
7027     if (window->ParentWindow == g.NavWindow)
7028     {
7029         IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened);
7030         if (!window->ClipRect.Contains(cand))
7031             return false;
7032         cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
7033     }
7034 
7035     // 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)
7036     // For example, this ensure that items in one column are not reached when moving vertically from items in another column.
7037     NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect);
7038 
7039     // Compute distance between boxes
7040     // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
7041     float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
7042     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
7043     if (dby != 0.0f && dbx != 0.0f)
7044        dbx = (dbx/1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
7045     float dist_box = ImFabs(dbx) + ImFabs(dby);
7046 
7047     // 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)
7048     float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
7049     float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
7050     float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
7051 
7052     // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
7053     ImGuiDir quadrant;
7054     float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
7055     if (dbx != 0.0f || dby != 0.0f)
7056     {
7057         // For non-overlapping boxes, use distance between boxes
7058         dax = dbx;
7059         day = dby;
7060         dist_axial = dist_box;
7061         quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
7062     }
7063     else if (dcx != 0.0f || dcy != 0.0f)
7064     {
7065         // For overlapping boxes with different centers, use distance between centers
7066         dax = dcx;
7067         day = dcy;
7068         dist_axial = dist_center;
7069         quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
7070     }
7071     else
7072     {
7073         // 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)
7074         quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
7075     }
7076 
7077 #if IMGUI_DEBUG_NAV_SCORING
7078     char buf[128];
7079     if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max))
7080     {
7081         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]);
7082         ImDrawList* draw_list = ImGui::GetOverlayDrawList(window);
7083         draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
7084         draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
7085         draw_list->AddRectFilled(cand.Max-ImVec2(4,4), cand.Max+ImGui::CalcTextSize(buf)+ImVec2(4,4), IM_COL32(40,0,0,150));
7086         draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
7087     }
7088     else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
7089     {
7090         if (ImGui::IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; }
7091         if (quadrant == g.NavMoveDir)
7092         {
7093             ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
7094             ImDrawList* draw_list = ImGui::GetOverlayDrawList(window);
7095             draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
7096             draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
7097         }
7098     }
7099  #endif
7100 
7101     // Is it in the quadrant we're interesting in moving to?
7102     bool new_best = false;
7103     if (quadrant == g.NavMoveDir)
7104     {
7105         // Does it beat the current best candidate?
7106         if (dist_box < result->DistBox)
7107         {
7108             result->DistBox = dist_box;
7109             result->DistCenter = dist_center;
7110             return true;
7111         }
7112         if (dist_box == result->DistBox)
7113         {
7114             // Try using distance between center points to break ties
7115             if (dist_center < result->DistCenter)
7116             {
7117                 result->DistCenter = dist_center;
7118                 new_best = true;
7119             }
7120             else if (dist_center == result->DistCenter)
7121             {
7122                 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
7123                 // (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),
7124                 // 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.
7125                 if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
7126                     new_best = true;
7127             }
7128         }
7129     }
7130 
7131     // 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
7132     // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
7133     // 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.
7134     // 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.
7135     // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
7136     if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial)  // Check axial match
7137         if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
7138             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))
7139             {
7140                 result->DistAxial = dist_axial;
7141                 new_best = true;
7142             }
7143 
7144     return new_best;
7145 }
7146 
7147 // 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)7148 static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
7149 {
7150     ImGuiContext& g = *GImGui;
7151     //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.
7152     //    return;
7153 
7154     const ImGuiItemFlags item_flags = window->DC.ItemFlags;
7155     const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
7156 
7157     // Process Init Request
7158     if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
7159     {
7160         // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
7161         if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0)
7162         {
7163             g.NavInitResultId = id;
7164             g.NavInitResultRectRel = nav_bb_rel;
7165         }
7166         if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus))
7167         {
7168             g.NavInitRequest = false; // Found a match, clear request
7169             NavUpdateAnyRequestFlag();
7170         }
7171     }
7172 
7173     // Process Move Request (scoring for navigation)
7174     // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
7175     if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & ImGuiItemFlags_NoNav))
7176     {
7177         ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
7178 #if IMGUI_DEBUG_NAV_SCORING
7179         // [DEBUG] Score all items in NavWindow at all times
7180         if (!g.NavMoveRequest)
7181             g.NavMoveDir = g.NavMoveDirLast;
7182         bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest;
7183 #else
7184         bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb);
7185 #endif
7186         if (new_best)
7187         {
7188             result->ID = id;
7189             result->Window = window;
7190             result->RectRel = nav_bb_rel;
7191         }
7192 
7193         const float VISIBLE_RATIO = 0.70f;
7194         if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
7195             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)
7196                 if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb))
7197                 {
7198                     result = &g.NavMoveResultLocalVisibleSet;
7199                     result->ID = id;
7200                     result->Window = window;
7201                     result->RectRel = nav_bb_rel;
7202                 }
7203     }
7204 
7205     // Update window-relative bounding box of navigated item
7206     if (g.NavId == id)
7207     {
7208         g.NavWindow = window;                                           // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
7209         g.NavLayer = window->DC.NavLayerCurrent;
7210         g.NavIdIsAlive = true;
7211         g.NavIdTabCounter = window->FocusIdxTabCounter;
7212         window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel;    // Store item bounding box (relative to window position)
7213     }
7214 }
7215 
NavMoveRequestButNoResultYet()7216 bool ImGui::NavMoveRequestButNoResultYet()
7217 {
7218     ImGuiContext& g = *GImGui;
7219     return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
7220 }
7221 
NavMoveRequestCancel()7222 void ImGui::NavMoveRequestCancel()
7223 {
7224     ImGuiContext& g = *GImGui;
7225     g.NavMoveRequest = false;
7226     NavUpdateAnyRequestFlag();
7227 }
7228 
NavMoveRequestForward(ImGuiDir move_dir,ImGuiDir clip_dir,const ImRect & bb_rel,ImGuiNavMoveFlags move_flags)7229 void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags)
7230 {
7231     ImGuiContext& g = *GImGui;
7232     IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None);
7233     ImGui::NavMoveRequestCancel();
7234     g.NavMoveDir = move_dir;
7235     g.NavMoveClipDir = clip_dir;
7236     g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
7237     g.NavMoveRequestFlags = move_flags;
7238     g.NavWindow->NavRectRel[g.NavLayer] = bb_rel;
7239 }
7240 
NavMoveRequestTryWrapping(ImGuiWindow * window,ImGuiNavMoveFlags move_flags)7241 void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
7242 {
7243     ImGuiContext& g = *GImGui;
7244     if (g.NavWindow != window || !NavMoveRequestButNoResultYet() || g.NavMoveRequestForward != ImGuiNavForward_None || g.NavLayer != 0)
7245         return;
7246     IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
7247     ImRect bb_rel = window->NavRectRel[0];
7248 
7249     ImGuiDir clip_dir = g.NavMoveDir;
7250     if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
7251     {
7252         bb_rel.Min.x = bb_rel.Max.x = ImMax(window->SizeFull.x, window->SizeContents.x) - window->Scroll.x;
7253         if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(-bb_rel.GetHeight()); clip_dir = ImGuiDir_Up; }
7254         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7255     }
7256     if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
7257     {
7258         bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
7259         if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(+bb_rel.GetHeight()); clip_dir = ImGuiDir_Down; }
7260         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7261     }
7262     if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
7263     {
7264         bb_rel.Min.y = bb_rel.Max.y = ImMax(window->SizeFull.y, window->SizeContents.y) - window->Scroll.y;
7265         if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(-bb_rel.GetWidth()); clip_dir = ImGuiDir_Left; }
7266         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7267     }
7268     if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
7269     {
7270         bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
7271         if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(+bb_rel.GetWidth()); clip_dir = ImGuiDir_Right; }
7272         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7273     }
7274 }
7275 
NavSaveLastChildNavWindow(ImGuiWindow * nav_window)7276 static void ImGui::NavSaveLastChildNavWindow(ImGuiWindow* nav_window)
7277 {
7278     ImGuiWindow* parent_window = nav_window;
7279     while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
7280         parent_window = parent_window->ParentWindow;
7281     if (parent_window && parent_window != nav_window)
7282         parent_window->NavLastChildNavWindow = nav_window;
7283 }
7284 
7285 // Call when we are expected to land on Layer 0 after FocusWindow()
NavRestoreLastChildNavWindow(ImGuiWindow * window)7286 static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
7287 {
7288     return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window;
7289 }
7290 
NavRestoreLayer(ImGuiNavLayer layer)7291 static void NavRestoreLayer(ImGuiNavLayer layer)
7292 {
7293     ImGuiContext& g = *GImGui;
7294     g.NavLayer = layer;
7295     if (layer == 0)
7296         g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow);
7297     if (layer == 0 && g.NavWindow->NavLastIds[0] != 0)
7298         ImGui::SetNavIDWithRectRel(g.NavWindow->NavLastIds[0], layer, g.NavWindow->NavRectRel[0]);
7299     else
7300         ImGui::NavInitWindow(g.NavWindow, true);
7301 }
7302 
NavUpdateAnyRequestFlag()7303 static inline void ImGui::NavUpdateAnyRequestFlag()
7304 {
7305     ImGuiContext& g = *GImGui;
7306     g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
7307     if (g.NavAnyRequest)
7308         IM_ASSERT(g.NavWindow != NULL);
7309 }
7310 
7311 // This needs to be called before we submit any widget (aka in or before Begin)
NavInitWindow(ImGuiWindow * window,bool force_reinit)7312 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
7313 {
7314     ImGuiContext& g = *GImGui;
7315     IM_ASSERT(window == g.NavWindow);
7316     bool init_for_nav = false;
7317     if (!(window->Flags & ImGuiWindowFlags_NoNavInputs))
7318         if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
7319             init_for_nav = true;
7320     if (init_for_nav)
7321     {
7322         SetNavID(0, g.NavLayer);
7323         g.NavInitRequest = true;
7324         g.NavInitRequestFromMove = false;
7325         g.NavInitResultId = 0;
7326         g.NavInitResultRectRel = ImRect();
7327         NavUpdateAnyRequestFlag();
7328     }
7329     else
7330     {
7331         g.NavId = window->NavLastIds[0];
7332     }
7333 }
7334 
NavCalcPreferredRefPos()7335 static ImVec2 ImGui::NavCalcPreferredRefPos()
7336 {
7337     ImGuiContext& g = *GImGui;
7338     if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow)
7339     {
7340         // Mouse (we need a fallback in case the mouse becomes invalid after being used)
7341         if (IsMousePosValid(&g.IO.MousePos))
7342             return g.IO.MousePos;
7343         return g.LastValidMousePos;
7344     }
7345     else
7346     {
7347         // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item.
7348         const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
7349         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()));
7350         ImRect visible_rect = GetViewportRect();
7351         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.
7352     }
7353 }
7354 
GetNavInputAmount(ImGuiNavInput n,ImGuiInputReadMode mode)7355 float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode)
7356 {
7357     ImGuiContext& g = *GImGui;
7358     if (mode == ImGuiInputReadMode_Down)
7359         return g.IO.NavInputs[n];                         // Instant, read analog input (0.0f..1.0f, as provided by user)
7360 
7361     const float t = g.IO.NavInputsDownDuration[n];
7362     if (t < 0.0f && mode == ImGuiInputReadMode_Released)  // Return 1.0f when just released, no repeat, ignore analog input.
7363         return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
7364     if (t < 0.0f)
7365         return 0.0f;
7366     if (mode == ImGuiInputReadMode_Pressed)               // Return 1.0f when just pressed, no repeat, ignore analog input.
7367         return (t == 0.0f) ? 1.0f : 0.0f;
7368     if (mode == ImGuiInputReadMode_Repeat)
7369         return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.80f);
7370     if (mode == ImGuiInputReadMode_RepeatSlow)
7371         return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 1.00f, g.IO.KeyRepeatRate * 2.00f);
7372     if (mode == ImGuiInputReadMode_RepeatFast)
7373         return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.30f);
7374     return 0.0f;
7375 }
7376 
GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources,ImGuiInputReadMode mode,float slow_factor,float fast_factor)7377 ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor)
7378 {
7379     ImVec2 delta(0.0f, 0.0f);
7380     if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
7381         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode)   - GetNavInputAmount(ImGuiNavInput_KeyLeft_,   mode), GetNavInputAmount(ImGuiNavInput_KeyDown_,   mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_,   mode));
7382     if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
7383         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode)   - GetNavInputAmount(ImGuiNavInput_DpadLeft,   mode), GetNavInputAmount(ImGuiNavInput_DpadDown,   mode) - GetNavInputAmount(ImGuiNavInput_DpadUp,   mode));
7384     if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
7385         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode));
7386     if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow))
7387         delta *= slow_factor;
7388     if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast))
7389         delta *= fast_factor;
7390     return delta;
7391 }
7392 
7393 // Scroll to keep newly navigated item fully into view
7394 // NB: We modify rect_rel by the amount we scrolled for, so it is immediately updated.
NavScrollToBringItemIntoView(ImGuiWindow * window,const ImRect & item_rect)7395 static void NavScrollToBringItemIntoView(ImGuiWindow* window, const ImRect& item_rect)
7396 {
7397     ImRect window_rect(window->InnerMainRect.Min - ImVec2(1, 1), window->InnerMainRect.Max + ImVec2(1, 1));
7398     //GetOverlayDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
7399     if (window_rect.Contains(item_rect))
7400         return;
7401 
7402     ImGuiContext& g = *GImGui;
7403     if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x)
7404     {
7405         window->ScrollTarget.x = item_rect.Min.x - window->Pos.x + window->Scroll.x - g.Style.ItemSpacing.x;
7406         window->ScrollTargetCenterRatio.x = 0.0f;
7407     }
7408     else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x)
7409     {
7410         window->ScrollTarget.x = item_rect.Max.x - window->Pos.x + window->Scroll.x + g.Style.ItemSpacing.x;
7411         window->ScrollTargetCenterRatio.x = 1.0f;
7412     }
7413     if (item_rect.Min.y < window_rect.Min.y)
7414     {
7415         window->ScrollTarget.y = item_rect.Min.y - window->Pos.y + window->Scroll.y - g.Style.ItemSpacing.y;
7416         window->ScrollTargetCenterRatio.y = 0.0f;
7417     }
7418     else if (item_rect.Max.y >= window_rect.Max.y)
7419     {
7420         window->ScrollTarget.y = item_rect.Max.y - window->Pos.y + window->Scroll.y + g.Style.ItemSpacing.y;
7421         window->ScrollTargetCenterRatio.y = 1.0f;
7422     }
7423 }
7424 
NavUpdate()7425 static void ImGui::NavUpdate()
7426 {
7427     ImGuiContext& g = *GImGui;
7428     g.IO.WantSetMousePos = false;
7429 #if 0
7430     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);
7431 #endif
7432 
7433     // Set input source as Gamepad when buttons are pressed before we map Keyboard (some features differs when used with Gamepad vs Keyboard)
7434     bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
7435     bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
7436     if (nav_gamepad_active)
7437         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)
7438             g.NavInputSource = ImGuiInputSource_NavGamepad;
7439 
7440     // Update Keyboard->Nav inputs mapping
7441     if (nav_keyboard_active)
7442     {
7443         #define NAV_MAP_KEY(_KEY, _NAV_INPUT) if (IsKeyDown(g.IO.KeyMap[_KEY])) { g.IO.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; }
7444         NAV_MAP_KEY(ImGuiKey_Space,     ImGuiNavInput_Activate );
7445         NAV_MAP_KEY(ImGuiKey_Enter,     ImGuiNavInput_Input    );
7446         NAV_MAP_KEY(ImGuiKey_Escape,    ImGuiNavInput_Cancel   );
7447         NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ );
7448         NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
7449         NAV_MAP_KEY(ImGuiKey_UpArrow,   ImGuiNavInput_KeyUp_   );
7450         NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
7451         if (g.IO.KeyCtrl)   g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
7452         if (g.IO.KeyShift)  g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
7453         if (g.IO.KeyAlt)    g.IO.NavInputs[ImGuiNavInput_KeyMenu_]  = 1.0f;
7454         #undef NAV_MAP_KEY
7455     }
7456     memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration));
7457     for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++)
7458         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;
7459 
7460     // Process navigation init request (select first/default focus)
7461     if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove))
7462     {
7463         // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
7464         IM_ASSERT(g.NavWindow);
7465         if (g.NavInitRequestFromMove)
7466             SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, g.NavInitResultRectRel);
7467         else
7468             SetNavID(g.NavInitResultId, g.NavLayer);
7469         g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel;
7470     }
7471     g.NavInitRequest = false;
7472     g.NavInitRequestFromMove = false;
7473     g.NavInitResultId = 0;
7474     g.NavJustMovedToId = 0;
7475 
7476     // Process navigation move request
7477     if (g.NavMoveRequest)
7478         NavUpdateMoveResult();
7479 
7480     // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
7481     if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
7482     {
7483         IM_ASSERT(g.NavMoveRequest);
7484         if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
7485             g.NavDisableHighlight = false;
7486         g.NavMoveRequestForward = ImGuiNavForward_None;
7487     }
7488 
7489     // Apply application mouse position movement, after we had a chance to process move request result.
7490     if (g.NavMousePosDirty && g.NavIdIsAlive)
7491     {
7492         // Set mouse position given our knowledge of the navigated item position from last frame
7493         if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
7494         {
7495             if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
7496             {
7497                 g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos();
7498                 g.IO.WantSetMousePos = true;
7499             }
7500         }
7501         g.NavMousePosDirty = false;
7502     }
7503     g.NavIdIsAlive = false;
7504     g.NavJustTabbedId = 0;
7505     IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
7506 
7507     // 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
7508     if (g.NavWindow)
7509         NavSaveLastChildNavWindow(g.NavWindow);
7510     if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0)
7511         g.NavWindow->NavLastChildNavWindow = NULL;
7512 
7513     // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
7514     NavUpdateWindowing();
7515 
7516     // Set output flags for user application
7517     g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
7518     g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest;
7519 
7520     // Process NavCancel input (to close a popup, get back to parent, clear focus)
7521     if (IsNavInputPressed(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
7522     {
7523         if (g.ActiveId != 0)
7524         {
7525             ClearActiveID();
7526         }
7527         else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
7528         {
7529             // Exit child window
7530             ImGuiWindow* child_window = g.NavWindow;
7531             ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
7532             IM_ASSERT(child_window->ChildId != 0);
7533             FocusWindow(parent_window);
7534             SetNavID(child_window->ChildId, 0);
7535             g.NavIdIsAlive = false;
7536             if (g.NavDisableMouseHover)
7537                 g.NavMousePosDirty = true;
7538         }
7539         else if (g.OpenPopupStack.Size > 0)
7540         {
7541             // Close open popup/menu
7542             if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
7543                 ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
7544         }
7545         else if (g.NavLayer != 0)
7546         {
7547             // Leave the "menu" layer
7548             NavRestoreLayer(ImGuiNavLayer_Main);
7549         }
7550         else
7551         {
7552             // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
7553             if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
7554                 g.NavWindow->NavLastIds[0] = 0;
7555             g.NavId = 0;
7556         }
7557     }
7558 
7559     // Process manual activation request
7560     g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
7561     if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
7562     {
7563         bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
7564         bool activate_pressed = activate_down && IsNavInputPressed(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
7565         if (g.ActiveId == 0 && activate_pressed)
7566             g.NavActivateId = g.NavId;
7567         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
7568             g.NavActivateDownId = g.NavId;
7569         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
7570             g.NavActivatePressedId = g.NavId;
7571         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputPressed(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
7572             g.NavInputId = g.NavId;
7573     }
7574     if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
7575         g.NavDisableHighlight = true;
7576     if (g.NavActivateId != 0)
7577         IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
7578     g.NavMoveRequest = false;
7579 
7580     // Process programmatic activation request
7581     if (g.NavNextActivateId != 0)
7582         g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
7583     g.NavNextActivateId = 0;
7584 
7585     // Initiate directional inputs request
7586     const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags;
7587     if (g.NavMoveRequestForward == ImGuiNavForward_None)
7588     {
7589         g.NavMoveDir = ImGuiDir_None;
7590         g.NavMoveRequestFlags = ImGuiNavMoveFlags_None;
7591         if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
7592         {
7593             if ((allowed_dir_flags & (1<<ImGuiDir_Left))  && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadLeft, ImGuiNavInput_KeyLeft_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Left;
7594             if ((allowed_dir_flags & (1<<ImGuiDir_Right)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadRight,ImGuiNavInput_KeyRight_,ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Right;
7595             if ((allowed_dir_flags & (1<<ImGuiDir_Up))    && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadUp,   ImGuiNavInput_KeyUp_,   ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Up;
7596             if ((allowed_dir_flags & (1<<ImGuiDir_Down))  && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadDown, ImGuiNavInput_KeyDown_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Down;
7597         }
7598         g.NavMoveClipDir = g.NavMoveDir;
7599     }
7600     else
7601     {
7602         // 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)
7603         // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function)
7604         IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
7605         IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
7606         g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
7607     }
7608 
7609     // Update PageUp/PageDown scroll
7610     float nav_scoring_rect_offset_y = 0.0f;
7611     if (nav_keyboard_active)
7612         nav_scoring_rect_offset_y = NavUpdatePageUpPageDown(allowed_dir_flags);
7613 
7614     // 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
7615     if (g.NavMoveDir != ImGuiDir_None)
7616     {
7617         g.NavMoveRequest = true;
7618         g.NavMoveDirLast = g.NavMoveDir;
7619     }
7620     if (g.NavMoveRequest && g.NavId == 0)
7621     {
7622         g.NavInitRequest = g.NavInitRequestFromMove = true;
7623         g.NavInitResultId = 0;
7624         g.NavDisableHighlight = false;
7625     }
7626     NavUpdateAnyRequestFlag();
7627 
7628     // Scrolling
7629     if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
7630     {
7631         // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
7632         ImGuiWindow* window = g.NavWindow;
7633         const float scroll_speed = ImFloor(window->CalcFontSize() * 100 * g.IO.DeltaTime + 0.5f); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.
7634         if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
7635         {
7636             if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
7637                 SetWindowScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
7638             if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
7639                 SetWindowScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
7640         }
7641 
7642         // *Normal* Manual scroll with NavScrollXXX keys
7643         // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
7644         ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f);
7645         if (scroll_dir.x != 0.0f && window->ScrollbarX)
7646         {
7647             SetWindowScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
7648             g.NavMoveFromClampedRefRect = true;
7649         }
7650         if (scroll_dir.y != 0.0f)
7651         {
7652             SetWindowScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
7653             g.NavMoveFromClampedRefRect = true;
7654         }
7655     }
7656 
7657     // Reset search results
7658     g.NavMoveResultLocal.Clear();
7659     g.NavMoveResultLocalVisibleSet.Clear();
7660     g.NavMoveResultOther.Clear();
7661 
7662     // 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
7663     if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0)
7664     {
7665         ImGuiWindow* window = g.NavWindow;
7666         ImRect window_rect_rel(window->InnerMainRect.Min - window->Pos - ImVec2(1,1), window->InnerMainRect.Max - window->Pos + ImVec2(1,1));
7667         if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
7668         {
7669             float pad = window->CalcFontSize() * 0.5f;
7670             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
7671             window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel);
7672             g.NavId = 0;
7673         }
7674         g.NavMoveFromClampedRefRect = false;
7675     }
7676 
7677     // 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)
7678     ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0);
7679     g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect();
7680     g.NavScoringRectScreen.TranslateY(nav_scoring_rect_offset_y);
7681     g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x);
7682     g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x;
7683     IM_ASSERT(!g.NavScoringRectScreen.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem().
7684     //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
7685     g.NavScoringCount = 0;
7686 #if IMGUI_DEBUG_NAV_RECTS
7687     if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) GetOverlayDrawList(g.NavWindow)->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG]
7688     if (g.NavWindow) { 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); GetOverlayDrawList(g.NavWindow)->AddCircleFilled(p, 3.0f, col); GetOverlayDrawList(g.NavWindow)->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); }
7689 #endif
7690 }
7691 
7692 // Apply result from previous frame navigation directional move request
NavUpdateMoveResult()7693 static void ImGui::NavUpdateMoveResult()
7694 {
7695     ImGuiContext& g = *GImGui;
7696     if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
7697     {
7698         // 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)
7699         if (g.NavId != 0)
7700         {
7701             g.NavDisableHighlight = false;
7702             g.NavDisableMouseHover = true;
7703         }
7704         return;
7705     }
7706 
7707     // Select which result to use
7708     ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
7709 
7710     // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
7711     if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
7712         if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId)
7713             result = &g.NavMoveResultLocalVisibleSet;
7714 
7715     // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
7716     if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
7717         if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
7718             result = &g.NavMoveResultOther;
7719     IM_ASSERT(g.NavWindow && result->Window);
7720 
7721     // Scroll to keep newly navigated item fully into view.
7722     if (g.NavLayer == 0)
7723     {
7724         ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos);
7725         NavScrollToBringItemIntoView(result->Window, rect_abs);
7726 
7727         // Estimate upcoming scroll so we can offset our result position so mouse position can be applied immediately after in NavUpdate()
7728         ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(result->Window, false);
7729         ImVec2 delta_scroll = result->Window->Scroll - next_scroll;
7730         result->RectRel.Translate(delta_scroll);
7731 
7732         // Also scroll parent window to keep us into view if necessary (we could/should technically recurse back the whole the parent hierarchy).
7733         if (result->Window->Flags & ImGuiWindowFlags_ChildWindow)
7734             NavScrollToBringItemIntoView(result->Window->ParentWindow, ImRect(rect_abs.Min + delta_scroll, rect_abs.Max + delta_scroll));
7735     }
7736 
7737     ClearActiveID();
7738     g.NavWindow = result->Window;
7739     SetNavIDWithRectRel(result->ID, g.NavLayer, result->RectRel);
7740     g.NavJustMovedToId = result->ID;
7741     g.NavMoveFromClampedRefRect = false;
7742 }
7743 
NavUpdatePageUpPageDown(int allowed_dir_flags)7744 static float ImGui::NavUpdatePageUpPageDown(int allowed_dir_flags)
7745 {
7746     ImGuiContext& g = *GImGui;
7747     if (g.NavMoveDir == ImGuiDir_None && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget && g.NavLayer == 0)
7748     {
7749         ImGuiWindow* window = g.NavWindow;
7750         bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && (allowed_dir_flags & (1 << ImGuiDir_Up));
7751         bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && (allowed_dir_flags & (1 << ImGuiDir_Down));
7752         if ((page_up_held && !page_down_held) || (page_down_held && !page_up_held))
7753         {
7754             if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll)
7755             {
7756                 // Fallback manual-scroll when window has no navigable item
7757                 if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
7758                     SetWindowScrollY(window, window->Scroll.y - window->InnerClipRect.GetHeight());
7759                 else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
7760                     SetWindowScrollY(window, window->Scroll.y + window->InnerClipRect.GetHeight());
7761             }
7762             else
7763             {
7764                 const ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
7765                 const float page_offset_y = ImMax(0.0f, window->InnerClipRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
7766                 float nav_scoring_rect_offset_y = 0.0f;
7767                 if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
7768                 {
7769                     nav_scoring_rect_offset_y = -page_offset_y;
7770                     g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset, we intentionally request the opposite direction (so we can always land on the last item)
7771                     g.NavMoveClipDir = ImGuiDir_Up;
7772                     g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
7773                 }
7774                 else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
7775                 {
7776                     nav_scoring_rect_offset_y = +page_offset_y;
7777                     g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset, we intentionally request the opposite direction (so we can always land on the last item)
7778                     g.NavMoveClipDir = ImGuiDir_Down;
7779                     g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
7780                 }
7781                 return nav_scoring_rect_offset_y;
7782             }
7783         }
7784     }
7785     return 0.0f;
7786 }
7787 
FindWindowFocusIndex(ImGuiWindow * window)7788 static int FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N)
7789 {
7790     ImGuiContext& g = *GImGui;
7791     for (int i = g.WindowsFocusOrder.Size-1; i >= 0; i--)
7792         if (g.WindowsFocusOrder[i] == window)
7793             return i;
7794     return -1;
7795 }
7796 
FindWindowNavFocusable(int i_start,int i_stop,int dir)7797 static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
7798 {
7799     ImGuiContext& g = *GImGui;
7800     for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
7801         if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
7802             return g.WindowsFocusOrder[i];
7803     return NULL;
7804 }
7805 
NavUpdateWindowingHighlightWindow(int focus_change_dir)7806 static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
7807 {
7808     ImGuiContext& g = *GImGui;
7809     IM_ASSERT(g.NavWindowingTarget);
7810     if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
7811         return;
7812 
7813     const int i_current = FindWindowFocusIndex(g.NavWindowingTarget);
7814     ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
7815     if (!window_target)
7816         window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
7817     if (window_target) // Don't reset windowing target if there's a single window in the list
7818         g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
7819     g.NavWindowingToggleLayer = false;
7820 }
7821 
7822 // Window management mode (hold to: change focus/move/resize, tap to: toggle menu layer)
NavUpdateWindowing()7823 static void ImGui::NavUpdateWindowing()
7824 {
7825     ImGuiContext& g = *GImGui;
7826     ImGuiWindow* apply_focus_window = NULL;
7827     bool apply_toggle_layer = false;
7828 
7829     ImGuiWindow* modal_window = GetFrontMostPopupModal();
7830     if (modal_window != NULL)
7831     {
7832         g.NavWindowingTarget = NULL;
7833         return;
7834     }
7835 
7836     // Fade out
7837     if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
7838     {
7839         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f);
7840         if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
7841             g.NavWindowingTargetAnim = NULL;
7842     }
7843 
7844     // Start CTRL-TAB or Square+L/R window selection
7845     bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
7846     bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
7847     if (start_windowing_with_gamepad || start_windowing_with_keyboard)
7848         if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
7849         {
7850             g.NavWindowingTarget = g.NavWindowingTargetAnim = window;
7851             g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
7852             g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true;
7853             g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad;
7854         }
7855 
7856     // Gamepad update
7857     g.NavWindowingTimer += g.IO.DeltaTime;
7858     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad)
7859     {
7860         // 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
7861         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
7862 
7863         // Select window to focus
7864         const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow);
7865         if (focus_change_dir != 0)
7866         {
7867             NavUpdateWindowingHighlightWindow(focus_change_dir);
7868             g.NavWindowingHighlightAlpha = 1.0f;
7869         }
7870 
7871         // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered front-most)
7872         if (!IsNavInputDown(ImGuiNavInput_Menu))
7873         {
7874             g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
7875             if (g.NavWindowingToggleLayer && g.NavWindow)
7876                 apply_toggle_layer = true;
7877             else if (!g.NavWindowingToggleLayer)
7878                 apply_focus_window = g.NavWindowingTarget;
7879             g.NavWindowingTarget = NULL;
7880         }
7881     }
7882 
7883     // Keyboard: Focus
7884     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard)
7885     {
7886         // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
7887         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
7888         if (IsKeyPressedMap(ImGuiKey_Tab, true))
7889             NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1);
7890         if (!g.IO.KeyCtrl)
7891             apply_focus_window = g.NavWindowingTarget;
7892     }
7893 
7894     // Keyboard: Press and Release ALT to toggle menu layer
7895     // 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
7896     if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released))
7897         if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev))
7898             apply_toggle_layer = true;
7899 
7900     // Move window
7901     if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
7902     {
7903         ImVec2 move_delta;
7904         if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift)
7905             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
7906         if (g.NavInputSource == ImGuiInputSource_NavGamepad)
7907             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down);
7908         if (move_delta.x != 0.0f || move_delta.y != 0.0f)
7909         {
7910             const float NAV_MOVE_SPEED = 800.0f;
7911             const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't code variable framerate very well
7912             g.NavWindowingTarget->RootWindow->Pos += move_delta * move_speed;
7913             g.NavDisableMouseHover = true;
7914             MarkIniSettingsDirty(g.NavWindowingTarget);
7915         }
7916     }
7917 
7918     // Apply final focus
7919     if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
7920     {
7921         g.NavDisableHighlight = false;
7922         g.NavDisableMouseHover = true;
7923         apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
7924         ClosePopupsOverWindow(apply_focus_window);
7925         FocusWindow(apply_focus_window);
7926         if (apply_focus_window->NavLastIds[0] == 0)
7927             NavInitWindow(apply_focus_window, false);
7928 
7929         // If the window only has a menu layer, select it directly
7930         if (apply_focus_window->DC.NavLayerActiveMask == (1 << ImGuiNavLayer_Menu))
7931             g.NavLayer = ImGuiNavLayer_Menu;
7932     }
7933     if (apply_focus_window)
7934         g.NavWindowingTarget = NULL;
7935 
7936     // Apply menu/layer toggle
7937     if (apply_toggle_layer && g.NavWindow)
7938     {
7939         // Move to parent menu if necessary
7940         ImGuiWindow* new_nav_window = g.NavWindow;
7941         while ((new_nav_window->DC.NavLayerActiveMask & (1 << 1)) == 0
7942             && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
7943             && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
7944             new_nav_window = new_nav_window->ParentWindow;
7945         if (new_nav_window != g.NavWindow)
7946         {
7947             ImGuiWindow* old_nav_window = g.NavWindow;
7948             FocusWindow(new_nav_window);
7949             new_nav_window->NavLastChildNavWindow = old_nav_window;
7950         }
7951         g.NavDisableHighlight = false;
7952         g.NavDisableMouseHover = true;
7953         NavRestoreLayer((g.NavWindow->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main);
7954     }
7955 }
7956 
7957 // Window has already passed the IsWindowNavFocusable()
GetFallbackWindowNameForWindowingList(ImGuiWindow * window)7958 static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
7959 {
7960     if (window->Flags & ImGuiWindowFlags_Popup)
7961         return "(Popup)";
7962     if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
7963         return "(Main menu bar)";
7964     return "(Untitled)";
7965 }
7966 
7967 // Overlay displayed when using CTRL+TAB. Called by EndFrame().
NavUpdateWindowingList()7968 void ImGui::NavUpdateWindowingList()
7969 {
7970     ImGuiContext& g = *GImGui;
7971     IM_ASSERT(g.NavWindowingTarget != NULL);
7972 
7973     if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
7974         return;
7975 
7976     if (g.NavWindowingList == NULL)
7977         g.NavWindowingList = FindWindowByName("###NavWindowingList");
7978     SetNextWindowSizeConstraints(ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
7979     SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
7980     PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
7981     Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
7982     for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
7983     {
7984         ImGuiWindow* window = g.WindowsFocusOrder[n];
7985         if (!IsWindowNavFocusable(window))
7986             continue;
7987         const char* label = window->Name;
7988         if (label == FindRenderedTextEnd(label))
7989             label = GetFallbackWindowNameForWindowingList(window);
7990         Selectable(label, g.NavWindowingTarget == window);
7991     }
7992     End();
7993     PopStyleVar();
7994 }
7995 
7996 //-----------------------------------------------------------------------------
7997 // [SECTION] COLUMNS
7998 // In the current version, Columns are very weak. Needs to be replaced with a more full-featured system.
7999 //-----------------------------------------------------------------------------
8000 
NextColumn()8001 void ImGui::NextColumn()
8002 {
8003     ImGuiWindow* window = GetCurrentWindow();
8004     if (window->SkipItems || window->DC.ColumnsSet == NULL)
8005         return;
8006 
8007     ImGuiContext& g = *GImGui;
8008     PopItemWidth();
8009     PopClipRect();
8010 
8011     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8012     columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y);
8013     if (++columns->Current < columns->Count)
8014     {
8015         // Columns 1+ cancel out IndentX
8016         window->DC.ColumnsOffset.x = GetColumnOffset(columns->Current) - window->DC.Indent.x + g.Style.ItemSpacing.x;
8017         window->DrawList->ChannelsSetCurrent(columns->Current);
8018     }
8019     else
8020     {
8021         window->DC.ColumnsOffset.x = 0.0f;
8022         window->DrawList->ChannelsSetCurrent(0);
8023         columns->Current = 0;
8024         columns->LineMinY = columns->LineMaxY;
8025     }
8026     window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
8027     window->DC.CursorPos.y = columns->LineMinY;
8028     window->DC.CurrentLineSize = ImVec2(0.0f, 0.0f);
8029     window->DC.CurrentLineTextBaseOffset = 0.0f;
8030 
8031     PushColumnClipRect();
8032     PushItemWidth(GetColumnWidth() * 0.65f);  // FIXME: Move on columns setup
8033 }
8034 
GetColumnIndex()8035 int ImGui::GetColumnIndex()
8036 {
8037     ImGuiWindow* window = GetCurrentWindowRead();
8038     return window->DC.ColumnsSet ? window->DC.ColumnsSet->Current : 0;
8039 }
8040 
GetColumnsCount()8041 int ImGui::GetColumnsCount()
8042 {
8043     ImGuiWindow* window = GetCurrentWindowRead();
8044     return window->DC.ColumnsSet ? window->DC.ColumnsSet->Count : 1;
8045 }
8046 
OffsetNormToPixels(const ImGuiColumnsSet * columns,float offset_norm)8047 static float OffsetNormToPixels(const ImGuiColumnsSet* columns, float offset_norm)
8048 {
8049     return offset_norm * (columns->MaxX - columns->MinX);
8050 }
8051 
PixelsToOffsetNorm(const ImGuiColumnsSet * columns,float offset)8052 static float PixelsToOffsetNorm(const ImGuiColumnsSet* columns, float offset)
8053 {
8054     return offset / (columns->MaxX - columns->MinX);
8055 }
8056 
GetColumnsRectHalfWidth()8057 static inline float GetColumnsRectHalfWidth() { return 4.0f; }
8058 
GetDraggedColumnOffset(ImGuiColumnsSet * columns,int column_index)8059 static float GetDraggedColumnOffset(ImGuiColumnsSet* columns, int column_index)
8060 {
8061     // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing
8062     // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning.
8063     ImGuiContext& g = *GImGui;
8064     ImGuiWindow* window = g.CurrentWindow;
8065     IM_ASSERT(column_index > 0); // We are not supposed to drag column 0.
8066     IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index));
8067 
8068     float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + GetColumnsRectHalfWidth() - window->Pos.x;
8069     x = ImMax(x, ImGui::GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing);
8070     if ((columns->Flags & ImGuiColumnsFlags_NoPreserveWidths))
8071         x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing);
8072 
8073     return x;
8074 }
8075 
GetColumnOffset(int column_index)8076 float ImGui::GetColumnOffset(int column_index)
8077 {
8078     ImGuiWindow* window = GetCurrentWindowRead();
8079     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8080     IM_ASSERT(columns != NULL);
8081 
8082     if (column_index < 0)
8083         column_index = columns->Current;
8084     IM_ASSERT(column_index < columns->Columns.Size);
8085 
8086     const float t = columns->Columns[column_index].OffsetNorm;
8087     const float x_offset = ImLerp(columns->MinX, columns->MaxX, t);
8088     return x_offset;
8089 }
8090 
GetColumnWidthEx(ImGuiColumnsSet * columns,int column_index,bool before_resize=false)8091 static float GetColumnWidthEx(ImGuiColumnsSet* columns, int column_index, bool before_resize = false)
8092 {
8093     if (column_index < 0)
8094         column_index = columns->Current;
8095 
8096     float offset_norm;
8097     if (before_resize)
8098         offset_norm = columns->Columns[column_index + 1].OffsetNormBeforeResize - columns->Columns[column_index].OffsetNormBeforeResize;
8099     else
8100         offset_norm = columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm;
8101     return OffsetNormToPixels(columns, offset_norm);
8102 }
8103 
GetColumnWidth(int column_index)8104 float ImGui::GetColumnWidth(int column_index)
8105 {
8106     ImGuiWindow* window = GetCurrentWindowRead();
8107     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8108     IM_ASSERT(columns != NULL);
8109 
8110     if (column_index < 0)
8111         column_index = columns->Current;
8112     return OffsetNormToPixels(columns, columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm);
8113 }
8114 
SetColumnOffset(int column_index,float offset)8115 void ImGui::SetColumnOffset(int column_index, float offset)
8116 {
8117     ImGuiContext& g = *GImGui;
8118     ImGuiWindow* window = g.CurrentWindow;
8119     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8120     IM_ASSERT(columns != NULL);
8121 
8122     if (column_index < 0)
8123         column_index = columns->Current;
8124     IM_ASSERT(column_index < columns->Columns.Size);
8125 
8126     const bool preserve_width = !(columns->Flags & ImGuiColumnsFlags_NoPreserveWidths) && (column_index < columns->Count-1);
8127     const float width = preserve_width ? GetColumnWidthEx(columns, column_index, columns->IsBeingResized) : 0.0f;
8128 
8129     if (!(columns->Flags & ImGuiColumnsFlags_NoForceWithinWindow))
8130         offset = ImMin(offset, columns->MaxX - g.Style.ColumnsMinSpacing * (columns->Count - column_index));
8131     columns->Columns[column_index].OffsetNorm = PixelsToOffsetNorm(columns, offset - columns->MinX);
8132 
8133     if (preserve_width)
8134         SetColumnOffset(column_index + 1, offset + ImMax(g.Style.ColumnsMinSpacing, width));
8135 }
8136 
SetColumnWidth(int column_index,float width)8137 void ImGui::SetColumnWidth(int column_index, float width)
8138 {
8139     ImGuiWindow* window = GetCurrentWindowRead();
8140     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8141     IM_ASSERT(columns != NULL);
8142 
8143     if (column_index < 0)
8144         column_index = columns->Current;
8145     SetColumnOffset(column_index + 1, GetColumnOffset(column_index) + width);
8146 }
8147 
PushColumnClipRect(int column_index)8148 void ImGui::PushColumnClipRect(int column_index)
8149 {
8150     ImGuiWindow* window = GetCurrentWindowRead();
8151     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8152     if (column_index < 0)
8153         column_index = columns->Current;
8154 
8155     PushClipRect(columns->Columns[column_index].ClipRect.Min, columns->Columns[column_index].ClipRect.Max, false);
8156 }
8157 
FindOrAddColumnsSet(ImGuiWindow * window,ImGuiID id)8158 static ImGuiColumnsSet* FindOrAddColumnsSet(ImGuiWindow* window, ImGuiID id)
8159 {
8160     for (int n = 0; n < window->ColumnsStorage.Size; n++)
8161         if (window->ColumnsStorage[n].ID == id)
8162             return &window->ColumnsStorage[n];
8163 
8164     window->ColumnsStorage.push_back(ImGuiColumnsSet());
8165     ImGuiColumnsSet* columns = &window->ColumnsStorage.back();
8166     columns->ID = id;
8167     return columns;
8168 }
8169 
BeginColumns(const char * str_id,int columns_count,ImGuiColumnsFlags flags)8170 void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiColumnsFlags flags)
8171 {
8172     ImGuiContext& g = *GImGui;
8173     ImGuiWindow* window = GetCurrentWindow();
8174 
8175     IM_ASSERT(columns_count > 1);
8176     IM_ASSERT(window->DC.ColumnsSet == NULL); // Nested columns are currently not supported
8177 
8178     // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget.
8179     // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer.
8180     PushID(0x11223347 + (str_id ? 0 : columns_count));
8181     ImGuiID id = window->GetID(str_id ? str_id : "columns");
8182     PopID();
8183 
8184     // Acquire storage for the columns set
8185     ImGuiColumnsSet* columns = FindOrAddColumnsSet(window, id);
8186     IM_ASSERT(columns->ID == id);
8187     columns->Current = 0;
8188     columns->Count = columns_count;
8189     columns->Flags = flags;
8190     window->DC.ColumnsSet = columns;
8191 
8192     // Set state for first column
8193     const float content_region_width = (window->SizeContentsExplicit.x != 0.0f) ? (window->SizeContentsExplicit.x) : (window->InnerClipRect.Max.x - window->Pos.x);
8194     columns->MinX = window->DC.Indent.x - g.Style.ItemSpacing.x; // Lock our horizontal range
8195     columns->MaxX = ImMax(content_region_width - window->Scroll.x, columns->MinX + 1.0f);
8196     columns->StartPosY = window->DC.CursorPos.y;
8197     columns->StartMaxPosX = window->DC.CursorMaxPos.x;
8198     columns->LineMinY = columns->LineMaxY = window->DC.CursorPos.y;
8199     window->DC.ColumnsOffset.x = 0.0f;
8200     window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
8201 
8202     // Clear data if columns count changed
8203     if (columns->Columns.Size != 0 && columns->Columns.Size != columns_count + 1)
8204         columns->Columns.resize(0);
8205 
8206     // Initialize defaults
8207     columns->IsFirstFrame = (columns->Columns.Size == 0);
8208     if (columns->Columns.Size == 0)
8209     {
8210         columns->Columns.reserve(columns_count + 1);
8211         for (int n = 0; n < columns_count + 1; n++)
8212         {
8213             ImGuiColumnData column;
8214             column.OffsetNorm = n / (float)columns_count;
8215             columns->Columns.push_back(column);
8216         }
8217     }
8218 
8219     for (int n = 0; n < columns_count; n++)
8220     {
8221         // Compute clipping rectangle
8222         ImGuiColumnData* column = &columns->Columns[n];
8223         float clip_x1 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n) - 1.0f);
8224         float clip_x2 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n + 1) - 1.0f);
8225         column->ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX);
8226         column->ClipRect.ClipWith(window->ClipRect);
8227     }
8228 
8229     window->DrawList->ChannelsSplit(columns->Count);
8230     PushColumnClipRect();
8231     PushItemWidth(GetColumnWidth() * 0.65f);
8232 }
8233 
EndColumns()8234 void ImGui::EndColumns()
8235 {
8236     ImGuiContext& g = *GImGui;
8237     ImGuiWindow* window = GetCurrentWindow();
8238     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8239     IM_ASSERT(columns != NULL);
8240 
8241     PopItemWidth();
8242     PopClipRect();
8243     window->DrawList->ChannelsMerge();
8244 
8245     columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y);
8246     window->DC.CursorPos.y = columns->LineMaxY;
8247     if (!(columns->Flags & ImGuiColumnsFlags_GrowParentContentsSize))
8248         window->DC.CursorMaxPos.x = columns->StartMaxPosX;  // Restore cursor max pos, as columns don't grow parent
8249 
8250     // Draw columns borders and handle resize
8251     bool is_being_resized = false;
8252     if (!(columns->Flags & ImGuiColumnsFlags_NoBorder) && !window->SkipItems)
8253     {
8254         const float y1 = columns->StartPosY;
8255         const float y2 = window->DC.CursorPos.y;
8256         int dragging_column = -1;
8257         for (int n = 1; n < columns->Count; n++)
8258         {
8259             float x = window->Pos.x + GetColumnOffset(n);
8260             const ImGuiID column_id = columns->ID + ImGuiID(n);
8261             const float column_hw = GetColumnsRectHalfWidth(); // Half-width for interaction
8262             const ImRect column_rect(ImVec2(x - column_hw, y1), ImVec2(x + column_hw, y2));
8263             KeepAliveID(column_id);
8264             if (IsClippedEx(column_rect, column_id, false))
8265                 continue;
8266 
8267             bool hovered = false, held = false;
8268             if (!(columns->Flags & ImGuiColumnsFlags_NoResize))
8269             {
8270                 ButtonBehavior(column_rect, column_id, &hovered, &held);
8271                 if (hovered || held)
8272                     g.MouseCursor = ImGuiMouseCursor_ResizeEW;
8273                 if (held && !(columns->Columns[n].Flags & ImGuiColumnsFlags_NoResize))
8274                     dragging_column = n;
8275             }
8276 
8277             // Draw column (we clip the Y boundaries CPU side because very long triangles are mishandled by some GPU drivers.)
8278             const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
8279             const float xi = (float)(int)x;
8280             window->DrawList->AddLine(ImVec2(xi, ImMax(y1 + 1.0f, window->ClipRect.Min.y)), ImVec2(xi, ImMin(y2, window->ClipRect.Max.y)), col);
8281         }
8282 
8283         // Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame.
8284         if (dragging_column != -1)
8285         {
8286             if (!columns->IsBeingResized)
8287                 for (int n = 0; n < columns->Count + 1; n++)
8288                     columns->Columns[n].OffsetNormBeforeResize = columns->Columns[n].OffsetNorm;
8289             columns->IsBeingResized = is_being_resized = true;
8290             float x = GetDraggedColumnOffset(columns, dragging_column);
8291             SetColumnOffset(dragging_column, x);
8292         }
8293     }
8294     columns->IsBeingResized = is_being_resized;
8295 
8296     window->DC.ColumnsSet = NULL;
8297     window->DC.ColumnsOffset.x = 0.0f;
8298     window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
8299 }
8300 
8301 // [2018-03: This is currently the only public API, while we are working on making BeginColumns/EndColumns user-facing]
Columns(int columns_count,const char * id,bool border)8302 void ImGui::Columns(int columns_count, const char* id, bool border)
8303 {
8304     ImGuiWindow* window = GetCurrentWindow();
8305     IM_ASSERT(columns_count >= 1);
8306 
8307     ImGuiColumnsFlags flags = (border ? 0 : ImGuiColumnsFlags_NoBorder);
8308     //flags |= ImGuiColumnsFlags_NoPreserveWidths; // NB: Legacy behavior
8309     if (window->DC.ColumnsSet != NULL && window->DC.ColumnsSet->Count == columns_count && window->DC.ColumnsSet->Flags == flags)
8310         return;
8311 
8312     if (window->DC.ColumnsSet != NULL)
8313         EndColumns();
8314 
8315     if (columns_count != 1)
8316         BeginColumns(id, columns_count, flags);
8317 }
8318 
8319 //-----------------------------------------------------------------------------
8320 // [SECTION] DRAG AND DROP
8321 //-----------------------------------------------------------------------------
8322 
ClearDragDrop()8323 void ImGui::ClearDragDrop()
8324 {
8325     ImGuiContext& g = *GImGui;
8326     g.DragDropActive = false;
8327     g.DragDropPayload.Clear();
8328     g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
8329     g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
8330     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
8331     g.DragDropAcceptFrameCount = -1;
8332 
8333     g.DragDropPayloadBufHeap.clear();
8334     memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
8335 }
8336 
8337 // Call when current ID is active.
8338 // 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)8339 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
8340 {
8341     ImGuiContext& g = *GImGui;
8342     ImGuiWindow* window = g.CurrentWindow;
8343 
8344     bool source_drag_active = false;
8345     ImGuiID source_id = 0;
8346     ImGuiID source_parent_id = 0;
8347     int mouse_button = 0;
8348     if (!(flags & ImGuiDragDropFlags_SourceExtern))
8349     {
8350         source_id = window->DC.LastItemId;
8351         if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case
8352             return false;
8353         if (g.IO.MouseDown[mouse_button] == false)
8354             return false;
8355 
8356         if (source_id == 0)
8357         {
8358             // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
8359             // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
8360             if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
8361             {
8362                 IM_ASSERT(0);
8363                 return false;
8364             }
8365 
8366             // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
8367             // We build a throwaway ID based on current ID stack + relative AABB of items in window.
8368             // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
8369             // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
8370             bool is_hovered = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) != 0;
8371             if (!is_hovered && (g.ActiveId == 0 || g.ActiveIdWindow != window))
8372                 return false;
8373             source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect);
8374             if (is_hovered)
8375                 SetHoveredID(source_id);
8376             if (is_hovered && g.IO.MouseClicked[mouse_button])
8377             {
8378                 SetActiveID(source_id, window);
8379                 FocusWindow(window);
8380             }
8381             if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
8382                 g.ActiveIdAllowOverlap = is_hovered;
8383         }
8384         else
8385         {
8386             g.ActiveIdAllowOverlap = false;
8387         }
8388         if (g.ActiveId != source_id)
8389             return false;
8390         source_parent_id = window->IDStack.back();
8391         source_drag_active = IsMouseDragging(mouse_button);
8392     }
8393     else
8394     {
8395         window = NULL;
8396         source_id = ImHash("#SourceExtern", 0);
8397         source_drag_active = true;
8398     }
8399 
8400     if (source_drag_active)
8401     {
8402         if (!g.DragDropActive)
8403         {
8404             IM_ASSERT(source_id != 0);
8405             ClearDragDrop();
8406             ImGuiPayload& payload = g.DragDropPayload;
8407             payload.SourceId = source_id;
8408             payload.SourceParentId = source_parent_id;
8409             g.DragDropActive = true;
8410             g.DragDropSourceFlags = flags;
8411             g.DragDropMouseButton = mouse_button;
8412         }
8413         g.DragDropSourceFrameCount = g.FrameCount;
8414         g.DragDropWithinSourceOrTarget = true;
8415 
8416         if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
8417         {
8418             // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
8419             // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
8420             BeginTooltip();
8421             if (g.DragDropActive && g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
8422             {
8423                 ImGuiWindow* tooltip_window = g.CurrentWindow;
8424                 tooltip_window->SkipItems = true;
8425                 tooltip_window->HiddenFramesRegular = 1;
8426             }
8427         }
8428 
8429         if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
8430             window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
8431 
8432         return true;
8433     }
8434     return false;
8435 }
8436 
EndDragDropSource()8437 void ImGui::EndDragDropSource()
8438 {
8439     ImGuiContext& g = *GImGui;
8440     IM_ASSERT(g.DragDropActive);
8441     IM_ASSERT(g.DragDropWithinSourceOrTarget && "Not after a BeginDragDropSource()?");
8442 
8443     if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
8444         EndTooltip();
8445 
8446     // Discard the drag if have not called SetDragDropPayload()
8447     if (g.DragDropPayload.DataFrameCount == -1)
8448         ClearDragDrop();
8449     g.DragDropWithinSourceOrTarget = false;
8450 }
8451 
8452 // 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)8453 bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
8454 {
8455     ImGuiContext& g = *GImGui;
8456     ImGuiPayload& payload = g.DragDropPayload;
8457     if (cond == 0)
8458         cond = ImGuiCond_Always;
8459 
8460     IM_ASSERT(type != NULL);
8461     IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
8462     IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
8463     IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
8464     IM_ASSERT(payload.SourceId != 0);                               // Not called between BeginDragDropSource() and EndDragDropSource()
8465 
8466     if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
8467     {
8468         // Copy payload
8469         ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
8470         g.DragDropPayloadBufHeap.resize(0);
8471         if (data_size > sizeof(g.DragDropPayloadBufLocal))
8472         {
8473             // Store in heap
8474             g.DragDropPayloadBufHeap.resize((int)data_size);
8475             payload.Data = g.DragDropPayloadBufHeap.Data;
8476             memcpy(payload.Data, data, data_size);
8477         }
8478         else if (data_size > 0)
8479         {
8480             // Store locally
8481             memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
8482             payload.Data = g.DragDropPayloadBufLocal;
8483             memcpy(payload.Data, data, data_size);
8484         }
8485         else
8486         {
8487             payload.Data = NULL;
8488         }
8489         payload.DataSize = (int)data_size;
8490     }
8491     payload.DataFrameCount = g.FrameCount;
8492 
8493     return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
8494 }
8495 
BeginDragDropTargetCustom(const ImRect & bb,ImGuiID id)8496 bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
8497 {
8498     ImGuiContext& g = *GImGui;
8499     if (!g.DragDropActive)
8500         return false;
8501 
8502     ImGuiWindow* window = g.CurrentWindow;
8503     if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
8504         return false;
8505     IM_ASSERT(id != 0);
8506     if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
8507         return false;
8508     if (window->SkipItems)
8509         return false;
8510 
8511     IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
8512     g.DragDropTargetRect = bb;
8513     g.DragDropTargetId = id;
8514     g.DragDropWithinSourceOrTarget = true;
8515     return true;
8516 }
8517 
8518 // We don't use BeginDragDropTargetCustom() and duplicate its code because:
8519 // 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
8520 // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
8521 // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
BeginDragDropTarget()8522 bool ImGui::BeginDragDropTarget()
8523 {
8524     ImGuiContext& g = *GImGui;
8525     if (!g.DragDropActive)
8526         return false;
8527 
8528     ImGuiWindow* window = g.CurrentWindow;
8529     if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
8530         return false;
8531     if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
8532         return false;
8533 
8534     const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect;
8535     ImGuiID id = window->DC.LastItemId;
8536     if (id == 0)
8537         id = window->GetIDFromRectangle(display_rect);
8538     if (g.DragDropPayload.SourceId == id)
8539         return false;
8540 
8541     IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
8542     g.DragDropTargetRect = display_rect;
8543     g.DragDropTargetId = id;
8544     g.DragDropWithinSourceOrTarget = true;
8545     return true;
8546 }
8547 
IsDragDropPayloadBeingAccepted()8548 bool ImGui::IsDragDropPayloadBeingAccepted()
8549 {
8550     ImGuiContext& g = *GImGui;
8551     return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
8552 }
8553 
AcceptDragDropPayload(const char * type,ImGuiDragDropFlags flags)8554 const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
8555 {
8556     ImGuiContext& g = *GImGui;
8557     ImGuiWindow* window = g.CurrentWindow;
8558     ImGuiPayload& payload = g.DragDropPayload;
8559     IM_ASSERT(g.DragDropActive);                        // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
8560     IM_ASSERT(payload.DataFrameCount != -1);            // Forgot to call EndDragDropTarget() ?
8561     if (type != NULL && !payload.IsDataType(type))
8562         return NULL;
8563 
8564     // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
8565     // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
8566     const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
8567     ImRect r = g.DragDropTargetRect;
8568     float r_surface = r.GetWidth() * r.GetHeight();
8569     if (r_surface < g.DragDropAcceptIdCurrRectSurface)
8570     {
8571         g.DragDropAcceptFlags = flags;
8572         g.DragDropAcceptIdCurr = g.DragDropTargetId;
8573         g.DragDropAcceptIdCurrRectSurface = r_surface;
8574     }
8575 
8576     // Render default drop visuals
8577     payload.Preview = was_accepted_previously;
8578     flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
8579     if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
8580     {
8581         // FIXME-DRAG: Settle on a proper default visuals for drop target.
8582         r.Expand(3.5f);
8583         bool push_clip_rect = !window->ClipRect.Contains(r);
8584         if (push_clip_rect) window->DrawList->PushClipRect(r.Min-ImVec2(1,1), r.Max+ImVec2(1,1));
8585         window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f);
8586         if (push_clip_rect) window->DrawList->PopClipRect();
8587     }
8588 
8589     g.DragDropAcceptFrameCount = g.FrameCount;
8590     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()
8591     if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
8592         return NULL;
8593 
8594     return &payload;
8595 }
8596 
GetDragDropPayload()8597 const ImGuiPayload* ImGui::GetDragDropPayload()
8598 {
8599     ImGuiContext& g = *GImGui;
8600     return g.DragDropActive ? &g.DragDropPayload : NULL;
8601 }
8602 
8603 // We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
EndDragDropTarget()8604 void ImGui::EndDragDropTarget()
8605 {
8606     ImGuiContext& g = *GImGui;
8607     IM_ASSERT(g.DragDropActive);
8608     IM_ASSERT(g.DragDropWithinSourceOrTarget);
8609     g.DragDropWithinSourceOrTarget = false;
8610 }
8611 
8612 //-----------------------------------------------------------------------------
8613 // [SECTION] DOCKING
8614 //-----------------------------------------------------------------------------
8615 
8616 // (this section is filled in the 'docking' branch)
8617 
8618 //-----------------------------------------------------------------------------
8619 // [SECTION] LOGGING/CAPTURING
8620 //-----------------------------------------------------------------------------
8621 
8622 // Pass text data straight to log (without being displayed)
LogText(const char * fmt,...)8623 void ImGui::LogText(const char* fmt, ...)
8624 {
8625     ImGuiContext& g = *GImGui;
8626     if (!g.LogEnabled)
8627         return;
8628 
8629     va_list args;
8630     va_start(args, fmt);
8631     if (g.LogFile)
8632         vfprintf(g.LogFile, fmt, args);
8633     else
8634         g.LogClipboard.appendfv(fmt, args);
8635     va_end(args);
8636 }
8637 
8638 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
8639 // We split text into individual lines to add current tree level padding
LogRenderedText(const ImVec2 * ref_pos,const char * text,const char * text_end)8640 void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
8641 {
8642     ImGuiContext& g = *GImGui;
8643     ImGuiWindow* window = g.CurrentWindow;
8644 
8645     if (!text_end)
8646         text_end = FindRenderedTextEnd(text, text_end);
8647 
8648     const bool log_new_line = ref_pos && (ref_pos->y > window->DC.LogLinePosY + 1);
8649     if (ref_pos)
8650         window->DC.LogLinePosY = ref_pos->y;
8651 
8652     const char* text_remaining = text;
8653     if (g.LogStartDepth > window->DC.TreeDepth)  // Re-adjust padding if we have popped out of our starting depth
8654         g.LogStartDepth = window->DC.TreeDepth;
8655     const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth);
8656     for (;;)
8657     {
8658         // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
8659         const char* line_start = text_remaining;
8660         const char* line_end = ImStreolRange(line_start, text_end);
8661         const bool is_first_line = (line_start == text);
8662         const bool is_last_line = (line_end == text_end);
8663         if (!is_last_line || (line_start != line_end))
8664         {
8665             const int char_count = (int)(line_end - line_start);
8666             if (log_new_line || !is_first_line)
8667                 LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, line_start);
8668             else
8669                 LogText(" %.*s", char_count, line_start);
8670         }
8671 
8672         if (is_last_line)
8673             break;
8674         text_remaining = line_end + 1;
8675     }
8676 }
8677 
8678 // Start logging ImGui output to TTY
LogToTTY(int max_depth)8679 void ImGui::LogToTTY(int max_depth)
8680 {
8681     ImGuiContext& g = *GImGui;
8682     if (g.LogEnabled)
8683         return;
8684     ImGuiWindow* window = g.CurrentWindow;
8685 
8686     IM_ASSERT(g.LogFile == NULL);
8687     g.LogFile = stdout;
8688     g.LogEnabled = true;
8689     g.LogStartDepth = window->DC.TreeDepth;
8690     if (max_depth >= 0)
8691         g.LogAutoExpandMaxDepth = max_depth;
8692 }
8693 
8694 // Start logging ImGui output to given file
LogToFile(int max_depth,const char * filename)8695 void ImGui::LogToFile(int max_depth, const char* filename)
8696 {
8697     ImGuiContext& g = *GImGui;
8698     if (g.LogEnabled)
8699         return;
8700     ImGuiWindow* window = g.CurrentWindow;
8701 
8702     if (!filename)
8703     {
8704         filename = g.IO.LogFilename;
8705         if (!filename)
8706             return;
8707     }
8708 
8709     IM_ASSERT(g.LogFile == NULL);
8710     g.LogFile = ImFileOpen(filename, "ab");
8711     if (!g.LogFile)
8712     {
8713         IM_ASSERT(g.LogFile != NULL); // Consider this an error
8714         return;
8715     }
8716     g.LogEnabled = true;
8717     g.LogStartDepth = window->DC.TreeDepth;
8718     if (max_depth >= 0)
8719         g.LogAutoExpandMaxDepth = max_depth;
8720 }
8721 
8722 // Start logging ImGui output to clipboard
LogToClipboard(int max_depth)8723 void ImGui::LogToClipboard(int max_depth)
8724 {
8725     ImGuiContext& g = *GImGui;
8726     if (g.LogEnabled)
8727         return;
8728     ImGuiWindow* window = g.CurrentWindow;
8729 
8730     IM_ASSERT(g.LogFile == NULL);
8731     g.LogFile = NULL;
8732     g.LogEnabled = true;
8733     g.LogStartDepth = window->DC.TreeDepth;
8734     if (max_depth >= 0)
8735         g.LogAutoExpandMaxDepth = max_depth;
8736 }
8737 
LogFinish()8738 void ImGui::LogFinish()
8739 {
8740     ImGuiContext& g = *GImGui;
8741     if (!g.LogEnabled)
8742         return;
8743 
8744     LogText(IM_NEWLINE);
8745     if (g.LogFile != NULL)
8746     {
8747         if (g.LogFile == stdout)
8748             fflush(g.LogFile);
8749         else
8750             fclose(g.LogFile);
8751         g.LogFile = NULL;
8752     }
8753     if (g.LogClipboard.size() > 1)
8754     {
8755         SetClipboardText(g.LogClipboard.begin());
8756         g.LogClipboard.clear();
8757     }
8758     g.LogEnabled = false;
8759 }
8760 
8761 // Helper to display logging buttons
LogButtons()8762 void ImGui::LogButtons()
8763 {
8764     ImGuiContext& g = *GImGui;
8765 
8766     PushID("LogButtons");
8767     const bool log_to_tty = Button("Log To TTY"); SameLine();
8768     const bool log_to_file = Button("Log To File"); SameLine();
8769     const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
8770     PushItemWidth(80.0f);
8771     PushAllowKeyboardFocus(false);
8772     SliderInt("Depth", &g.LogAutoExpandMaxDepth, 0, 9, NULL);
8773     PopAllowKeyboardFocus();
8774     PopItemWidth();
8775     PopID();
8776 
8777     // Start logging at the end of the function so that the buttons don't appear in the log
8778     if (log_to_tty)
8779         LogToTTY(g.LogAutoExpandMaxDepth);
8780     if (log_to_file)
8781         LogToFile(g.LogAutoExpandMaxDepth, g.IO.LogFilename);
8782     if (log_to_clipboard)
8783         LogToClipboard(g.LogAutoExpandMaxDepth);
8784 }
8785 
8786 //-----------------------------------------------------------------------------
8787 // [SECTION] SETTINGS
8788 //-----------------------------------------------------------------------------
8789 
MarkIniSettingsDirty()8790 void ImGui::MarkIniSettingsDirty()
8791 {
8792     ImGuiContext& g = *GImGui;
8793     if (g.SettingsDirtyTimer <= 0.0f)
8794         g.SettingsDirtyTimer = g.IO.IniSavingRate;
8795 }
8796 
MarkIniSettingsDirty(ImGuiWindow * window)8797 void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
8798 {
8799     ImGuiContext& g = *GImGui;
8800     if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
8801         if (g.SettingsDirtyTimer <= 0.0f)
8802             g.SettingsDirtyTimer = g.IO.IniSavingRate;
8803 }
8804 
CreateNewWindowSettings(const char * name)8805 ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
8806 {
8807     ImGuiContext& g = *GImGui;
8808     g.SettingsWindows.push_back(ImGuiWindowSettings());
8809     ImGuiWindowSettings* settings = &g.SettingsWindows.back();
8810     settings->Name = ImStrdup(name);
8811     settings->ID = ImHash(name, 0);
8812     return settings;
8813 }
8814 
FindWindowSettings(ImGuiID id)8815 ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
8816 {
8817     ImGuiContext& g = *GImGui;
8818     for (int i = 0; i != g.SettingsWindows.Size; i++)
8819         if (g.SettingsWindows[i].ID == id)
8820             return &g.SettingsWindows[i];
8821     return NULL;
8822 }
8823 
LoadIniSettingsFromDisk(const char * ini_filename)8824 void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
8825 {
8826     size_t file_data_size = 0;
8827     char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
8828     if (!file_data)
8829         return;
8830     LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
8831     ImGui::MemFree(file_data);
8832 }
8833 
FindSettingsHandler(const char * type_name)8834 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
8835 {
8836     ImGuiContext& g = *GImGui;
8837     const ImGuiID type_hash = ImHash(type_name, 0, 0);
8838     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
8839         if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
8840             return &g.SettingsHandlers[handler_n];
8841     return NULL;
8842 }
8843 
8844 // Zero-tolerance, no error reporting, cheap .ini parsing
LoadIniSettingsFromMemory(const char * ini_data,size_t ini_size)8845 void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
8846 {
8847     ImGuiContext& g = *GImGui;
8848     IM_ASSERT(g.Initialized);
8849     IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
8850 
8851     // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
8852     // 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..
8853     if (ini_size == 0)
8854         ini_size = strlen(ini_data);
8855     char* buf = (char*)ImGui::MemAlloc(ini_size + 1);
8856     char* buf_end = buf + ini_size;
8857     memcpy(buf, ini_data, ini_size);
8858     buf[ini_size] = 0;
8859 
8860     void* entry_data = NULL;
8861     ImGuiSettingsHandler* entry_handler = NULL;
8862 
8863     char* line_end = NULL;
8864     for (char* line = buf; line < buf_end; line = line_end + 1)
8865     {
8866         // Skip new lines markers, then find end of the line
8867         while (*line == '\n' || *line == '\r')
8868             line++;
8869         line_end = line;
8870         while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
8871             line_end++;
8872         line_end[0] = 0;
8873         if (line[0] == ';')
8874             continue;
8875         if (line[0] == '[' && line_end > line && line_end[-1] == ']')
8876         {
8877             // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
8878             line_end[-1] = 0;
8879             const char* name_end = line_end - 1;
8880             const char* type_start = line + 1;
8881             char* type_end = (char*)(intptr_t)ImStrchrRange(type_start, name_end, ']');
8882             const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
8883             if (!type_end || !name_start)
8884             {
8885                 name_start = type_start; // Import legacy entries that have no type
8886                 type_start = "Window";
8887             }
8888             else
8889             {
8890                 *type_end = 0; // Overwrite first ']'
8891                 name_start++;  // Skip second '['
8892             }
8893             entry_handler = FindSettingsHandler(type_start);
8894             entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
8895         }
8896         else if (entry_handler != NULL && entry_data != NULL)
8897         {
8898             // Let type handler parse the line
8899             entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
8900         }
8901     }
8902     ImGui::MemFree(buf);
8903     g.SettingsLoaded = true;
8904 }
8905 
SaveIniSettingsToDisk(const char * ini_filename)8906 void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
8907 {
8908     ImGuiContext& g = *GImGui;
8909     g.SettingsDirtyTimer = 0.0f;
8910     if (!ini_filename)
8911         return;
8912 
8913     size_t ini_data_size = 0;
8914     const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
8915     FILE* f = ImFileOpen(ini_filename, "wt");
8916     if (!f)
8917         return;
8918     fwrite(ini_data, sizeof(char), ini_data_size, f);
8919     fclose(f);
8920 }
8921 
8922 // Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
SaveIniSettingsToMemory(size_t * out_size)8923 const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
8924 {
8925     ImGuiContext& g = *GImGui;
8926     g.SettingsDirtyTimer = 0.0f;
8927     g.SettingsIniData.Buf.resize(0);
8928     g.SettingsIniData.Buf.push_back(0);
8929     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
8930     {
8931         ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
8932         handler->WriteAllFn(&g, handler, &g.SettingsIniData);
8933     }
8934     if (out_size)
8935         *out_size = (size_t)g.SettingsIniData.size();
8936     return g.SettingsIniData.c_str();
8937 }
8938 
SettingsHandlerWindow_ReadOpen(ImGuiContext *,ImGuiSettingsHandler *,const char * name)8939 static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
8940 {
8941     ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHash(name, 0));
8942     if (!settings)
8943         settings = ImGui::CreateNewWindowSettings(name);
8944     return (void*)settings;
8945 }
8946 
SettingsHandlerWindow_ReadLine(ImGuiContext *,ImGuiSettingsHandler *,void * entry,const char * line)8947 static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
8948 {
8949     ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
8950     float x, y;
8951     int i;
8952     if (sscanf(line, "Pos=%f,%f", &x, &y) == 2)         settings->Pos = ImVec2(x, y);
8953     else if (sscanf(line, "Size=%f,%f", &x, &y) == 2)   settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize);
8954     else if (sscanf(line, "Collapsed=%d", &i) == 1)     settings->Collapsed = (i != 0);
8955 }
8956 
SettingsHandlerWindow_WriteAll(ImGuiContext * imgui_ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)8957 static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
8958 {
8959     // Gather data from windows that were active during this session
8960     // (if a window wasn't opened in this session we preserve its settings)
8961     ImGuiContext& g = *imgui_ctx;
8962     for (int i = 0; i != g.Windows.Size; i++)
8963     {
8964         ImGuiWindow* window = g.Windows[i];
8965         if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
8966             continue;
8967 
8968         ImGuiWindowSettings* settings = (window->SettingsIdx != -1) ? &g.SettingsWindows[window->SettingsIdx] : ImGui::FindWindowSettings(window->ID);
8969         if (!settings)
8970         {
8971             settings = ImGui::CreateNewWindowSettings(window->Name);
8972             window->SettingsIdx = g.SettingsWindows.index_from_ptr(settings);
8973         }
8974         IM_ASSERT(settings->ID == window->ID);
8975         settings->Pos = window->Pos;
8976         settings->Size = window->SizeFull;
8977         settings->Collapsed = window->Collapsed;
8978     }
8979 
8980     // Write to text buffer
8981     buf->reserve(buf->size() + g.SettingsWindows.Size * 96); // ballpark reserve
8982     for (int i = 0; i != g.SettingsWindows.Size; i++)
8983     {
8984         const ImGuiWindowSettings* settings = &g.SettingsWindows[i];
8985         if (settings->Pos.x == FLT_MAX)
8986             continue;
8987         const char* name = settings->Name;
8988         if (const char* p = strstr(name, "###"))  // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
8989             name = p;
8990         buf->appendf("[%s][%s]\n", handler->TypeName, name);
8991         buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y);
8992         buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y);
8993         buf->appendf("Collapsed=%d\n", settings->Collapsed);
8994         buf->appendf("\n");
8995     }
8996 }
8997 
8998 //-----------------------------------------------------------------------------
8999 // [SECTION] PLATFORM DEPENDENT HELPERS
9000 //-----------------------------------------------------------------------------
9001 
9002 #if defined(_WIN32) && !defined(_WINDOWS_) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS))
9003 #ifndef WIN32_LEAN_AND_MEAN
9004 #define WIN32_LEAN_AND_MEAN
9005 #endif
9006 #ifndef __MINGW32__
9007 #include <Windows.h>
9008 #else
9009 #include <windows.h>
9010 #endif
9011 #endif
9012 
9013 // Win32 API clipboard implementation
9014 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
9015 
9016 #ifdef _MSC_VER
9017 #pragma comment(lib, "user32")
9018 #endif
9019 
GetClipboardTextFn_DefaultImpl(void *)9020 static const char* GetClipboardTextFn_DefaultImpl(void*)
9021 {
9022     static ImVector<char> buf_local;
9023     buf_local.clear();
9024     if (!::OpenClipboard(NULL))
9025         return NULL;
9026     HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
9027     if (wbuf_handle == NULL)
9028     {
9029         ::CloseClipboard();
9030         return NULL;
9031     }
9032     if (ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle))
9033     {
9034         int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
9035         buf_local.resize(buf_len);
9036         ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL);
9037     }
9038     ::GlobalUnlock(wbuf_handle);
9039     ::CloseClipboard();
9040     return buf_local.Data;
9041 }
9042 
SetClipboardTextFn_DefaultImpl(void *,const char * text)9043 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9044 {
9045     if (!::OpenClipboard(NULL))
9046         return;
9047     const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
9048     HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
9049     if (wbuf_handle == NULL)
9050     {
9051         ::CloseClipboard();
9052         return;
9053     }
9054     ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle);
9055     ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
9056     ::GlobalUnlock(wbuf_handle);
9057     ::EmptyClipboard();
9058     if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
9059         ::GlobalFree(wbuf_handle);
9060     ::CloseClipboard();
9061 }
9062 
9063 #else
9064 
9065 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
GetClipboardTextFn_DefaultImpl(void *)9066 static const char* GetClipboardTextFn_DefaultImpl(void*)
9067 {
9068     ImGuiContext& g = *GImGui;
9069     return g.PrivateClipboard.empty() ? NULL : g.PrivateClipboard.begin();
9070 }
9071 
9072 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
SetClipboardTextFn_DefaultImpl(void *,const char * text)9073 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9074 {
9075     ImGuiContext& g = *GImGui;
9076     g.PrivateClipboard.clear();
9077     const char* text_end = text + strlen(text);
9078     g.PrivateClipboard.resize((int)(text_end - text) + 1);
9079     memcpy(&g.PrivateClipboard[0], text, (size_t)(text_end - text));
9080     g.PrivateClipboard[(int)(text_end - text)] = 0;
9081 }
9082 
9083 #endif
9084 
9085 // Win32 API IME support (for Asian languages, etc.)
9086 #if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
9087 
9088 #include <imm.h>
9089 #ifdef _MSC_VER
9090 #pragma comment(lib, "imm32")
9091 #endif
9092 
ImeSetInputScreenPosFn_DefaultImpl(int x,int y)9093 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
9094 {
9095     // Notify OS Input Method Editor of text input position
9096     if (HWND hwnd = (HWND)GImGui->IO.ImeWindowHandle)
9097         if (HIMC himc = ::ImmGetContext(hwnd))
9098         {
9099             COMPOSITIONFORM cf;
9100             cf.ptCurrentPos.x = x;
9101             cf.ptCurrentPos.y = y;
9102             cf.dwStyle = CFS_FORCE_POSITION;
9103             ::ImmSetCompositionWindow(himc, &cf);
9104             ::ImmReleaseContext(hwnd, himc);
9105         }
9106 }
9107 
9108 #else
9109 
ImeSetInputScreenPosFn_DefaultImpl(int,int)9110 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
9111 
9112 #endif
9113 
9114 //-----------------------------------------------------------------------------
9115 // [SECTION] METRICS/DEBUG WINDOW
9116 //-----------------------------------------------------------------------------
9117 
ShowMetricsWindow(bool * p_open)9118 void ImGui::ShowMetricsWindow(bool* p_open)
9119 {
9120     if (!ImGui::Begin("ImGui Metrics", p_open))
9121     {
9122         ImGui::End();
9123         return;
9124     }
9125 
9126     static bool show_draw_cmd_clip_rects = true;
9127     static bool show_window_begin_order = false;
9128     ImGuiIO& io = ImGui::GetIO();
9129     ImGui::Text("Dear ImGui %s", ImGui::GetVersion());
9130     ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
9131     ImGui::Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
9132     ImGui::Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
9133     ImGui::Text("%d allocations", io.MetricsActiveAllocations);
9134     ImGui::Checkbox("Show clipping rectangles when hovering draw commands", &show_draw_cmd_clip_rects);
9135     ImGui::Checkbox("Ctrl shows window begin order", &show_window_begin_order);
9136     ImGui::Separator();
9137 
9138     struct Funcs
9139     {
9140         static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label)
9141         {
9142             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);
9143             if (draw_list == ImGui::GetWindowDrawList())
9144             {
9145                 ImGui::SameLine();
9146                 ImGui::TextColored(ImColor(255,100,100), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
9147                 if (node_open) ImGui::TreePop();
9148                 return;
9149             }
9150 
9151             ImDrawList* overlay_draw_list = GetOverlayDrawList(window); // Render additional visuals into the top-most draw list
9152             if (window && IsItemHovered())
9153                 overlay_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
9154             if (!node_open)
9155                 return;
9156 
9157             int elem_offset = 0;
9158             for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
9159             {
9160                 if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0)
9161                     continue;
9162                 if (pcmd->UserCallback)
9163                 {
9164                     ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
9165                     continue;
9166                 }
9167                 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
9168                 bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "Draw %4d %s vtx, tex 0x%p, clip_rect (%4.0f,%4.0f)-(%4.0f,%4.0f)", pcmd->ElemCount, draw_list->IdxBuffer.Size > 0 ? "indexed" : "non-indexed", pcmd->TextureId, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
9169                 if (show_draw_cmd_clip_rects && ImGui::IsItemHovered())
9170                 {
9171                     ImRect clip_rect = pcmd->ClipRect;
9172                     ImRect vtxs_rect;
9173                     for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++)
9174                         vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos);
9175                     clip_rect.Floor(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,255,0,255));
9176                     vtxs_rect.Floor(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,0,255,255));
9177                 }
9178                 if (!pcmd_node_open)
9179                     continue;
9180 
9181                 // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
9182                 ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
9183                 while (clipper.Step())
9184                     for (int prim = clipper.DisplayStart, vtx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++)
9185                     {
9186                         char buf[300];
9187                         char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf);
9188                         ImVec2 triangles_pos[3];
9189                         for (int n = 0; n < 3; n++, vtx_i++)
9190                         {
9191                             ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[vtx_i] : vtx_i];
9192                             triangles_pos[n] = v.pos;
9193                             buf_p += ImFormatString(buf_p, (int)(buf_end - buf_p), "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n", (n == 0) ? "vtx" : "   ", vtx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
9194                         }
9195                         ImGui::Selectable(buf, false);
9196                         if (ImGui::IsItemHovered())
9197                         {
9198                             ImDrawListFlags backup_flags = overlay_draw_list->Flags;
9199                             overlay_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines at is more readable for very large and thin triangles.
9200                             overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f);
9201                             overlay_draw_list->Flags = backup_flags;
9202                         }
9203                     }
9204                 ImGui::TreePop();
9205             }
9206             ImGui::TreePop();
9207         }
9208 
9209         static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label)
9210         {
9211             if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size))
9212                 return;
9213             for (int i = 0; i < windows.Size; i++)
9214                 Funcs::NodeWindow(windows[i], "Window");
9215             ImGui::TreePop();
9216         }
9217 
9218         static void NodeWindow(ImGuiWindow* window, const char* label)
9219         {
9220             if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window))
9221                 return;
9222             ImGuiWindowFlags flags = window->Flags;
9223             NodeDrawList(window, window->DrawList, "DrawList");
9224             ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y);
9225             ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
9226                 (flags & ImGuiWindowFlags_ChildWindow)  ? "Child " : "",      (flags & ImGuiWindowFlags_Tooltip)     ? "Tooltip "   : "",  (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
9227                 (flags & ImGuiWindowFlags_Modal)        ? "Modal " : "",      (flags & ImGuiWindowFlags_ChildMenu)   ? "ChildMenu " : "",  (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
9228                 (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
9229             ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, GetWindowScrollMaxX(window), window->Scroll.y, GetWindowScrollMaxY(window));
9230             ImGui::BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
9231             ImGui::BulletText("Appearing: %d, Hidden: %d (Reg %d Resize %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesRegular, window->HiddenFramesForResize, window->SkipItems);
9232             ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask);
9233             ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
9234             if (!window->NavRectRel[0].IsInverted())
9235                 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);
9236             else
9237                 ImGui::BulletText("NavRectRel[0]: <None>");
9238             if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
9239             if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow");
9240             if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
9241             if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
9242             {
9243                 for (int n = 0; n < window->ColumnsStorage.Size; n++)
9244                 {
9245                     const ImGuiColumnsSet* columns = &window->ColumnsStorage[n];
9246                     if (ImGui::TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
9247                     {
9248                         ImGui::BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->MaxX - columns->MinX, columns->MinX, columns->MaxX);
9249                         for (int column_n = 0; column_n < columns->Columns.Size; column_n++)
9250                             ImGui::BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, OffsetNormToPixels(columns, columns->Columns[column_n].OffsetNorm));
9251                         ImGui::TreePop();
9252                     }
9253                 }
9254                 ImGui::TreePop();
9255             }
9256             ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair));
9257             ImGui::TreePop();
9258         }
9259 
9260         static void NodeTabBar(ImGuiTabBar* tab_bar)
9261         {
9262             // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernable strings.
9263             char buf[256];
9264             char* p = buf;
9265             const char* buf_end = buf + IM_ARRAYSIZE(buf);
9266             p += ImFormatString(p, buf_end - p, "TabBar (%d tabs)%s",
9267                 tab_bar->Tabs.Size, (tab_bar->PrevFrameVisible < ImGui::GetFrameCount() - 2) ? " *Inactive*" : "");
9268             if (ImGui::TreeNode(tab_bar, "%s", buf))
9269             {
9270                 for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
9271                 {
9272                     const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
9273                     ImGui::PushID(tab);
9274                     if (ImGui::SmallButton("<")) { TabBarQueueChangeTabOrder(tab_bar, tab, -1); } ImGui::SameLine(0, 2);
9275                     if (ImGui::SmallButton(">")) { TabBarQueueChangeTabOrder(tab_bar, tab, +1); } ImGui::SameLine();
9276                     ImGui::Text("%02d%c Tab 0x%08X", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID);
9277                     ImGui::PopID();
9278                 }
9279                 ImGui::TreePop();
9280             }
9281         }
9282     };
9283 
9284     // Access private state, we are going to display the draw lists from last frame
9285     ImGuiContext& g = *GImGui;
9286     Funcs::NodeWindows(g.Windows, "Windows");
9287     if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size))
9288     {
9289         for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++)
9290             Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList");
9291         ImGui::TreePop();
9292     }
9293     if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
9294     {
9295         for (int i = 0; i < g.OpenPopupStack.Size; i++)
9296         {
9297             ImGuiWindow* window = g.OpenPopupStack[i].Window;
9298             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" : "");
9299         }
9300         ImGui::TreePop();
9301     }
9302     if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.Data.Size))
9303     {
9304         for (int n = 0; n < g.TabBars.Data.Size; n++)
9305             Funcs::NodeTabBar(g.TabBars.GetByIndex(n));
9306         ImGui::TreePop();
9307     }
9308     if (ImGui::TreeNode("Internal state"))
9309     {
9310         const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
9311         ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
9312         ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
9313         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
9314         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]);
9315         ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
9316         ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
9317         ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
9318         ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
9319         ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
9320         ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
9321         ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
9322         ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
9323         ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
9324         ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
9325         ImGui::TreePop();
9326     }
9327 
9328 
9329     if (g.IO.KeyCtrl && show_window_begin_order)
9330     {
9331         for (int n = 0; n < g.Windows.Size; n++)
9332         {
9333             ImGuiWindow* window = g.Windows[n];
9334             if ((window->Flags & ImGuiWindowFlags_ChildWindow) || !window->WasActive)
9335                 continue;
9336             char buf[32];
9337             ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
9338             float font_size = ImGui::GetFontSize() * 2;
9339             ImDrawList* overlay_draw_list = GetOverlayDrawList(window);
9340             overlay_draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
9341             overlay_draw_list->AddText(NULL, font_size, window->Pos, IM_COL32(255, 255, 255, 255), buf);
9342         }
9343     }
9344     ImGui::End();
9345 }
9346 
9347 //-----------------------------------------------------------------------------
9348 
9349 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
9350 // 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.
9351 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
9352 #include "imgui_user.inl"
9353 #endif
9354 
9355 //-----------------------------------------------------------------------------
9356