duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

imgui.cpp (839121B)


      1 // dear imgui, v1.91.0
      2 // (main code and documentation)
      3 
      4 // Help:
      5 // - See links below.
      6 // - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
      7 // - Read top of imgui.cpp for more details, links and comments.
      8 
      9 // Resources:
     10 // - FAQ ........................ https://dearimgui.com/faq (in repository as docs/FAQ.md)
     11 // - Homepage ................... https://github.com/ocornut/imgui
     12 // - Releases & changelog ....... https://github.com/ocornut/imgui/releases
     13 // - Gallery .................... https://github.com/ocornut/imgui/issues/7503 (please post your screenshots/video there!)
     14 // - Wiki ....................... https://github.com/ocornut/imgui/wiki (lots of good stuff there)
     15 //   - Getting Started            https://github.com/ocornut/imgui/wiki/Getting-Started (how to integrate in an existing app by adding ~25 lines of code)
     16 //   - Third-party Extensions     https://github.com/ocornut/imgui/wiki/Useful-Extensions (ImPlot & many more)
     17 //   - Bindings/Backends          https://github.com/ocornut/imgui/wiki/Bindings (language bindings, backends for various tech/engines)
     18 //   - Glossary                   https://github.com/ocornut/imgui/wiki/Glossary
     19 //   - Debug Tools                https://github.com/ocornut/imgui/wiki/Debug-Tools
     20 //   - Software using Dear ImGui  https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui
     21 // - Issues & support ........... https://github.com/ocornut/imgui/issues
     22 // - Test Engine & Automation ... https://github.com/ocornut/imgui_test_engine (test suite, test engine to automate your apps)
     23 
     24 // For first-time users having issues compiling/linking/running/loading fonts:
     25 // please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above.
     26 // Everything else should be asked in 'Issues'! We are building a database of cross-linked knowledge there.
     27 
     28 // Copyright (c) 2014-2024 Omar Cornut
     29 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
     30 // See LICENSE.txt for copyright and licensing details (standard MIT License).
     31 // This library is free but needs your support to sustain development and maintenance.
     32 // Businesses: you can support continued development via B2B invoiced technical support, maintenance and sponsoring contracts.
     33 // PLEASE reach out at omar AT dearimgui DOT com. See https://github.com/ocornut/imgui/wiki/Funding
     34 // Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine.
     35 
     36 // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
     37 // Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
     38 // modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
     39 // come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
     40 // to a better solution or official support for them.
     41 
     42 /*
     43 
     44 Index of this file:
     45 
     46 DOCUMENTATION
     47 
     48 - MISSION STATEMENT
     49 - CONTROLS GUIDE
     50 - PROGRAMMER GUIDE
     51   - READ FIRST
     52   - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
     53   - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
     54   - HOW A SIMPLE APPLICATION MAY LOOK LIKE
     55   - HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
     56 - API BREAKING CHANGES (read me when you update!)
     57 - FREQUENTLY ASKED QUESTIONS (FAQ)
     58   - Read all answers online: https://www.dearimgui.com/faq, or in docs/FAQ.md (with a Markdown viewer)
     59 
     60 CODE
     61 (search for "[SECTION]" in the code to find them)
     62 
     63 // [SECTION] INCLUDES
     64 // [SECTION] FORWARD DECLARATIONS
     65 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
     66 // [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
     67 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
     68 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
     69 // [SECTION] MISC HELPERS/UTILITIES (File functions)
     70 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
     71 // [SECTION] MISC HELPERS/UTILITIES (Color functions)
     72 // [SECTION] ImGuiStorage
     73 // [SECTION] ImGuiTextFilter
     74 // [SECTION] ImGuiTextBuffer, ImGuiTextIndex
     75 // [SECTION] ImGuiListClipper
     76 // [SECTION] STYLING
     77 // [SECTION] RENDER HELPERS
     78 // [SECTION] INITIALIZATION, SHUTDOWN
     79 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
     80 // [SECTION] ID STACK
     81 // [SECTION] INPUTS
     82 // [SECTION] ERROR CHECKING
     83 // [SECTION] ITEM SUBMISSION
     84 // [SECTION] LAYOUT
     85 // [SECTION] SCROLLING
     86 // [SECTION] TOOLTIPS
     87 // [SECTION] POPUPS
     88 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
     89 // [SECTION] DRAG AND DROP
     90 // [SECTION] LOGGING/CAPTURING
     91 // [SECTION] SETTINGS
     92 // [SECTION] LOCALIZATION
     93 // [SECTION] VIEWPORTS, PLATFORM WINDOWS
     94 // [SECTION] PLATFORM DEPENDENT HELPERS
     95 // [SECTION] METRICS/DEBUGGER WINDOW
     96 // [SECTION] DEBUG LOG WINDOW
     97 // [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL)
     98 
     99 */
    100 
    101 //-----------------------------------------------------------------------------
    102 // DOCUMENTATION
    103 //-----------------------------------------------------------------------------
    104 
    105 /*
    106 
    107  MISSION STATEMENT
    108  =================
    109 
    110  - Easy to use to create code-driven and data-driven tools.
    111  - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
    112  - Easy to hack and improve.
    113  - Minimize setup and maintenance.
    114  - Minimize state storage on user side.
    115  - Minimize state synchronization.
    116  - Portable, minimize dependencies, run on target (consoles, phones, etc.).
    117  - Efficient runtime and memory consumption.
    118 
    119  Designed primarily for developers and content-creators, not the typical end-user!
    120  Some of the current weaknesses (which we aim to address in the future) includes:
    121 
    122  - Doesn't look fancy.
    123  - Limited layout features, intricate layouts are typically crafted in code.
    124 
    125 
    126  CONTROLS GUIDE
    127  ==============
    128 
    129  - MOUSE CONTROLS
    130    - Mouse wheel:                   Scroll vertically.
    131    - SHIFT+Mouse wheel:             Scroll horizontally.
    132    - Click [X]:                     Close a window, available when 'bool* p_open' is passed to ImGui::Begin().
    133    - Click ^, Double-Click title:   Collapse window.
    134    - Drag on corner/border:         Resize window (double-click to auto fit window to its contents).
    135    - Drag on any empty space:       Move window (unless io.ConfigWindowsMoveFromTitleBarOnly = true).
    136    - Left-click outside popup:      Close popup stack (right-click over underlying popup: Partially close popup stack).
    137 
    138  - TEXT EDITOR
    139    - Hold SHIFT or Drag Mouse:      Select text.
    140    - CTRL+Left/Right:               Word jump.
    141    - CTRL+Shift+Left/Right:         Select words.
    142    - CTRL+A or Double-Click:        Select All.
    143    - CTRL+X, CTRL+C, CTRL+V:        Use OS clipboard.
    144    - CTRL+Z, CTRL+Y:                Undo, Redo.
    145    - ESCAPE:                        Revert text to its original value.
    146    - On OSX, controls are automatically adjusted to match standard OSX text editing 2ts and behaviors.
    147 
    148  - KEYBOARD CONTROLS
    149    - Basic:
    150      - Tab, SHIFT+Tab               Cycle through text editable fields.
    151      - CTRL+Tab, CTRL+Shift+Tab     Cycle through windows.
    152      - CTRL+Click                   Input text into a Slider or Drag widget.
    153    - Extended features with `io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard`:
    154      - Tab, SHIFT+Tab:              Cycle through every items.
    155      - Arrow keys                   Move through items using directional navigation. Tweak value.
    156      - Arrow keys + Alt, Shift      Tweak slower, tweak faster (when using arrow keys).
    157      - Enter                        Activate item (prefer text input when possible).
    158      - Space                        Activate item (prefer tweaking with arrows when possible).
    159      - Escape                       Deactivate item, leave child window, close popup.
    160      - Page Up, Page Down           Previous page, next page.
    161      - Home, End                    Scroll to top, scroll to bottom.
    162      - Alt                          Toggle between scrolling layer and menu layer.
    163      - CTRL+Tab then Ctrl+Arrows    Move window. Hold SHIFT to resize instead of moving.
    164    - Output when ImGuiConfigFlags_NavEnableKeyboard set,
    165      - io.WantCaptureKeyboard flag is set when keyboard is claimed.
    166      - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
    167      - io.NavVisible: true when the navigation cursor is visible (usually goes to back false when mouse is used).
    168 
    169  - GAMEPAD CONTROLS
    170    - Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
    171    - Particularly useful to use Dear ImGui on a console system (e.g. PlayStation, Switch, Xbox) without a mouse!
    172    - Download controller mapping PNG/PSD at http://dearimgui.com/controls_sheets
    173    - Backend support: backend needs to:
    174       - Set 'io.BackendFlags |= ImGuiBackendFlags_HasGamepad' + call io.AddKeyEvent/AddKeyAnalogEvent() with ImGuiKey_Gamepad_XXX keys.
    175       - For analog values (0.0f to 1.0f), backend is responsible to handling a dead-zone and rescaling inputs accordingly.
    176         Backend 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.).
    177       - BEFORE 1.87, BACKENDS USED TO WRITE TO io.NavInputs[]. This is now obsolete. Please call io functions instead!
    178    - If you need to share inputs between your game and the Dear ImGui interface, the easiest approach is to go all-or-nothing,
    179      with a buttons combo to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
    180 
    181  - REMOTE INPUTS SHARING & MOUSE EMULATION
    182    - PS4/PS5 users: Consider emulating a mouse cursor with DualShock touch pad or a spare analog stick as a mouse-emulation fallback.
    183    - Consoles/Tablet/Phone users: Consider using a Synergy 1.x server (on your PC) + run examples/libs/synergy/uSynergy.c (on your console/tablet/phone app)
    184      in order to share your PC mouse/keyboard.
    185    - See https://github.com/ocornut/imgui/wiki/Useful-Extensions#remoting for other remoting solutions.
    186    - 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.
    187      Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs Dear ImGui to move your mouse cursor along with navigation movements.
    188      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.
    189      When that happens your backend NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the backends in examples/ do that.
    190      (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, Dear ImGui will misbehave as it will see your mouse moving back & forth!)
    191      (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
    192      to set a boolean to ignore your other external mouse positions until the external source is moved again.)
    193 
    194 
    195  PROGRAMMER GUIDE
    196  ================
    197 
    198  READ FIRST
    199  ----------
    200  - Remember to check the wonderful Wiki (https://github.com/ocornut/imgui/wiki)
    201  - Your code creates the UI every frame of your application loop, if your code doesn't run the UI is gone!
    202    The UI can be highly dynamic, there are no construction or destruction steps, less superfluous
    203    data retention on your side, less state duplication, less state synchronization, fewer bugs.
    204  - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
    205    Or browse https://pthom.github.io/imgui_manual_online/manual/imgui_manual.html for interactive web version.
    206  - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
    207  - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
    208    You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in Wiki.
    209  - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
    210    For every application frame, your UI code will be called only once. This is in contrast to e.g. Unity's implementation of an IMGUI,
    211    where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
    212  - Our origin is on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
    213  - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).
    214    If you get an assert, read the messages and comments around the assert.
    215  - This codebase aims to be highly optimized:
    216    - A typical idle frame should never call malloc/free.
    217    - We rely on a maximum of constant-time or O(N) algorithms. Limiting searches/scans as much as possible.
    218    - We put particular energy in making sure performances are decent with typical "Debug" build settings as well.
    219      Which mean we tend to avoid over-relying on "zero-cost abstraction" as they aren't zero-cost at all.
    220  - This codebase aims to be both highly opinionated and highly flexible:
    221    - This code works because of the things it choose to solve or not solve.
    222    - C++: this is a pragmatic C-ish codebase: we don't use fancy C++ features, we don't include C++ headers,
    223      and ImGui:: is a namespace. We rarely use member functions (and when we did, I am mostly regretting it now).
    224      This is to increase compatibility, increase maintainability and facilitate use from other languages.
    225    - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
    226      See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
    227      We can can optionally export math operators for ImVec2/ImVec4 using IMGUI_DEFINE_MATH_OPERATORS, which we use internally.
    228    - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction
    229      (so don't use ImVector in your code or at our own risk!).
    230    - Building: We don't use nor mandate a build system for the main library.
    231      This is in an effort to ensure that it works in the real world aka with any esoteric build setup.
    232      This is also because providing a build system for the main library would be of little-value.
    233      The build problems are almost never coming from the main library but from specific backends.
    234 
    235 
    236  HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
    237  ----------------------------------------------
    238  - Update submodule or copy/overwrite every file.
    239  - About imconfig.h:
    240    - You may modify your copy of imconfig.h, in this case don't overwrite it.
    241    - or you may locally branch to modify imconfig.h and merge/rebase latest.
    242    - or you may '#define IMGUI_USER_CONFIG "my_config_file.h"' globally from your build system to
    243      specify a custom path for your imconfig.h file and instead not have to modify the default one.
    244 
    245  - Overwrite all the sources files except for imconfig.h (if you have modified your copy of imconfig.h)
    246  - Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over "master".
    247  - You can also use '#define IMGUI_USER_CONFIG "my_config_file.h" to redirect configuration to your own file.
    248  - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
    249    If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
    250    from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
    251    likely be a comment about it. Please report any issue to the GitHub page!
    252  - To find out usage of old API, you can add '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in your configuration file.
    253  - Try to keep your copy of Dear ImGui reasonably up to date!
    254 
    255 
    256  GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
    257  ---------------------------------------------------------------
    258  - See https://github.com/ocornut/imgui/wiki/Getting-Started.
    259  - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
    260  - In the majority of cases you should be able to use unmodified backends files available in the backends/ folder.
    261  - Add the Dear ImGui source files + selected backend source files to your projects or using your preferred build system.
    262    It is recommended you build and statically link the .cpp files as part of your project and NOT as a shared library (DLL).
    263  - You can later customize the imconfig.h file to tweak some compile-time behavior, such as integrating Dear ImGui types with your own maths types.
    264  - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
    265  - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
    266    Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
    267    phases of your own application. All rendering information is stored into command-lists that you will retrieve after calling ImGui::Render().
    268  - Refer to the backends and demo applications in the examples/ folder for instruction on how to setup your code.
    269  - 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.
    270 
    271 
    272  HOW A SIMPLE APPLICATION MAY LOOK LIKE
    273  --------------------------------------
    274  EXHIBIT 1: USING THE EXAMPLE BACKENDS (= imgui_impl_XXX.cpp files from the backends/ folder).
    275  The sub-folders in examples/ contain examples applications following this structure.
    276 
    277      // Application init: create a dear imgui context, setup some options, load fonts
    278      ImGui::CreateContext();
    279      ImGuiIO& io = ImGui::GetIO();
    280      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
    281      // TODO: Fill optional fields of the io structure later.
    282      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
    283 
    284      // Initialize helper Platform and Renderer backends (here we are using imgui_impl_win32.cpp and imgui_impl_dx11.cpp)
    285      ImGui_ImplWin32_Init(hwnd);
    286      ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
    287 
    288      // Application main loop
    289      while (true)
    290      {
    291          // Feed inputs to dear imgui, start new frame
    292          ImGui_ImplDX11_NewFrame();
    293          ImGui_ImplWin32_NewFrame();
    294          ImGui::NewFrame();
    295 
    296          // Any application code here
    297          ImGui::Text("Hello, world!");
    298 
    299          // Render dear imgui into screen
    300          ImGui::Render();
    301          ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
    302          g_pSwapChain->Present(1, 0);
    303      }
    304 
    305      // Shutdown
    306      ImGui_ImplDX11_Shutdown();
    307      ImGui_ImplWin32_Shutdown();
    308      ImGui::DestroyContext();
    309 
    310  EXHIBIT 2: IMPLEMENTING CUSTOM BACKEND / CUSTOM ENGINE
    311 
    312      // Application init: create a dear imgui context, setup some options, load fonts
    313      ImGui::CreateContext();
    314      ImGuiIO& io = ImGui::GetIO();
    315      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
    316      // TODO: Fill optional fields of the io structure later.
    317      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
    318 
    319      // Build and load the texture atlas into a texture
    320      // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
    321      int width, height;
    322      unsigned char* pixels = nullptr;
    323      io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
    324 
    325      // At this point you've got the texture data and you need to upload that to your graphic system:
    326      // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
    327      // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID.
    328      MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
    329      io.Fonts->SetTexID((void*)texture);
    330 
    331      // Application main loop
    332      while (true)
    333      {
    334         // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
    335         // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform Backends)
    336         io.DeltaTime = 1.0f/60.0f;              // set the time elapsed since the previous frame (in seconds)
    337         io.DisplaySize.x = 1920.0f;             // set the current display width
    338         io.DisplaySize.y = 1280.0f;             // set the current display height here
    339         io.AddMousePosEvent(mouse_x, mouse_y);  // update mouse position
    340         io.AddMouseButtonEvent(0, mouse_b[0]);  // update mouse button states
    341         io.AddMouseButtonEvent(1, mouse_b[1]);  // update mouse button states
    342 
    343         // Call NewFrame(), after this point you can use ImGui::* functions anytime
    344         // (So you want to try calling NewFrame() as early as you can in your main loop to be able to use Dear ImGui everywhere)
    345         ImGui::NewFrame();
    346 
    347         // Most of your application code here
    348         ImGui::Text("Hello, world!");
    349         MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
    350         MyGameRender(); // may use any Dear ImGui functions as well!
    351 
    352         // Render dear imgui, swap buffers
    353         // (You want to try calling EndFrame/Render as late as you can, to be able to use Dear ImGui in your own game rendering code)
    354         ImGui::EndFrame();
    355         ImGui::Render();
    356         ImDrawData* draw_data = ImGui::GetDrawData();
    357         MyImGuiRenderFunction(draw_data);
    358         SwapBuffers();
    359      }
    360 
    361      // Shutdown
    362      ImGui::DestroyContext();
    363 
    364  To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest of your application,
    365  you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
    366  Please read the FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" about this.
    367 
    368 
    369  HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
    370  ---------------------------------------------
    371  The backends in impl_impl_XXX.cpp files contain many working implementations of a rendering function.
    372 
    373     void MyImGuiRenderFunction(ImDrawData* draw_data)
    374     {
    375        // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
    376        // TODO: Setup texture sampling state: sample with bilinear filtering (NOT point/nearest filtering). Use 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines;' to allow point/nearest filtering.
    377        // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
    378        // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
    379        // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
    380        ImVec2 clip_off = draw_data->DisplayPos;
    381        for (int n = 0; n < draw_data->CmdListsCount; n++)
    382        {
    383           const ImDrawList* cmd_list = draw_data->CmdLists[n];
    384           const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;  // vertex buffer generated by Dear ImGui
    385           const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;   // index buffer generated by Dear ImGui
    386           for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
    387           {
    388              const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
    389              if (pcmd->UserCallback)
    390              {
    391                  pcmd->UserCallback(cmd_list, pcmd);
    392              }
    393              else
    394              {
    395                  // Project scissor/clipping rectangles into framebuffer space
    396                  ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y);
    397                  ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y);
    398                  if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
    399                      continue;
    400 
    401                  // We are using scissoring to clip some objects. All low-level graphics API should support it.
    402                  // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
    403                  //   (some elements visible outside their bounds) but you can fix that once everything else works!
    404                  // - Clipping coordinates are provided in imgui coordinates space:
    405                  //   - For a given viewport, draw_data->DisplayPos == viewport->Pos and draw_data->DisplaySize == viewport->Size
    406                  //   - In a single viewport application, draw_data->DisplayPos == (0,0) and draw_data->DisplaySize == io.DisplaySize, but always use GetMainViewport()->Pos/Size instead of hardcoding those values.
    407                  //   - In the interest of supporting multi-viewport applications (see 'docking' branch on github),
    408                  //     always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
    409                  // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
    410                  MyEngineSetScissor(clip_min.x, clip_min.y, clip_max.x, clip_max.y);
    411 
    412                  // The texture for the draw call is specified by pcmd->GetTexID().
    413                  // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization.
    414                  MyEngineBindTexture((MyTexture*)pcmd->GetTexID());
    415 
    416                  // Render 'pcmd->ElemCount/3' indexed triangles.
    417                  // By default the indices ImDrawIdx are 16-bit, you can change them to 32-bit in imconfig.h if your engine doesn't support 16-bit indices.
    418                  MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer + pcmd->IdxOffset, vtx_buffer, pcmd->VtxOffset);
    419              }
    420           }
    421        }
    422     }
    423 
    424 
    425  API BREAKING CHANGES
    426  ====================
    427 
    428  Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
    429  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.
    430  When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
    431  You can read releases logs https://github.com/ocornut/imgui/releases for more details.
    432 
    433  - 2024/07/25 (1.91.0) - obsoleted GetContentRegionMax(), GetWindowContentRegionMin() and GetWindowContentRegionMax(). (see #7838 on GitHub for more info)
    434                          you should never need those functions. you can do everything with GetCursorScreenPos() and GetContentRegionAvail() in a more simple way.
    435                             - instead of:  GetWindowContentRegionMax().x - GetCursorPos().x
    436                             - you can use: GetContentRegionAvail().x
    437                             - instead of:  GetWindowContentRegionMax().x + GetWindowPos().x
    438                             - you can use: GetCursorScreenPos().x + GetContentRegionAvail().x // when called from left edge of window
    439                             - instead of:  GetContentRegionMax()
    440                             - you can use: GetContentRegionAvail() + GetCursorScreenPos() - GetWindowPos() // right edge in local coordinates
    441                             - instead of:  GetWindowContentRegionMax().x - GetWindowContentRegionMin().x
    442                             - you can use: GetContentRegionAvail() // when called from left edge of window
    443  - 2024/07/15 (1.91.0) - renamed ImGuiSelectableFlags_DontClosePopups to ImGuiSelectableFlags_NoAutoClosePopups. (#1379, #1468, #2200, #4936, #5216, #7302, #7573)
    444                          (internals: also renamed ImGuiItemFlags_SelectableDontClosePopup into ImGuiItemFlags_AutoClosePopups with inverted behaviors)
    445  - 2024/07/15 (1.91.0) - obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag(ImGuiItemFlags_ButtonRepeat, ...)/PopItemFlag().
    446  - 2024/07/02 (1.91.0) - commented out obsolete ImGuiModFlags (renamed to ImGuiKeyChord in 1.89). (#4921, #456)
    447                        - commented out obsolete ImGuiModFlags_XXX values (renamed to ImGuiMod_XXX in 1.89). (#4921, #456)
    448                             - ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl, ImGuiModFlags_Shift -> ImGuiMod_Shift etc.
    449  - 2024/07/02 (1.91.0) - IO, IME: renamed platform IME hook and added explicit context for consistency and future-proofness.
    450                             - old: io.SetPlatformImeDataFn(ImGuiViewport* viewport, ImGuiPlatformImeData* data);
    451                             - new: io.PlatformSetImeDataFn(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data);
    452  - 2024/06/21 (1.90.9) - BeginChild: added ImGuiChildFlags_NavFlattened as a replacement for the window flag ImGuiWindowFlags_NavFlattened: the feature only ever made sense for BeginChild() anyhow.
    453                             - old: BeginChild("Name", size, 0, ImGuiWindowFlags_NavFlattened);
    454                             - new: BeginChild("Name", size, ImGuiChildFlags_NavFlattened, 0);
    455  - 2024/06/21 (1.90.9) - io: ClearInputKeys() (first exposed in 1.89.8) doesn't clear mouse data, newly added ClearInputMouse() does.
    456  - 2024/06/20 (1.90.9) - renamed ImGuiDragDropFlags_SourceAutoExpirePayload to ImGuiDragDropFlags_PayloadAutoExpire.
    457  - 2024/06/18 (1.90.9) - style: renamed ImGuiCol_TabActive -> ImGuiCol_TabSelected, ImGuiCol_TabUnfocused -> ImGuiCol_TabDimmed, ImGuiCol_TabUnfocusedActive -> ImGuiCol_TabDimmedSelected.
    458  - 2024/06/10 (1.90.9) - removed old nested structure: renaming ImGuiStorage::ImGuiStoragePair type to ImGuiStoragePair (simpler for many languages).
    459  - 2024/06/06 (1.90.8) - reordered ImGuiInputTextFlags values. This should not be breaking unless you are using generated headers that have values not matching the main library.
    460  - 2024/06/06 (1.90.8) - removed 'ImGuiButtonFlags_MouseButtonDefault_ = ImGuiButtonFlags_MouseButtonLeft', was mostly unused and misleading.
    461  - 2024/05/27 (1.90.7) - commented out obsolete symbols marked obsolete in 1.88 (May 2022):
    462                             - old: CaptureKeyboardFromApp(bool)
    463                             - new: SetNextFrameWantCaptureKeyboard(bool)
    464                             - old: CaptureMouseFromApp(bool)
    465                             - new: SetNextFrameWantCaptureMouse(bool)
    466  - 2024/05/22 (1.90.7) - inputs (internals): renamed ImGuiKeyOwner_None to ImGuiKeyOwner_NoOwner, to make use more explicit and reduce confusion with the default it is a non-zero value and cannot be the default value (never made public, but disclosing as I expect a few users caught on owner-aware inputs).
    467                        - inputs (internals): renamed ImGuiInputFlags_RouteGlobalLow -> ImGuiInputFlags_RouteGlobal, ImGuiInputFlags_RouteGlobal -> ImGuiInputFlags_RouteGlobalOverFocused, ImGuiInputFlags_RouteGlobalHigh -> ImGuiInputFlags_RouteGlobalHighest.
    468                        - inputs (internals): Shortcut(), SetShortcutRouting(): swapped last two parameters order in function signatures:
    469                             - old: Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id = 0, ImGuiInputFlags flags = 0);
    470                             - new: Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0, ImGuiID owner_id = 0);
    471                        - inputs (internals): owner-aware versions of IsKeyPressed(), IsKeyChordPressed(), IsMouseClicked(): swapped last two parameters order in function signatures.
    472                             - old: IsKeyPressed(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0);
    473                             - new: IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id = 0);
    474                             - old: IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInputFlags flags = 0);
    475                             - new: IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id = 0);
    476                          for various reasons those changes makes sense. They are being made because making some of those API public.
    477                          only past users of imgui_internal.h with the extra parameters will be affected. Added asserts for valid flags in various functions to detect _some_ misuses, BUT NOT ALL.
    478  - 2024/05/16 (1.90.7) - inputs: on macOS X, Cmd and Ctrl keys are now automatically swapped by io.AddKeyEvent() as this naturally align with how macOS X uses those keys.
    479                            - it shouldn't really affect you unless you had custom shortcut swapping in place for macOS X apps.
    480                            - removed ImGuiMod_Shortcut which was previously dynamically remapping to Ctrl or Cmd/Super. It is now unnecessary to specific cross-platform idiomatic shortcuts. (#2343, #4084, #5923, #456)
    481  - 2024/05/14 (1.90.7) - backends: SDL_Renderer2 and SDL_Renderer3 backend now take a SDL_Renderer* in their RenderDrawData() functions.
    482  - 2024/04/18 (1.90.6) - TreeNode: Fixed a layout inconsistency when using an empty/hidden label followed by a SameLine() call. (#7505, #282)
    483                            - old: TreeNode("##Hidden"); SameLine(); Text("Hello");     // <-- This was actually incorrect! BUT appeared to look ok with the default style where ItemSpacing.x == FramePadding.x * 2 (it didn't look aligned otherwise).
    484                            - new: TreeNode("##Hidden"); SameLine(0, 0); Text("Hello"); // <-- This is correct for all styles values.
    485                          with the fix, IF you were successfully using TreeNode("")+SameLine(); you will now have extra spacing between your TreeNode and the following item.
    486                          You'll need to change the SameLine() call to SameLine(0,0) to remove this extraneous spacing. This seemed like the more sensible fix that's not making things less consistent.
    487                          (Note: when using this idiom you are likely to also use ImGuiTreeNodeFlags_SpanAvailWidth).
    488  - 2024/03/18 (1.90.5) - merged the radius_x/radius_y parameters in ImDrawList::AddEllipse(), AddEllipseFilled() and PathEllipticalArcTo() into a single ImVec2 parameter. Exceptionally, because those functions were added in 1.90, we are not adding inline redirection functions. The transition is easy and should affect few users. (#2743, #7417)
    489  - 2024/03/08 (1.90.5) - inputs: more formally obsoleted GetKeyIndex() when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is set. It has been unnecessary and a no-op since 1.87 (it returns the same value as passed when used with a 1.87+ backend using io.AddKeyEvent() function). (#4921)
    490                            - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX)
    491  - 2024/01/15 (1.90.2) - commented out obsolete ImGuiIO::ImeWindowHandle marked obsolete in 1.87, favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'.
    492  - 2023/12/19 (1.90.1) - commented out obsolete ImGuiKey_KeyPadEnter redirection to ImGuiKey_KeypadEnter.
    493  - 2023/11/06 (1.90.1) - removed CalcListClipping() marked obsolete in 1.86. Prefer using ImGuiListClipper which can return non-contiguous ranges.
    494  - 2023/11/05 (1.90.1) - imgui_freetype: commented out ImGuiFreeType::BuildFontAtlas() obsoleted in 1.81. prefer using #define IMGUI_ENABLE_FREETYPE or see commented code for manual calls.
    495  - 2023/11/05 (1.90.1) - internals,columns: commented out legacy ImGuiColumnsFlags_XXX symbols redirecting to ImGuiOldColumnsFlags_XXX, obsoleted from imgui_internal.h in 1.80.
    496  - 2023/11/09 (1.90.0) - removed IM_OFFSETOF() macro in favor of using offsetof() available in C++11. Kept redirection define (will obsolete).
    497  - 2023/11/07 (1.90.0) - removed BeginChildFrame()/EndChildFrame() in favor of using BeginChild() with the ImGuiChildFlags_FrameStyle flag. kept inline redirection function (will obsolete).
    498                          those functions were merely PushStyle/PopStyle helpers, the removal isn't so much motivated by needing to add the feature in BeginChild(), but by the necessity to avoid BeginChildFrame() signature mismatching BeginChild() signature and features.
    499  - 2023/11/02 (1.90.0) - BeginChild: upgraded 'bool border = true' parameter to 'ImGuiChildFlags flags' type, added ImGuiChildFlags_Border equivalent. As with our prior "bool-to-flags" API updates, the ImGuiChildFlags_Border value is guaranteed to be == true forever to ensure a smoother transition, meaning all existing calls will still work.
    500                            - old: BeginChild("Name", size, true)
    501                            - new: BeginChild("Name", size, ImGuiChildFlags_Border)
    502                            - old: BeginChild("Name", size, false)
    503                            - new: BeginChild("Name", size) or BeginChild("Name", 0) or BeginChild("Name", size, ImGuiChildFlags_None)
    504  - 2023/11/02 (1.90.0) - BeginChild: added child-flag ImGuiChildFlags_AlwaysUseWindowPadding as a replacement for the window-flag ImGuiWindowFlags_AlwaysUseWindowPadding: the feature only ever made sense for BeginChild() anyhow.
    505                            - old: BeginChild("Name", size, 0, ImGuiWindowFlags_AlwaysUseWindowPadding);
    506                            - new: BeginChild("Name", size, ImGuiChildFlags_AlwaysUseWindowPadding, 0);
    507  - 2023/09/27 (1.90.0) - io: removed io.MetricsActiveAllocations introduced in 1.63. Same as 'g.DebugMemAllocCount - g.DebugMemFreeCount' (still displayed in Metrics, unlikely to be accessed by end-user).
    508  - 2023/09/26 (1.90.0) - debug tools: Renamed ShowStackToolWindow() ("Stack Tool") to ShowIDStackToolWindow() ("ID Stack Tool"), as earlier name was misleading. Kept inline redirection function. (#4631)
    509  - 2023/09/15 (1.90.0) - ListBox, Combo: changed signature of "name getter" callback in old one-liner ListBox()/Combo() apis. kept inline redirection function (will obsolete).
    510                            - old: bool Combo(const char* label, int* current_item, bool (*getter)(void* user_data, int idx, const char** out_text), ...)
    511                            - new: bool Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...);
    512                            - old: bool ListBox(const char* label, int* current_item, bool (*getting)(void* user_data, int idx, const char** out_text), ...);
    513                            - new: bool ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...);
    514  - 2023/09/08 (1.90.0) - commented out obsolete redirecting functions:
    515                            - GetWindowContentRegionWidth()  -> use GetWindowContentRegionMax().x - GetWindowContentRegionMin().x. Consider that generally 'GetContentRegionAvail().x' is more useful.
    516                            - ImDrawCornerFlags_XXX          -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details + grep commented names in sources.
    517                        - commented out runtime support for hardcoded ~0 or 0x01..0x0F rounding flags values for AddRect()/AddRectFilled()/PathRect()/AddImageRounded() -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details
    518  - 2023/08/25 (1.89.9) - clipper: Renamed IncludeRangeByIndices() (also called ForceDisplayRangeByIndices() before 1.89.6) to IncludeItemsByIndex(). Kept inline redirection function. Sorry!
    519  - 2023/07/12 (1.89.8) - ImDrawData: CmdLists now owned, changed from ImDrawList** to ImVector<ImDrawList*>. Majority of users shouldn't be affected, but you cannot compare to NULL nor reassign manually anymore. Instead use AddDrawList(). (#6406, #4879, #1878)
    520  - 2023/06/28 (1.89.7) - overlapping items: obsoleted 'SetItemAllowOverlap()' (called after item) in favor of calling 'SetNextItemAllowOverlap()' (called before item). 'SetItemAllowOverlap()' didn't and couldn't work reliably since 1.89 (2022-11-15).
    521  - 2023/06/28 (1.89.7) - overlapping items: renamed 'ImGuiTreeNodeFlags_AllowItemOverlap' to 'ImGuiTreeNodeFlags_AllowOverlap', 'ImGuiSelectableFlags_AllowItemOverlap' to 'ImGuiSelectableFlags_AllowOverlap'. Kept redirecting enums (will obsolete).
    522  - 2023/06/28 (1.89.7) - overlapping items: IsItemHovered() now by default return false when querying an item using AllowOverlap mode which is being overlapped. Use ImGuiHoveredFlags_AllowWhenOverlappedByItem to revert to old behavior.
    523  - 2023/06/28 (1.89.7) - overlapping items: Selectable and TreeNode don't allow overlap when active so overlapping widgets won't appear as hovered. While this fixes a common small visual issue, it also means that calling IsItemHovered() after a non-reactive elements - e.g. Text() - overlapping an active one may fail if you don't use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem). (#6610)
    524  - 2023/06/20 (1.89.7) - moved io.HoverDelayShort/io.HoverDelayNormal to style.HoverDelayShort/style.HoverDelayNormal. As the fields were added in 1.89 and expected to be left unchanged by most users, or only tweaked once during app initialization, we are exceptionally accepting the breakage.
    525  - 2023/05/30 (1.89.6) - backends: renamed "imgui_impl_sdlrenderer.cpp" to "imgui_impl_sdlrenderer2.cpp" and "imgui_impl_sdlrenderer.h" to "imgui_impl_sdlrenderer2.h". This is in prevision for the future release of SDL3.
    526  - 2023/05/22 (1.89.6) - listbox: commented out obsolete/redirecting functions that were marked obsolete more than two years ago:
    527                            - ListBoxHeader()  -> use BeginListBox() (note how two variants of ListBoxHeader() existed. Check commented versions in imgui.h for reference)
    528                            - ListBoxFooter()  -> use EndListBox()
    529  - 2023/05/15 (1.89.6) - clipper: commented out obsolete redirection constructor 'ImGuiListClipper(int items_count, float items_height = -1.0f)' that was marked obsolete in 1.79. Use default constructor + clipper.Begin().
    530  - 2023/05/15 (1.89.6) - clipper: renamed ImGuiListClipper::ForceDisplayRangeByIndices() to ImGuiListClipper::IncludeRangeByIndices().
    531  - 2023/03/14 (1.89.4) - commented out redirecting enums/functions names that were marked obsolete two years ago:
    532                            - ImGuiSliderFlags_ClampOnInput        -> use ImGuiSliderFlags_AlwaysClamp
    533                            - ImGuiInputTextFlags_AlwaysInsertMode -> use ImGuiInputTextFlags_AlwaysOverwrite
    534                            - ImDrawList::AddBezierCurve()         -> use ImDrawList::AddBezierCubic()
    535                            - ImDrawList::PathBezierCurveTo()      -> use ImDrawList::PathBezierCubicCurveTo()
    536  - 2023/03/09 (1.89.4) - renamed PushAllowKeyboardFocus()/PopAllowKeyboardFocus() to PushTabStop()/PopTabStop(). Kept inline redirection functions (will obsolete).
    537  - 2023/03/09 (1.89.4) - tooltips: Added 'bool' return value to BeginTooltip() for API consistency. Please only submit contents and call EndTooltip() if BeginTooltip() returns true. In reality the function will _currently_ always return true, but further changes down the line may change this, best to clarify API sooner.
    538  - 2023/02/15 (1.89.4) - moved the optional "courtesy maths operators" implementation from imgui_internal.h in imgui.h.
    539                          Even though we encourage using your own maths types and operators by setting up IM_VEC2_CLASS_EXTRA,
    540                          it has been frequently requested by people to use our own. We had an opt-in define which was
    541                          previously fulfilled in imgui_internal.h. It is now fulfilled in imgui.h. (#6164)
    542                            - OK:     #define IMGUI_DEFINE_MATH_OPERATORS / #include "imgui.h" / #include "imgui_internal.h"
    543                            - Error:  #include "imgui.h" / #define IMGUI_DEFINE_MATH_OPERATORS / #include "imgui_internal.h"
    544  - 2023/02/07 (1.89.3) - backends: renamed "imgui_impl_sdl.cpp" to "imgui_impl_sdl2.cpp" and "imgui_impl_sdl.h" to "imgui_impl_sdl2.h". (#6146) This is in prevision for the future release of SDL3.
    545  - 2022/10/26 (1.89)   - commented out redirecting OpenPopupContextItem() which was briefly the name of OpenPopupOnItemClick() from 1.77 to 1.79.
    546  - 2022/10/12 (1.89)   - removed runtime patching of invalid "%f"/"%0.f" format strings for DragInt()/SliderInt(). This was obsoleted in 1.61 (May 2018). See 1.61 changelog for details.
    547  - 2022/09/26 (1.89)   - renamed and merged keyboard modifiers key enums and flags into a same set. Kept inline redirection enums (will obsolete).
    548                            - ImGuiKey_ModCtrl  and ImGuiModFlags_Ctrl  -> ImGuiMod_Ctrl
    549                            - ImGuiKey_ModShift and ImGuiModFlags_Shift -> ImGuiMod_Shift
    550                            - ImGuiKey_ModAlt   and ImGuiModFlags_Alt   -> ImGuiMod_Alt
    551                            - ImGuiKey_ModSuper and ImGuiModFlags_Super -> ImGuiMod_Super
    552                          the ImGuiKey_ModXXX were introduced in 1.87 and mostly used by backends.
    553                          the ImGuiModFlags_XXX have been exposed in imgui.h but not really used by any public api only by third-party extensions.
    554                          exceptionally commenting out the older ImGuiKeyModFlags_XXX names ahead of obsolescence schedule to reduce confusion and because they were not meant to be used anyway.
    555  - 2022/09/20 (1.89)   - ImGuiKey is now a typed enum, allowing ImGuiKey_XXX symbols to be named in debuggers.
    556                          this will require uses of legacy backend-dependent indices to be casted, e.g.
    557                             - with imgui_impl_glfw:  IsKeyPressed(GLFW_KEY_A) -> IsKeyPressed((ImGuiKey)GLFW_KEY_A);
    558                             - with imgui_impl_win32: IsKeyPressed('A')        -> IsKeyPressed((ImGuiKey)'A')
    559                             - etc. However if you are upgrading code you might well use the better, backend-agnostic IsKeyPressed(ImGuiKey_A) now!
    560  - 2022/09/12 (1.89) - removed the bizarre legacy default argument for 'TreePush(const void* ptr = NULL)', always pass a pointer value explicitly. NULL/nullptr is ok but require cast, e.g. TreePush((void*)nullptr);
    561  - 2022/09/05 (1.89) - commented out redirecting functions/enums names that were marked obsolete in 1.77 and 1.78 (June 2020):
    562                          - DragScalar(), DragScalarN(), DragFloat(), DragFloat2(), DragFloat3(), DragFloat4(): For old signatures ending with (..., const char* format, float power = 1.0f) -> use (..., format ImGuiSliderFlags_Logarithmic) if power != 1.0f.
    563                          - SliderScalar(), SliderScalarN(), SliderFloat(), SliderFloat2(), SliderFloat3(), SliderFloat4(): For old signatures ending with (..., const char* format, float power = 1.0f) -> use (..., format ImGuiSliderFlags_Logarithmic) if power != 1.0f.
    564                          - BeginPopupContextWindow(const char*, ImGuiMouseButton, bool) -> use BeginPopupContextWindow(const char*, ImGuiPopupFlags)
    565  - 2022/09/02 (1.89) - obsoleted using SetCursorPos()/SetCursorScreenPos() to extend parent window/cell boundaries.
    566                        this relates to when moving the cursor position beyond current boundaries WITHOUT submitting an item.
    567                          - previously this would make the window content size ~200x200:
    568                               Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End();
    569                          - instead, please submit an item:
    570                               Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End();
    571                          - alternative:
    572                               Begin(...) + Dummy(ImVec2(200,200)) + End();
    573                          - content size is now only extended when submitting an item!
    574                          - with '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will now be detected and assert.
    575                          - without '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will silently be fixed until we obsolete it.
    576  - 2022/08/03 (1.89) - changed signature of ImageButton() function. Kept redirection function (will obsolete).
    577                         - added 'const char* str_id' parameter + removed 'int frame_padding = -1' parameter.
    578                         - old signature: bool ImageButton(ImTextureID tex_id, ImVec2 size, ImVec2 uv0 = ImVec2(0,0), ImVec2 uv1 = ImVec2(1,1), int frame_padding = -1, ImVec4 bg_col = ImVec4(0,0,0,0), ImVec4 tint_col = ImVec4(1,1,1,1));
    579                           - used the ImTextureID value to create an ID. This was inconsistent with other functions, led to ID conflicts, and caused problems with engines using transient ImTextureID values.
    580                           - had a FramePadding override which was inconsistent with other functions and made the already-long signature even longer.
    581                         - new signature: bool ImageButton(const char* str_id, ImTextureID tex_id, ImVec2 size, ImVec2 uv0 = ImVec2(0,0), ImVec2 uv1 = ImVec2(1,1), ImVec4 bg_col = ImVec4(0,0,0,0), ImVec4 tint_col = ImVec4(1,1,1,1));
    582                           - requires an explicit identifier. You may still use e.g. PushID() calls and then pass an empty identifier.
    583                           - always uses style.FramePadding for padding, to be consistent with other buttons. You may use PushStyleVar() to alter this.
    584  - 2022/07/08 (1.89) - inputs: removed io.NavInputs[] and ImGuiNavInput enum (following 1.87 changes).
    585                         - Official backends from 1.87+                  -> no issue.
    586                         - Official backends from 1.60 to 1.86           -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need updating!
    587                         - Custom backends not writing to io.NavInputs[] -> no issue.
    588                         - Custom backends writing to io.NavInputs[]     -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need fixing!
    589                         - TL;DR: Backends should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values instead of filling io.NavInput[].
    590  - 2022/06/15 (1.88) - renamed IMGUI_DISABLE_METRICS_WINDOW to IMGUI_DISABLE_DEBUG_TOOLS for correctness. kept support for old define (will obsolete).
    591  - 2022/05/03 (1.88) - backends: osx: removed ImGui_ImplOSX_HandleEvent() from backend API in favor of backend automatically handling event capture. All ImGui_ImplOSX_HandleEvent() calls should be removed as they are now unnecessary.
    592  - 2022/04/05 (1.88) - inputs: renamed ImGuiKeyModFlags to ImGuiModFlags. Kept inline redirection enums (will obsolete). This was never used in public API functions but technically present in imgui.h and ImGuiIO.
    593  - 2022/01/20 (1.87) - inputs: reworded gamepad IO.
    594                         - Backend writing to io.NavInputs[]            -> backend should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values.
    595  - 2022/01/19 (1.87) - sliders, drags: removed support for legacy arithmetic operators (+,+-,*,/) when inputing text. This doesn't break any api/code but a feature that used to be accessible by end-users (which seemingly no one used).
    596  - 2022/01/17 (1.87) - inputs: reworked mouse IO.
    597                         - Backend writing to io.MousePos               -> backend should call io.AddMousePosEvent()
    598                         - Backend writing to io.MouseDown[]            -> backend should call io.AddMouseButtonEvent()
    599                         - Backend writing to io.MouseWheel             -> backend should call io.AddMouseWheelEvent()
    600                         - Backend writing to io.MouseHoveredViewport   -> backend should call io.AddMouseViewportEvent() [Docking branch w/ multi-viewports only]
    601                        note: for all calls to IO new functions, the Dear ImGui context should be bound/current.
    602                        read https://github.com/ocornut/imgui/issues/4921 for details.
    603  - 2022/01/10 (1.87) - inputs: reworked keyboard IO. Removed io.KeyMap[], io.KeysDown[] in favor of calling io.AddKeyEvent(). Removed GetKeyIndex(), now unnecessary. All IsKeyXXX() functions now take ImGuiKey values. All features are still functional until IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Read Changelog and Release Notes for details.
    604                         - IsKeyPressed(MY_NATIVE_KEY_XXX)              -> use IsKeyPressed(ImGuiKey_XXX)
    605                         - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX))      -> use IsKeyPressed(ImGuiKey_XXX)
    606                         - Backend writing to io.KeyMap[],io.KeysDown[] -> backend should call io.AddKeyEvent() (+ call io.SetKeyEventNativeData() if you want legacy user code to stil function with legacy key codes).
    607                         - Backend writing to io.KeyCtrl, io.KeyShift.. -> backend should call io.AddKeyEvent() with ImGuiMod_XXX values. *IF YOU PULLED CODE BETWEEN 2021/01/10 and 2021/01/27: We used to have a io.AddKeyModsEvent() function which was now replaced by io.AddKeyEvent() with ImGuiMod_XXX values.*
    608                      - one case won't work with backward compatibility: if your custom backend used ImGuiKey as mock native indices (e.g. "io.KeyMap[ImGuiKey_A] = ImGuiKey_A") because those values are now larger than the legacy KeyDown[] array. Will assert.
    609                      - inputs: added ImGuiKey_ModCtrl/ImGuiKey_ModShift/ImGuiKey_ModAlt/ImGuiKey_ModSuper values to submit keyboard modifiers using io.AddKeyEvent(), instead of writing directly to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper.
    610  - 2022/01/05 (1.87) - inputs: renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum.
    611  - 2022/01/05 (1.87) - removed io.ImeSetInputScreenPosFn() in favor of more flexible io.SetPlatformImeDataFn(). Removed 'void* io.ImeWindowHandle' in favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'.
    612  - 2022/01/01 (1.87) - commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019)
    613                         - ImGui::SetNextTreeNodeOpen()        -> use ImGui::SetNextItemOpen()
    614                         - ImGui::GetContentRegionAvailWidth() -> use ImGui::GetContentRegionAvail().x
    615                         - ImGui::TreeAdvanceToLabelPos()      -> use ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetTreeNodeToLabelSpacing());
    616                         - ImFontAtlas::CustomRect             -> use ImFontAtlasCustomRect
    617                         - ImGuiColorEditFlags_RGB/HSV/HEX     -> use ImGuiColorEditFlags_DisplayRGB/HSV/Hex
    618  - 2021/12/20 (1.86) - backends: removed obsolete Marmalade backend (imgui_impl_marmalade.cpp) + example. Find last supported version at https://github.com/ocornut/imgui/wiki/Bindings
    619  - 2021/11/04 (1.86) - removed CalcListClipping() function. Prefer using ImGuiListClipper which can return non-contiguous ranges. Please open an issue if you think you really need this function.
    620  - 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead for generally 'GetContentRegionAvail().x' is more useful.
    621  - 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019):
    622                         - ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList()
    623                         - ImFont::GlyphRangesBuilder  -> use ImFontGlyphRangesBuilder
    624  - 2021/05/19 (1.83) - backends: obsoleted direct access to ImDrawCmd::TextureId in favor of calling ImDrawCmd::GetTexID().
    625                         - if you are using official backends from the source tree: you have nothing to do.
    626                         - if you have copied old backend code or using your own: change access to draw_cmd->TextureId to draw_cmd->GetTexID().
    627  - 2021/03/12 (1.82) - upgraded ImDrawList::AddRect(), AddRectFilled(), PathRect() to use ImDrawFlags instead of ImDrawCornersFlags.
    628                         - ImDrawCornerFlags_TopLeft  -> use ImDrawFlags_RoundCornersTopLeft
    629                         - ImDrawCornerFlags_BotRight -> use ImDrawFlags_RoundCornersBottomRight
    630                         - ImDrawCornerFlags_None     -> use ImDrawFlags_RoundCornersNone etc.
    631                        flags now sanely defaults to 0 instead of 0x0F, consistent with all other flags in the API.
    632                        breaking: the default with rounding > 0.0f is now "round all corners" vs old implicit "round no corners":
    633                         - rounding == 0.0f + flags == 0 --> meant no rounding  --> unchanged (common use)
    634                         - rounding  > 0.0f + flags != 0 --> meant rounding     --> unchanged (common use)
    635                         - rounding == 0.0f + flags != 0 --> meant no rounding  --> unchanged (unlikely use)
    636                         - rounding  > 0.0f + flags == 0 --> meant no rounding  --> BREAKING (unlikely use): will now round all corners --> use ImDrawFlags_RoundCornersNone or rounding == 0.0f.
    637                        this ONLY matters for hard coded use of 0 + rounding > 0.0f. Use of named ImDrawFlags_RoundCornersNone (new) or ImDrawCornerFlags_None (old) are ok.
    638                        the old ImDrawCornersFlags used awkward default values of ~0 or 0xF (4 lower bits set) to signify "round all corners" and we sometimes encouraged using them as shortcuts.
    639                        legacy path still support use of hard coded ~0 or any value from 0x1 or 0xF. They will behave the same with legacy paths enabled (will assert otherwise).
    640  - 2021/03/11 (1.82) - removed redirecting functions/enums names that were marked obsolete in 1.66 (September 2018):
    641                         - ImGui::SetScrollHere()              -> use ImGui::SetScrollHereY()
    642  - 2021/03/11 (1.82) - clarified that ImDrawList::PathArcTo(), ImDrawList::PathArcToFast() won't render with radius < 0.0f. Previously it sorts of accidentally worked but would generally lead to counter-clockwise paths and have an effect on anti-aliasing.
    643  - 2021/03/10 (1.82) - upgraded ImDrawList::AddPolyline() and PathStroke() "bool closed" parameter to "ImDrawFlags flags". The matching ImDrawFlags_Closed value is guaranteed to always stay == 1 in the future.
    644  - 2021/02/22 (1.82) - (*undone in 1.84*) win32+mingw: Re-enabled IME functions by default even under MinGW. In July 2016, issue #738 had me incorrectly disable those default functions for MinGW. MinGW users should: either link with -limm32, either set their imconfig file  with '#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS'.
    645  - 2021/02/17 (1.82) - renamed rarely used style.CircleSegmentMaxError (old default = 1.60f) to style.CircleTessellationMaxError (new default = 0.30f) as the meaning of the value changed.
    646  - 2021/02/03 (1.81) - renamed ListBoxHeader(const char* label, ImVec2 size) to BeginListBox(). Kept inline redirection function (will obsolete).
    647                      - removed ListBoxHeader(const char* label, int items_count, int height_in_items = -1) in favor of specifying size. Kept inline redirection function (will obsolete).
    648                      - renamed ListBoxFooter() to EndListBox(). Kept inline redirection function (will obsolete).
    649  - 2021/01/26 (1.81) - removed ImGuiFreeType::BuildFontAtlas(). Kept inline redirection function. Prefer using '#define IMGUI_ENABLE_FREETYPE', but there's a runtime selection path available too. The shared extra flags parameters (very rarely used) are now stored in ImFontAtlas::FontBuilderFlags.
    650                      - renamed ImFontConfig::RasterizerFlags (used by FreeType) to ImFontConfig::FontBuilderFlags.
    651                      - renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API.
    652  - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.63 (August 2018):
    653                         - ImGui::IsItemDeactivatedAfterChange() -> use ImGui::IsItemDeactivatedAfterEdit().
    654                         - ImGuiCol_ModalWindowDarkening       -> use ImGuiCol_ModalWindowDimBg
    655                         - ImGuiInputTextCallback              -> use ImGuiTextEditCallback
    656                         - ImGuiInputTextCallbackData          -> use ImGuiTextEditCallbackData
    657  - 2020/12/21 (1.80) - renamed ImDrawList::AddBezierCurve() to AddBezierCubic(), and PathBezierCurveTo() to PathBezierCubicCurveTo(). Kept inline redirection function (will obsolete).
    658  - 2020/12/04 (1.80) - added imgui_tables.cpp file! Manually constructed project files will need the new file added!
    659  - 2020/11/18 (1.80) - renamed undocumented/internals ImGuiColumnsFlags_* to ImGuiOldColumnFlags_* in prevision of incoming Tables API.
    660  - 2020/11/03 (1.80) - renamed io.ConfigWindowsMemoryCompactTimer to io.ConfigMemoryCompactTimer as the feature will apply to other data structures
    661  - 2020/10/14 (1.80) - backends: moved all backends files (imgui_impl_XXXX.cpp, imgui_impl_XXXX.h) from examples/ to backends/.
    662  - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.60 (April 2018):
    663                         - io.RenderDrawListsFn pointer        -> use ImGui::GetDrawData() value and call the render function of your backend
    664                         - ImGui::IsAnyWindowFocused()         -> use ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)
    665                         - ImGui::IsAnyWindowHovered()         -> use ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
    666                         - ImGuiStyleVar_Count_                -> use ImGuiStyleVar_COUNT
    667                         - ImGuiMouseCursor_Count_             -> use ImGuiMouseCursor_COUNT
    668                       - removed redirecting functions names that were marked obsolete in 1.61 (May 2018):
    669                         - InputFloat (... int decimal_precision ...) -> use InputFloat (... const char* format ...) with format = "%.Xf" where X is your value for decimal_precision.
    670                         - same for InputFloat2()/InputFloat3()/InputFloat4() variants taking a `int decimal_precision` parameter.
    671  - 2020/10/05 (1.79) - removed ImGuiListClipper: Renamed constructor parameters which created an ambiguous alternative to using the ImGuiListClipper::Begin() function, with misleading edge cases (note: imgui_memory_editor <0.40 from imgui_club/ used this old clipper API. Update your copy if needed).
    672  - 2020/09/25 (1.79) - renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. Kept redirection enum (will obsolete sooner because previous name was added recently).
    673  - 2020/09/25 (1.79) - renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton.
    674  - 2020/09/21 (1.79) - renamed OpenPopupContextItem() back to OpenPopupOnItemClick(), reverting the change from 1.77. For varieties of reason this is more self-explanatory.
    675  - 2020/09/21 (1.79) - removed return value from OpenPopupOnItemClick() - returned true on mouse release on an item - because it is inconsistent with other popup APIs and makes others misleading. It's also and unnecessary: you can use IsWindowAppearing() after BeginPopup() for a similar result.
    676  - 2020/09/17 (1.79) - removed ImFont::DisplayOffset in favor of ImFontConfig::GlyphOffset. DisplayOffset was applied after scaling and not very meaningful/useful outside of being needed by the default ProggyClean font. If you scaled this value after calling AddFontDefault(), this is now done automatically. It was also getting in the way of better font scaling, so let's get rid of it now!
    677  - 2020/08/17 (1.78) - obsoleted use of the trailing 'float power=1.0f' parameter for DragFloat(), DragFloat2(), DragFloat3(), DragFloat4(), DragFloatRange2(), DragScalar(), DragScalarN(), SliderFloat(), SliderFloat2(), SliderFloat3(), SliderFloat4(), SliderScalar(), SliderScalarN(), VSliderFloat() and VSliderScalar().
    678                        replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags).
    679                        worked out a backward-compatibility scheme so hopefully most C++ codebase should not be affected. in short, when calling those functions:
    680                        - if you omitted the 'power' parameter (likely!), you are not affected.
    681                        - if you set the 'power' parameter to 1.0f (same as previous default value): 1/ your compiler may warn on float>int conversion, 2/ everything else will work. 3/ you can replace the 1.0f value with 0 to fix the warning, and be technically correct.
    682                        - if you set the 'power' parameter to >1.0f (to enable non-linear editing): 1/ your compiler may warn on float>int conversion, 2/ code will assert at runtime, 3/ in case asserts are disabled, the code will not crash and enable the _Logarithmic flag. 4/ you can replace the >1.0f value with ImGuiSliderFlags_Logarithmic to fix the warning/assert and get a _similar_ effect as previous uses of power >1.0f.
    683                        see https://github.com/ocornut/imgui/issues/3361 for all details.
    684                        kept inline redirection functions (will obsolete) apart for: DragFloatRange2(), VSliderFloat(), VSliderScalar(). For those three the 'float power=1.0f' version was removed directly as they were most unlikely ever used.
    685                        for shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`.
    686                      - obsoleted use of v_min > v_max in DragInt, DragFloat, DragScalar to lock edits (introduced in 1.73, was not demoed nor documented very), will be replaced by a more generic ReadOnly feature. You may use the ImGuiSliderFlags_ReadOnly internal flag in the meantime.
    687  - 2020/06/23 (1.77) - removed BeginPopupContextWindow(const char*, int mouse_button, bool also_over_items) in favor of BeginPopupContextWindow(const char*, ImGuiPopupFlags flags) with ImGuiPopupFlags_NoOverItems.
    688  - 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete). [NOTE: THIS WAS REVERTED IN 1.79]
    689  - 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017.
    690  - 2020/04/23 (1.77) - removed unnecessary ID (first arg) of ImFontAtlas::AddCustomRectRegular().
    691  - 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more.
    692  - 2019/12/17 (1.75) - [undid this change in 1.76] made Columns() limited to 64 columns by asserting above that limit. While the current code technically supports it, future code may not so we're putting the restriction ahead.
    693  - 2019/12/13 (1.75) - [imgui_internal.h] changed ImRect() default constructor initializes all fields to 0.0f instead of (FLT_MAX,FLT_MAX,-FLT_MAX,-FLT_MAX). If you used ImRect::Add() to create bounding boxes by adding multiple points into it, you may need to fix your initial value.
    694  - 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017):
    695                        - ShowTestWindow()                    -> use ShowDemoWindow()
    696                        - IsRootWindowFocused()               -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow)
    697                        - IsRootWindowOrAnyChildFocused()     -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
    698                        - SetNextWindowContentWidth(w)        -> use SetNextWindowContentSize(ImVec2(w, 0.0f)
    699                        - GetItemsLineHeightWithSpacing()     -> use GetFrameHeightWithSpacing()
    700                        - ImGuiCol_ChildWindowBg              -> use ImGuiCol_ChildBg
    701                        - ImGuiStyleVar_ChildWindowRounding   -> use ImGuiStyleVar_ChildRounding
    702                        - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap
    703                        - IMGUI_DISABLE_TEST_WINDOWS          -> use IMGUI_DISABLE_DEMO_WINDOWS
    704  - 2019/12/08 (1.75) - obsoleted calling ImDrawList::PrimReserve() with a negative count (which was vaguely documented and rarely if ever used). Instead, we added an explicit PrimUnreserve() API.
    705  - 2019/12/06 (1.75) - removed implicit default parameter to IsMouseDragging(int button = 0) to be consistent with other mouse functions (none of the other functions have it).
    706  - 2019/11/21 (1.74) - ImFontAtlas::AddCustomRectRegular() now requires an ID larger than 0x110000 (instead of 0x10000) to conform with supporting Unicode planes 1-16 in a future update. ID below 0x110000 will now assert.
    707  - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency.
    708  - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency.
    709  - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017):
    710                        - Begin() [old 5 args version]        -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed
    711                        - IsRootWindowOrAnyChildHovered()     -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows)
    712                        - AlignFirstTextHeightToWidgets()     -> use AlignTextToFramePadding()
    713                        - SetNextWindowPosCenter()            -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f)
    714                        - ImFont::Glyph                       -> use ImFontGlyph
    715  - 2019/10/14 (1.74) - inputs: Fixed a miscalculation in the keyboard/mouse "typematic" repeat delay/rate calculation, used by keys and e.g. repeating mouse buttons as well as the GetKeyPressedAmount() function.
    716                        if you were using a non-default value for io.KeyRepeatRate (previous default was 0.250), you can add +io.KeyRepeatDelay to it to compensate for the fix.
    717                        The function was triggering on: 0.0 and (delay+rate*N) where (N>=1). Fixed formula responds to (N>=0). Effectively it made io.KeyRepeatRate behave like it was set to (io.KeyRepeatRate + io.KeyRepeatDelay).
    718                        If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you.
    719  - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete).
    720  - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete).
    721  - 2019/06/14 (1.72) - removed redirecting functions/enums names that were marked obsolete in 1.51 (June 2017): ImGuiCol_Column*, ImGuiSetCond_*, IsItemHoveredRect(), IsPosHoveringAnyWindow(), IsMouseHoveringAnyWindow(), IsMouseHoveringWindow(), IMGUI_ONCE_UPON_A_FRAME. Grep this log for details and new names, or see how they were implemented until 1.71.
    722  - 2019/06/07 (1.71) - rendering of child window outer decorations (bg color, border, scrollbars) is now performed as part of the parent window. If you have
    723                        overlapping child windows in a same parent, and relied on their relative z-order to be mapped to their submission order, this will affect your rendering.
    724                        This optimization is disabled if the parent window has no visual output, because it appears to be the most common situation leading to the creation of overlapping child windows.
    725                        Please reach out if you are affected.
    726  - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete).
    727  - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c).
    728  - 2019/04/29 (1.70) - improved ImDrawList thick strokes (>1.0f) preserving correct thickness up to 90 degrees angles (e.g. rectangles). If you have custom rendering using thick lines, they will appear thicker now.
    729  - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).
    730  - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).
    731  - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).
    732  - 2019/02/14 (1.68) - made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). If for some reason your time step calculation gives you a zero value, replace it with an arbitrarily small value!
    733  - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
    734  - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
    735  - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).
    736  - 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.
    737  - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
    738  - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
    739  - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
    740  - 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.
    741                        If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
    742  - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
    743  - 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.
    744                        NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
    745                        Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
    746  - 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).
    747  - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
    748  - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
    749  - 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.
    750  - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
    751  - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
    752  - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
    753  - 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.).
    754                        old backends will still work as is, however prefer using the separated backends as they will be updated to support multi-viewports.
    755                        when adopting new backends follow the main.cpp code of your preferred examples/ folder to know which functions to call.
    756                        in particular, note that old backends called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function.
    757  - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
    758  - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
    759  - 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.
    760                        If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
    761                        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.
    762                        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.
    763  - 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",
    764                        consistent with other functions. Kept redirection functions (will obsolete).
    765  - 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.
    766  - 2018/03/20 (1.60) - renamed io.WantMoveMouse to io.WantSetMousePos for consistency and ease of understanding (was added in 1.52, _not_ used by core and only honored by some backend ahead of merging the Nav branch).
    767  - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
    768  - 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.
    769  - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
    770  - 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.
    771  - 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.
    772  - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
    773                        - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
    774                        - removed Shutdown() function, as DestroyContext() serve this purpose.
    775                        - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
    776                        - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
    777                        - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
    778  - 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.
    779  - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
    780  - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
    781  - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
    782  - 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.
    783  - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
    784  - 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
    785  - 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.
    786  - 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.
    787  - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
    788  - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
    789                      - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
    790  - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
    791  - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
    792  - 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.
    793  - 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.
    794                        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.
    795  - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
    796  - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
    797  - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
    798  - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
    799  - 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.
    800  - 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.
    801  - 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.
    802                        removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
    803                          IsItemHoveredRect()        --> IsItemHovered(ImGuiHoveredFlags_RectOnly)
    804                          IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
    805                          IsMouseHoveringWindow()    --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior]
    806  - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
    807  - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
    808  - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).
    809  - 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).
    810  - 2017/08/25 (1.52) - io.MousePos needs to be set to ImVec2(-FLT_MAX,-FLT_MAX) when mouse is unavailable/missing. Previously ImVec2(-1,-1) was enough but we now accept negative mouse coordinates. In your backend if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)".
    811  - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
    812                      - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
    813                      - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
    814  - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
    815  - 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.
    816  - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type.
    817  - 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.
    818  - 2017/08/13 (1.51) - renamed ImGuiCol_Column to ImGuiCol_Separator, ImGuiCol_ColumnHovered to ImGuiCol_SeparatorHovered, ImGuiCol_ColumnActive to ImGuiCol_SeparatorActive. Kept redirection enums (will obsolete).
    819  - 2017/08/11 (1.51) - renamed ImGuiSetCond_Always to ImGuiCond_Always, ImGuiSetCond_Once to ImGuiCond_Once, ImGuiSetCond_FirstUseEver to ImGuiCond_FirstUseEver, ImGuiSetCond_Appearing to ImGuiCond_Appearing. Kept redirection enums (will obsolete).
    820  - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
    821  - 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.
    822                      - 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.
    823                      - 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))'
    824  - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
    825  - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
    826  - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
    827  - 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().
    828  - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
    829  - 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.
    830  - 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.
    831  - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
    832                        If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you, otherwise if <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
    833                        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:
    834                        ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col) { float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a; return ImVec4((win_bg_col.x * win_bg_col.w + title_bg_col.x) * k, (win_bg_col.y * win_bg_col.w + title_bg_col.y) * k, (win_bg_col.z * win_bg_col.w + title_bg_col.z) * k, new_a); }
    835                        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.
    836  - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
    837  - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
    838  - 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).
    839  - 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.
    840  - 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).
    841  - 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)
    842  - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
    843  - 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.
    844  - 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.
    845  - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
    846  - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
    847  - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
    848                        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.
    849                        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!
    850  - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
    851  - 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.
    852  - 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
    853  - 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.
    854                        you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
    855  - 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.
    856                        this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
    857                      - if you are using a vanilla copy of one of the imgui_impl_XXX.cpp provided in the example, you just need to update your copy and you can ignore the rest.
    858                      - the signature of the io.RenderDrawListsFn handler has changed!
    859                        old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
    860                        new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
    861                          parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
    862                          ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
    863                          ImDrawCmd:  'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
    864                      - 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.
    865                      - 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!
    866                      - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
    867  - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
    868  - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
    869  - 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.
    870  - 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
    871  - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely used. Sorry!
    872  - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
    873  - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
    874  - 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.
    875  - 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.
    876  - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
    877  - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
    878  - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
    879  - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
    880  - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
    881  - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
    882  - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
    883  - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
    884  - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
    885  - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
    886  - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
    887  - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
    888  - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
    889  - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
    890  - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
    891  - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
    892  - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
    893  - 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
    894                        - old:  const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..];
    895                        - new:  unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->SetTexID(YourTexIdentifier);
    896                        you now have more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs. It is now recommended that you sample the font texture with bilinear interpolation.
    897  - 2015/01/11 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to call io.Fonts->SetTexID()
    898  - 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
    899  - 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
    900  - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
    901  - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
    902  - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
    903  - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
    904  - 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)
    905  - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
    906  - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
    907  - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
    908  - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
    909  - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
    910  - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
    911 
    912 
    913  FREQUENTLY ASKED QUESTIONS (FAQ)
    914  ================================
    915 
    916  Read all answers online:
    917    https://www.dearimgui.com/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url)
    918  Read all answers locally (with a text editor or ideally a Markdown viewer):
    919    docs/FAQ.md
    920  Some answers are copied down here to facilitate searching in code.
    921 
    922  Q&A: Basics
    923  ===========
    924 
    925  Q: Where is the documentation?
    926  A: This library is poorly documented at the moment and expects the user to be acquainted with C/C++.
    927     - Run the examples/ applications and explore them.
    928     - Read Getting Started (https://github.com/ocornut/imgui/wiki/Getting-Started) guide.
    929     - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function.
    930     - The demo covers most features of Dear ImGui, so you can read the code and see its output.
    931     - See documentation and comments at the top of imgui.cpp + effectively imgui.h.
    932     - 20+ standalone example applications using e.g. OpenGL/DirectX are provided in the
    933       examples/ folder to explain how to integrate Dear ImGui with your own engine/application.
    934     - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links.
    935     - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful.
    936     - Your programming IDE is your friend, find the type or function declaration to find comments
    937       associated with it.
    938 
    939  Q: What is this library called?
    940  Q: Which version should I get?
    941  >> This library is called "Dear ImGui", please don't call it "ImGui" :)
    942  >> See https://www.dearimgui.com/faq for details.
    943 
    944  Q&A: Integration
    945  ================
    946 
    947  Q: How to get started?
    948  A: Read https://github.com/ocornut/imgui/wiki/Getting-Started. Read 'PROGRAMMER GUIDE' above. Read examples/README.txt.
    949 
    950  Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?
    951  A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
    952  >> See https://www.dearimgui.com/faq for a fully detailed answer. You really want to read this.
    953 
    954  Q. How can I enable keyboard or gamepad controls?
    955  Q: How can I use this on a machine without mouse, keyboard or screen? (input share, remote display)
    956  Q: I integrated Dear ImGui in my engine and little squares are showing instead of text...
    957  Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around...
    958  Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries...
    959  >> See https://www.dearimgui.com/faq
    960 
    961  Q&A: Usage
    962  ----------
    963 
    964  Q: About the ID Stack system..
    965    - Why is my widget not reacting when I click on it?
    966    - How can I have widgets with an empty label?
    967    - How can I have multiple widgets with the same label?
    968    - How can I have multiple windows with the same label?
    969  Q: How can I display an image? What is ImTextureID, how does it work?
    970  Q: How can I use my own math types instead of ImVec2?
    971  Q: How can I interact with standard C++ types (such as std::string and std::vector)?
    972  Q: How can I display custom shapes? (using low-level ImDrawList API)
    973  >> See https://www.dearimgui.com/faq
    974 
    975  Q&A: Fonts, Text
    976  ================
    977 
    978  Q: How should I handle DPI in my application?
    979  Q: How can I load a different font than the default?
    980  Q: How can I easily use icons in my application?
    981  Q: How can I load multiple fonts?
    982  Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
    983  >> See https://www.dearimgui.com/faq and https://github.com/ocornut/imgui/blob/master/docs/FONTS.md
    984 
    985  Q&A: Concerns
    986  =============
    987 
    988  Q: Who uses Dear ImGui?
    989  Q: Can you create elaborate/serious tools with Dear ImGui?
    990  Q: Can you reskin the look of Dear ImGui?
    991  Q: Why using C++ (as opposed to C)?
    992  >> See https://www.dearimgui.com/faq
    993 
    994  Q&A: Community
    995  ==============
    996 
    997  Q: How can I help?
    998  A: - Businesses: please reach out to "omar AT dearimgui DOT com" if you work in a place using Dear ImGui!
    999       We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts.
   1000       This is among the most useful thing you can do for Dear ImGui. With increased funding, we sustain and grow work on this project.
   1001       >>> See https://github.com/ocornut/imgui/wiki/Funding
   1002     - Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine.
   1003     - If you are experienced with Dear ImGui and C++, look at the GitHub issues, look at the Wiki, and see how you want to help and can help!
   1004     - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
   1005       You may post screenshot or links in the gallery threads. Visuals are ideal as they inspire other programmers.
   1006       But even without visuals, disclosing your use of dear imgui helps the library grow credibility, and help other teams and programmers with taking decisions.
   1007     - 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).
   1008 
   1009 */
   1010 
   1011 //-------------------------------------------------------------------------
   1012 // [SECTION] INCLUDES
   1013 //-------------------------------------------------------------------------
   1014 
   1015 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
   1016 #define _CRT_SECURE_NO_WARNINGS
   1017 #endif
   1018 
   1019 #ifndef IMGUI_DEFINE_MATH_OPERATORS
   1020 #define IMGUI_DEFINE_MATH_OPERATORS
   1021 #endif
   1022 
   1023 #include "imgui.h"
   1024 #ifndef IMGUI_DISABLE
   1025 #include "imgui_internal.h"
   1026 
   1027 // System includes
   1028 #include <stdio.h>      // vsnprintf, sscanf, printf
   1029 #include <stdint.h>     // intptr_t
   1030 
   1031 // [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled
   1032 #if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
   1033 #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
   1034 #endif
   1035 
   1036 // [Windows] OS specific includes (optional)
   1037 #if defined(_WIN32) && defined(IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) && defined(IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
   1038 #define IMGUI_DISABLE_WIN32_FUNCTIONS
   1039 #endif
   1040 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
   1041 #ifndef WIN32_LEAN_AND_MEAN
   1042 #define WIN32_LEAN_AND_MEAN
   1043 #endif
   1044 #ifndef NOMINMAX
   1045 #define NOMINMAX
   1046 #endif
   1047 #ifndef __MINGW32__
   1048 #include <Windows.h>        // _wfopen, OpenClipboard
   1049 #else
   1050 #include <windows.h>
   1051 #endif
   1052 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP || WINAPI_FAMILY == WINAPI_FAMILY_GAMES)
   1053 // The UWP and GDK Win32 API subsets don't support clipboard nor IME functions
   1054 #define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
   1055 #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
   1056 #define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
   1057 #endif
   1058 #endif
   1059 
   1060 // [Apple] OS specific includes
   1061 #if defined(__APPLE__)
   1062 #include <TargetConditionals.h>
   1063 #endif
   1064 
   1065 // Visual Studio warnings
   1066 #ifdef _MSC_VER
   1067 #pragma warning (disable: 4127)             // condition expression is constant
   1068 #pragma warning (disable: 4996)             // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
   1069 #if defined(_MSC_VER) && _MSC_VER >= 1922   // MSVC 2019 16.2 or later
   1070 #pragma warning (disable: 5054)             // operator '|': deprecated between enumerations of different types
   1071 #endif
   1072 #pragma warning (disable: 26451)            // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to an 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
   1073 #pragma warning (disable: 26495)            // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).
   1074 #pragma warning (disable: 26812)            // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
   1075 #endif
   1076 
   1077 // Clang/GCC warnings with -Weverything
   1078 #if defined(__clang__)
   1079 #if __has_warning("-Wunknown-warning-option")
   1080 #pragma clang diagnostic ignored "-Wunknown-warning-option"         // warning: unknown warning group 'xxx'                      // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!
   1081 #endif
   1082 #pragma clang diagnostic ignored "-Wunknown-pragmas"                // warning: unknown warning group 'xxx'
   1083 #pragma clang diagnostic ignored "-Wold-style-cast"                 // warning: use of old-style cast                            // yes, they are more terse.
   1084 #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.
   1085 #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.
   1086 #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.
   1087 #pragma clang diagnostic ignored "-Wglobal-constructors"            // warning: declaration requires a global destructor         // similar to above, not sure what the exact difference is.
   1088 #pragma clang diagnostic ignored "-Wsign-conversion"                // warning: implicit conversion changes signedness
   1089 #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.
   1090 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast"       // warning: cast to 'void *' from smaller integer type 'int'
   1091 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"  // warning: zero as null pointer constant                    // some standard header variations use #define NULL 0
   1092 #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.
   1093 #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"  // warning: implicit conversion from 'xxx' to 'float' may lose precision
   1094 #pragma clang diagnostic ignored "-Wunsafe-buffer-usage"            // warning: 'xxx' is an unsafe pointer used for buffer access
   1095 #elif defined(__GNUC__)
   1096 // We disable -Wpragmas because GCC doesn't provide a has_warning equivalent and some forks/patches may not follow the warning/version association.
   1097 #pragma GCC diagnostic ignored "-Wpragmas"                  // warning: unknown option after '#pragma GCC diagnostic' kind
   1098 #pragma GCC diagnostic ignored "-Wunused-function"          // warning: 'xxxx' defined but not used
   1099 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"      // warning: cast to pointer from integer of different size
   1100 #pragma GCC diagnostic ignored "-Wformat"                   // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
   1101 #pragma GCC diagnostic ignored "-Wdouble-promotion"         // warning: implicit conversion from 'float' to 'double' when passing argument to function
   1102 #pragma GCC diagnostic ignored "-Wconversion"               // warning: conversion to 'xxxx' from 'xxxx' may alter its value
   1103 #pragma GCC diagnostic ignored "-Wformat-nonliteral"        // warning: format not a string literal, format string not checked
   1104 #pragma GCC diagnostic ignored "-Wstrict-overflow"          // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
   1105 #pragma GCC diagnostic ignored "-Wclass-memaccess"          // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
   1106 #endif
   1107 
   1108 // Debug options
   1109 #define IMGUI_DEBUG_NAV_SCORING     0   // Display navigation scoring preview when hovering items. Hold CTRL to display for all candidates. CTRL+Arrow to change last direction.
   1110 #define IMGUI_DEBUG_NAV_RECTS       0   // Display the reference navigation rectangle for each window
   1111 
   1112 // 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.
   1113 static const float NAV_WINDOWING_HIGHLIGHT_DELAY            = 0.20f;    // Time before the highlight and screen dimming starts fading in
   1114 static const float NAV_WINDOWING_LIST_APPEAR_DELAY          = 0.15f;    // Time before the window list starts to appear
   1115 
   1116 static const float NAV_ACTIVATE_HIGHLIGHT_TIMER             = 0.10f;    // Time to highlight an item activated by a shortcut.
   1117 
   1118 // Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by backend)
   1119 static const float WINDOWS_HOVER_PADDING                    = 4.0f;     // Extend outside window for hovering/resizing (maxxed with TouchPadding) and inside windows for borders. Affect FindHoveredWindow().
   1120 static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f;    // Reduce visual noise by only highlighting the border after a certain time.
   1121 static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER    = 0.70f;    // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved.
   1122 
   1123 // Tooltip offset
   1124 static const ImVec2 TOOLTIP_DEFAULT_OFFSET = ImVec2(16, 10);            // Multiplied by g.Style.MouseCursorScale
   1125 
   1126 //-------------------------------------------------------------------------
   1127 // [SECTION] FORWARD DECLARATIONS
   1128 //-------------------------------------------------------------------------
   1129 
   1130 static void             SetCurrentWindow(ImGuiWindow* window);
   1131 static ImGuiWindow*     CreateNewWindow(const char* name, ImGuiWindowFlags flags);
   1132 static ImVec2           CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);
   1133 
   1134 static void             AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
   1135 
   1136 // Settings
   1137 static void             WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
   1138 static void*            WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
   1139 static void             WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
   1140 static void             WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);
   1141 static void             WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf);
   1142 
   1143 // Platform Dependents default implementation for IO functions
   1144 static const char*      GetClipboardTextFn_DefaultImpl(void* user_data_ctx);
   1145 static void             SetClipboardTextFn_DefaultImpl(void* user_data_ctx, const char* text);
   1146 static void             PlatformSetImeDataFn_DefaultImpl(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data);
   1147 static bool             PlatformOpenInShellFn_DefaultImpl(ImGuiContext* ctx, const char* path);
   1148 
   1149 namespace ImGui
   1150 {
   1151 // Item
   1152 static void             ItemHandleShortcut(ImGuiID id);
   1153 
   1154 // Navigation
   1155 static void             NavUpdate();
   1156 static void             NavUpdateWindowing();
   1157 static void             NavUpdateWindowingOverlay();
   1158 static void             NavUpdateCancelRequest();
   1159 static void             NavUpdateCreateMoveRequest();
   1160 static void             NavUpdateCreateTabbingRequest();
   1161 static float            NavUpdatePageUpPageDown();
   1162 static inline void      NavUpdateAnyRequestFlag();
   1163 static void             NavUpdateCreateWrappingRequest();
   1164 static void             NavEndFrame();
   1165 static bool             NavScoreItem(ImGuiNavItemData* result);
   1166 static void             NavApplyItemToResult(ImGuiNavItemData* result);
   1167 static void             NavProcessItem();
   1168 static void             NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags);
   1169 static ImVec2           NavCalcPreferredRefPos();
   1170 static void             NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
   1171 static ImGuiWindow*     NavRestoreLastChildNavWindow(ImGuiWindow* window);
   1172 static void             NavRestoreLayer(ImGuiNavLayer layer);
   1173 static int              FindWindowFocusIndex(ImGuiWindow* window);
   1174 
   1175 // Error Checking and Debug Tools
   1176 static void             ErrorCheckNewFrameSanityChecks();
   1177 static void             ErrorCheckEndFrameSanityChecks();
   1178 static void             UpdateDebugToolItemPicker();
   1179 static void             UpdateDebugToolStackQueries();
   1180 static void             UpdateDebugToolFlashStyleColor();
   1181 
   1182 // Inputs
   1183 static void             UpdateKeyboardInputs();
   1184 static void             UpdateMouseInputs();
   1185 static void             UpdateMouseWheel();
   1186 static void             UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt);
   1187 
   1188 // Misc
   1189 static void             UpdateSettings();
   1190 static int              UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect);
   1191 static void             RenderWindowOuterBorders(ImGuiWindow* window);
   1192 static void             RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size);
   1193 static void             RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
   1194 static void             RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col);
   1195 static void             RenderDimmedBackgrounds();
   1196 static void             SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect);
   1197 
   1198 // Viewports
   1199 const ImGuiID           IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHashStr("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter.
   1200 static void             UpdateViewportsNewFrame();
   1201 
   1202 }
   1203 
   1204 //-----------------------------------------------------------------------------
   1205 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
   1206 //-----------------------------------------------------------------------------
   1207 
   1208 // DLL users:
   1209 // - Heaps and globals are not shared across DLL boundaries!
   1210 // - You will need to call SetCurrentContext() + SetAllocatorFunctions() for each static/DLL boundary you are calling from.
   1211 // - Same applies for hot-reloading mechanisms that are reliant on reloading DLL (note that many hot-reloading mechanisms work without DLL).
   1212 // - Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
   1213 // - Confused? In a debugger: add GImGui to your watch window and notice how its value changes depending on your current location (which DLL boundary you are in).
   1214 
   1215 // Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
   1216 // - ImGui::CreateContext() will automatically set this pointer if it is NULL.
   1217 //   Change to a different context by calling ImGui::SetCurrentContext().
   1218 // - Important: Dear ImGui functions are not thread-safe because of this pointer.
   1219 //   If you want thread-safety to allow N threads to access N different contexts:
   1220 //   - Change this variable to use thread local storage so each thread can refer to a different context, in your imconfig.h:
   1221 //         struct ImGuiContext;
   1222 //         extern thread_local ImGuiContext* MyImGuiTLS;
   1223 //         #define GImGui MyImGuiTLS
   1224 //     And then define MyImGuiTLS in one of your cpp files. Note that thread_local is a C++11 keyword, earlier C++ uses compiler-specific keyword.
   1225 //   - Future development aims to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
   1226 //   - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from a different namespace.
   1227 // - DLL users: read comments above.
   1228 #ifndef GImGui
   1229 ImGuiContext*   GImGui = NULL;
   1230 #endif
   1231 
   1232 // Memory Allocator functions. Use SetAllocatorFunctions() to change them.
   1233 // - You probably don't want to modify that mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction.
   1234 // - DLL users: read comments above.
   1235 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
   1236 static void*   MallocWrapper(size_t size, void* user_data)    { IM_UNUSED(user_data); return malloc(size); }
   1237 static void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); free(ptr); }
   1238 #else
   1239 static void*   MallocWrapper(size_t size, void* user_data)    { IM_UNUSED(user_data); IM_UNUSED(size); IM_ASSERT(0); return NULL; }
   1240 static void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }
   1241 #endif
   1242 static ImGuiMemAllocFunc    GImAllocatorAllocFunc = MallocWrapper;
   1243 static ImGuiMemFreeFunc     GImAllocatorFreeFunc = FreeWrapper;
   1244 static void*                GImAllocatorUserData = NULL;
   1245 
   1246 //-----------------------------------------------------------------------------
   1247 // [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
   1248 //-----------------------------------------------------------------------------
   1249 
   1250 ImGuiStyle::ImGuiStyle()
   1251 {
   1252     Alpha                       = 1.0f;             // Global alpha applies to everything in Dear ImGui.
   1253     DisabledAlpha               = 0.60f;            // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha.
   1254     WindowPadding               = ImVec2(8,8);      // Padding within a window
   1255     WindowRounding              = 0.0f;             // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended.
   1256     WindowBorderSize            = 1.0f;             // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
   1257     WindowMinSize               = ImVec2(32,32);    // Minimum window size
   1258     WindowTitleAlign            = ImVec2(0.0f,0.5f);// Alignment for title bar text
   1259     WindowMenuButtonPosition    = ImGuiDir_Left;    // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.
   1260     ChildRounding               = 0.0f;             // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
   1261     ChildBorderSize             = 1.0f;             // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
   1262     PopupRounding               = 0.0f;             // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
   1263     PopupBorderSize             = 1.0f;             // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
   1264     FramePadding                = ImVec2(4,3);      // Padding within a framed rectangle (used by most widgets)
   1265     FrameRounding               = 0.0f;             // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
   1266     FrameBorderSize             = 0.0f;             // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
   1267     ItemSpacing                 = ImVec2(8,4);      // Horizontal and vertical spacing between widgets/lines
   1268     ItemInnerSpacing            = ImVec2(4,4);      // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
   1269     CellPadding                 = ImVec2(4,2);      // Padding within a table cell. Cellpadding.x is locked for entire table. CellPadding.y may be altered between different rows.
   1270     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!
   1271     IndentSpacing               = 21.0f;            // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
   1272     ColumnsMinSpacing           = 6.0f;             // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
   1273     ScrollbarSize               = 14.0f;            // Width of the vertical scrollbar, Height of the horizontal scrollbar
   1274     ScrollbarRounding           = 9.0f;             // Radius of grab corners rounding for scrollbar
   1275     GrabMinSize                 = 12.0f;            // Minimum width/height of a grab box for slider/scrollbar
   1276     GrabRounding                = 0.0f;             // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
   1277     LogSliderDeadzone           = 4.0f;             // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
   1278     TabRounding                 = 4.0f;             // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
   1279     TabBorderSize               = 0.0f;             // Thickness of border around tabs.
   1280     TabMinWidthForCloseButton   = 0.0f;             // Minimum width for close button to appear on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected.
   1281     TabBarBorderSize            = 1.0f;             // Thickness of tab-bar separator, which takes on the tab active color to denote focus.
   1282     TabBarOverlineSize          = 2.0f;             // Thickness of tab-bar overline, which highlights the selected tab-bar.
   1283     TableAngledHeadersAngle     = 35.0f * (IM_PI / 180.0f); // Angle of angled headers (supported values range from -50 degrees to +50 degrees).
   1284     TableAngledHeadersTextAlign = ImVec2(0.5f,0.0f);// Alignment of angled headers within the cell
   1285     ColorButtonPosition         = ImGuiDir_Right;   // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
   1286     ButtonTextAlign             = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
   1287     SelectableTextAlign         = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line.
   1288     SeparatorTextBorderSize     = 3.0f;             // Thickness of border in SeparatorText()
   1289     SeparatorTextAlign          = ImVec2(0.0f,0.5f);// Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center).
   1290     SeparatorTextPadding        = ImVec2(20.0f,3.f);// Horizontal offset of text from each edge of the separator + spacing on other axis. Generally small values. .y is recommended to be == FramePadding.y.
   1291     DisplayWindowPadding        = ImVec2(19,19);    // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows.
   1292     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.
   1293     MouseCursorScale            = 1.0f;             // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
   1294     AntiAliasedLines            = true;             // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU.
   1295     AntiAliasedLinesUseTex      = true;             // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering).
   1296     AntiAliasedFill             = true;             // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).
   1297     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.
   1298     CircleTessellationMaxError  = 0.30f;            // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
   1299 
   1300     // Behaviors
   1301     HoverStationaryDelay        = 0.15f;            // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary.
   1302     HoverDelayShort             = 0.15f;            // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay.
   1303     HoverDelayNormal            = 0.40f;            // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). "
   1304     HoverFlagsForTooltipMouse   = ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_AllowWhenDisabled;    // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse.
   1305     HoverFlagsForTooltipNav     = ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_AllowWhenDisabled;  // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad.
   1306 
   1307     // Default theme
   1308     ImGui::StyleColorsDark(this);
   1309 }
   1310 
   1311 // 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.
   1312 // 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.
   1313 void ImGuiStyle::ScaleAllSizes(float scale_factor)
   1314 {
   1315     WindowPadding = ImVec2(ImCeil(WindowPadding.x * scale_factor), ImCeil(WindowPadding.x * scale_factor));
   1316     WindowRounding = ImCeil(WindowRounding * scale_factor);
   1317     WindowMinSize = ImVec2(ImCeil(WindowMinSize.x * scale_factor), ImCeil(WindowMinSize.y * scale_factor));
   1318     ChildRounding = ImCeil(ChildRounding * scale_factor);
   1319     PopupRounding = ImCeil(PopupRounding * scale_factor);
   1320     FramePadding = ImVec2(ImCeil(FramePadding.x * scale_factor), ImCeil(FramePadding.y * scale_factor));
   1321     FrameRounding = ImCeil(FrameRounding * scale_factor);
   1322     ItemSpacing = ImVec2(ImCeil(ItemSpacing.x * scale_factor), ImCeil(ItemSpacing.y * scale_factor));
   1323     ItemInnerSpacing = ImVec2(ImCeil(ItemInnerSpacing.x * scale_factor), ImCeil(ItemInnerSpacing.y * scale_factor));
   1324     CellPadding = ImVec2(ImCeil(CellPadding.x * scale_factor), ImCeil(CellPadding.y * scale_factor));
   1325     TouchExtraPadding = ImVec2(ImCeil(TouchExtraPadding.x * scale_factor), ImCeil(TouchExtraPadding.y * scale_factor));
   1326     IndentSpacing = ImCeil(IndentSpacing * scale_factor);
   1327     ColumnsMinSpacing = ImCeil(ColumnsMinSpacing * scale_factor);
   1328     ScrollbarSize = ImCeil(ScrollbarSize * scale_factor);
   1329     ScrollbarRounding = ImCeil(ScrollbarRounding * scale_factor);
   1330     GrabMinSize = ImCeil(GrabMinSize * scale_factor);
   1331     GrabRounding = ImCeil(GrabRounding * scale_factor);
   1332     LogSliderDeadzone = ImCeil(LogSliderDeadzone * scale_factor);
   1333     TabRounding = ImCeil(TabRounding * scale_factor);
   1334     TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImCeil(TabMinWidthForCloseButton * scale_factor) : FLT_MAX;
   1335     TabBarOverlineSize = ImCeil(TabBarOverlineSize * scale_factor);
   1336     SeparatorTextPadding = ImVec2(ImCeil(SeparatorTextPadding.x * scale_factor), ImCeil(SeparatorTextPadding.y * scale_factor));
   1337     DisplayWindowPadding = ImVec2(ImCeil(DisplayWindowPadding.x * scale_factor), ImCeil(DisplayWindowPadding.y * scale_factor));
   1338     DisplaySafeAreaPadding = ImVec2(ImCeil(DisplaySafeAreaPadding.x * scale_factor), ImCeil(DisplaySafeAreaPadding.y * scale_factor));
   1339     MouseCursorScale = ImCeil(MouseCursorScale * scale_factor);
   1340 }
   1341 
   1342 ImGuiIO::ImGuiIO()
   1343 {
   1344     // Most fields are initialized with zero
   1345     memset(this, 0, sizeof(*this));
   1346     IM_STATIC_ASSERT(IM_ARRAYSIZE(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_ARRAYSIZE(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT);
   1347 
   1348     // Settings
   1349     ConfigFlags = ImGuiConfigFlags_None;
   1350     BackendFlags = ImGuiBackendFlags_None;
   1351     DisplaySize = ImVec2(-1.0f, -1.0f);
   1352     DeltaTime = 1.0f / 60.0f;
   1353     IniSavingRate = 5.0f;
   1354     IniFilename = "imgui.ini"; // Important: "imgui.ini" is relative to current working dir, most apps will want to lock this to an absolute path (e.g. same path as executables).
   1355     LogFilename = "imgui_log.txt";
   1356 #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
   1357     for (int i = 0; i < ImGuiKey_COUNT; i++)
   1358         KeyMap[i] = -1;
   1359 #endif
   1360     UserData = NULL;
   1361 
   1362     Fonts = NULL;
   1363     FontGlobalScale = 1.0f;
   1364     FontDefault = NULL;
   1365     FontAllowUserScaling = false;
   1366     DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
   1367 
   1368     MouseDoubleClickTime = 0.30f;
   1369     MouseDoubleClickMaxDist = 6.0f;
   1370     MouseDragThreshold = 6.0f;
   1371     KeyRepeatDelay = 0.275f;
   1372     KeyRepeatRate = 0.050f;
   1373 
   1374     // Miscellaneous options
   1375     MouseDrawCursor = false;
   1376 #ifdef __APPLE__
   1377     ConfigMacOSXBehaviors = true;  // Set Mac OS X style defaults based on __APPLE__ compile time flag
   1378 #else
   1379     ConfigMacOSXBehaviors = false;
   1380 #endif
   1381     ConfigNavSwapGamepadButtons = false;
   1382     ConfigInputTrickleEventQueue = true;
   1383     ConfigInputTextCursorBlink = true;
   1384     ConfigInputTextEnterKeepActive = false;
   1385     ConfigDragClickToInputText = false;
   1386     ConfigWindowsResizeFromEdges = true;
   1387     ConfigWindowsMoveFromTitleBarOnly = false;
   1388     ConfigMemoryCompactTimer = 60.0f;
   1389     ConfigDebugBeginReturnValueOnce = false;
   1390     ConfigDebugBeginReturnValueLoop = false;
   1391 
   1392     // Platform Functions
   1393     // Note: Initialize() will setup default clipboard/ime handlers.
   1394     BackendPlatformName = BackendRendererName = NULL;
   1395     BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
   1396     PlatformOpenInShellUserData = NULL;
   1397     PlatformLocaleDecimalPoint = '.';
   1398 
   1399     // Input (NB: we already have memset zero the entire structure!)
   1400     MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
   1401     MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
   1402     MouseSource = ImGuiMouseSource_Mouse;
   1403     for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
   1404     for (int i = 0; i < IM_ARRAYSIZE(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; }
   1405     AppAcceptingEvents = true;
   1406     BackendUsingLegacyKeyArrays = (ImS8)-1;
   1407     BackendUsingLegacyNavInputArray = true; // assume using legacy array until proven wrong
   1408 }
   1409 
   1410 // Pass in translated ASCII characters for text input.
   1411 // - with glfw you can get those from the callback set in glfwSetCharCallback()
   1412 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
   1413 // FIXME: Should in theory be called "AddCharacterEvent()" to be consistent with new API
   1414 void ImGuiIO::AddInputCharacter(unsigned int c)
   1415 {
   1416     IM_ASSERT(Ctx != NULL);
   1417     ImGuiContext& g = *Ctx;
   1418     if (c == 0 || !AppAcceptingEvents)
   1419         return;
   1420 
   1421     ImGuiInputEvent e;
   1422     e.Type = ImGuiInputEventType_Text;
   1423     e.Source = ImGuiInputSource_Keyboard;
   1424     e.EventId = g.InputEventsNextEventId++;
   1425     e.Text.Char = c;
   1426     g.InputEventsQueue.push_back(e);
   1427 }
   1428 
   1429 // UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so
   1430 // we should save the high surrogate.
   1431 void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c)
   1432 {
   1433     if ((c == 0 && InputQueueSurrogate == 0) || !AppAcceptingEvents)
   1434         return;
   1435 
   1436     if ((c & 0xFC00) == 0xD800) // High surrogate, must save
   1437     {
   1438         if (InputQueueSurrogate != 0)
   1439             AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID);
   1440         InputQueueSurrogate = c;
   1441         return;
   1442     }
   1443 
   1444     ImWchar cp = c;
   1445     if (InputQueueSurrogate != 0)
   1446     {
   1447         if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate
   1448         {
   1449             AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID);
   1450         }
   1451         else
   1452         {
   1453 #if IM_UNICODE_CODEPOINT_MAX == 0xFFFF
   1454             cp = IM_UNICODE_CODEPOINT_INVALID; // Codepoint will not fit in ImWchar
   1455 #else
   1456             cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000);
   1457 #endif
   1458         }
   1459 
   1460         InputQueueSurrogate = 0;
   1461     }
   1462     AddInputCharacter((unsigned)cp);
   1463 }
   1464 
   1465 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
   1466 {
   1467     if (!AppAcceptingEvents)
   1468         return;
   1469     while (*utf8_chars != 0)
   1470     {
   1471         unsigned int c = 0;
   1472         utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
   1473         AddInputCharacter(c);
   1474     }
   1475 }
   1476 
   1477 // Clear all incoming events.
   1478 void ImGuiIO::ClearEventsQueue()
   1479 {
   1480     IM_ASSERT(Ctx != NULL);
   1481     ImGuiContext& g = *Ctx;
   1482     g.InputEventsQueue.clear();
   1483 }
   1484 
   1485 // Clear current keyboard/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons.
   1486 void ImGuiIO::ClearInputKeys()
   1487 {
   1488 #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
   1489     memset(KeysDown, 0, sizeof(KeysDown));
   1490 #endif
   1491     for (int n = 0; n < IM_ARRAYSIZE(KeysData); n++)
   1492     {
   1493         if (ImGui::IsMouseKey((ImGuiKey)(n + ImGuiKey_KeysData_OFFSET)))
   1494             continue;
   1495         KeysData[n].Down             = false;
   1496         KeysData[n].DownDuration     = -1.0f;
   1497         KeysData[n].DownDurationPrev = -1.0f;
   1498     }
   1499     KeyCtrl = KeyShift = KeyAlt = KeySuper = false;
   1500     KeyMods = ImGuiMod_None;
   1501     InputQueueCharacters.resize(0); // Behavior of old ClearInputCharacters().
   1502 }
   1503 
   1504 void ImGuiIO::ClearInputMouse()
   1505 {
   1506     for (ImGuiKey key = ImGuiKey_Mouse_BEGIN; key < ImGuiKey_Mouse_END; key = (ImGuiKey)(key + 1))
   1507     {
   1508         ImGuiKeyData* key_data = &KeysData[key - ImGuiKey_KeysData_OFFSET];
   1509         key_data->Down = false;
   1510         key_data->DownDuration = -1.0f;
   1511         key_data->DownDurationPrev = -1.0f;
   1512     }
   1513     MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
   1514     for (int n = 0; n < IM_ARRAYSIZE(MouseDown); n++)
   1515     {
   1516         MouseDown[n] = false;
   1517         MouseDownDuration[n] = MouseDownDurationPrev[n] = -1.0f;
   1518     }
   1519     MouseWheel = MouseWheelH = 0.0f;
   1520 }
   1521 
   1522 // Removed this as it is ambiguous/misleading and generally incorrect to use with the existence of a higher-level input queue.
   1523 // Current frame character buffer is now also cleared by ClearInputKeys().
   1524 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
   1525 void ImGuiIO::ClearInputCharacters()
   1526 {
   1527     InputQueueCharacters.resize(0);
   1528 }
   1529 #endif
   1530 
   1531 static ImGuiInputEvent* FindLatestInputEvent(ImGuiContext* ctx, ImGuiInputEventType type, int arg = -1)
   1532 {
   1533     ImGuiContext& g = *ctx;
   1534     for (int n = g.InputEventsQueue.Size - 1; n >= 0; n--)
   1535     {
   1536         ImGuiInputEvent* e = &g.InputEventsQueue[n];
   1537         if (e->Type != type)
   1538             continue;
   1539         if (type == ImGuiInputEventType_Key && e->Key.Key != arg)
   1540             continue;
   1541         if (type == ImGuiInputEventType_MouseButton && e->MouseButton.Button != arg)
   1542             continue;
   1543         return e;
   1544     }
   1545     return NULL;
   1546 }
   1547 
   1548 // Queue a new key down/up event.
   1549 // - ImGuiKey key:       Translated key (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character)
   1550 // - bool down:          Is the key down? use false to signify a key release.
   1551 // - float analog_value: 0.0f..1.0f
   1552 // IMPORTANT: THIS FUNCTION AND OTHER "ADD" GRABS THE CONTEXT FROM OUR INSTANCE.
   1553 // WE NEED TO ENSURE THAT ALL FUNCTION CALLS ARE FULFILLING THIS, WHICH IS WHY GetKeyData() HAS AN EXPLICIT CONTEXT.
   1554 void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value)
   1555 {
   1556     //if (e->Down) { IMGUI_DEBUG_LOG_IO("AddKeyEvent() Key='%s' %d, NativeKeycode = %d, NativeScancode = %d\n", ImGui::GetKeyName(e->Key), e->Down, e->NativeKeycode, e->NativeScancode); }
   1557     IM_ASSERT(Ctx != NULL);
   1558     if (key == ImGuiKey_None || !AppAcceptingEvents)
   1559         return;
   1560     ImGuiContext& g = *Ctx;
   1561     IM_ASSERT(ImGui::IsNamedKeyOrMod(key)); // Backend needs to pass a valid ImGuiKey_ constant. 0..511 values are legacy native key codes which are not accepted by this API.
   1562     IM_ASSERT(ImGui::IsAliasKey(key) == false); // Backend cannot submit ImGuiKey_MouseXXX values they are automatically inferred from AddMouseXXX() events.
   1563 
   1564     // MacOS: swap Cmd(Super) and Ctrl
   1565     if (g.IO.ConfigMacOSXBehaviors)
   1566     {
   1567         if (key == ImGuiMod_Super)          { key = ImGuiMod_Ctrl; }
   1568         else if (key == ImGuiMod_Ctrl)      { key = ImGuiMod_Super; }
   1569         else if (key == ImGuiKey_LeftSuper) { key = ImGuiKey_LeftCtrl; }
   1570         else if (key == ImGuiKey_RightSuper){ key = ImGuiKey_RightCtrl; }
   1571         else if (key == ImGuiKey_LeftCtrl)  { key = ImGuiKey_LeftSuper; }
   1572         else if (key == ImGuiKey_RightCtrl) { key = ImGuiKey_RightSuper; }
   1573     }
   1574 
   1575     // Verify that backend isn't mixing up using new io.AddKeyEvent() api and old io.KeysDown[] + io.KeyMap[] data.
   1576 #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
   1577     IM_ASSERT((BackendUsingLegacyKeyArrays == -1 || BackendUsingLegacyKeyArrays == 0) && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!");
   1578     if (BackendUsingLegacyKeyArrays == -1)
   1579         for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++)
   1580             IM_ASSERT(KeyMap[n] == -1 && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!");
   1581     BackendUsingLegacyKeyArrays = 0;
   1582 #endif
   1583     if (ImGui::IsGamepadKey(key))
   1584         BackendUsingLegacyNavInputArray = false;
   1585 
   1586     // Filter duplicate (in particular: key mods and gamepad analog values are commonly spammed)
   1587     const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_Key, (int)key);
   1588     const ImGuiKeyData* key_data = ImGui::GetKeyData(&g, key);
   1589     const bool latest_key_down = latest_event ? latest_event->Key.Down : key_data->Down;
   1590     const float latest_key_analog = latest_event ? latest_event->Key.AnalogValue : key_data->AnalogValue;
   1591     if (latest_key_down == down && latest_key_analog == analog_value)
   1592         return;
   1593 
   1594     // Add event
   1595     ImGuiInputEvent e;
   1596     e.Type = ImGuiInputEventType_Key;
   1597     e.Source = ImGui::IsGamepadKey(key) ? ImGuiInputSource_Gamepad : ImGuiInputSource_Keyboard;
   1598     e.EventId = g.InputEventsNextEventId++;
   1599     e.Key.Key = key;
   1600     e.Key.Down = down;
   1601     e.Key.AnalogValue = analog_value;
   1602     g.InputEventsQueue.push_back(e);
   1603 }
   1604 
   1605 void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down)
   1606 {
   1607     if (!AppAcceptingEvents)
   1608         return;
   1609     AddKeyAnalogEvent(key, down, down ? 1.0f : 0.0f);
   1610 }
   1611 
   1612 // [Optional] Call after AddKeyEvent().
   1613 // Specify native keycode, scancode + Specify index for legacy <1.87 IsKeyXXX() functions with native indices.
   1614 // If you are writing a backend in 2022 or don't use IsKeyXXX() with native values that are not ImGuiKey values, you can avoid calling this.
   1615 void ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index)
   1616 {
   1617     if (key == ImGuiKey_None)
   1618         return;
   1619     IM_ASSERT(ImGui::IsNamedKey(key)); // >= 512
   1620     IM_ASSERT(native_legacy_index == -1 || ImGui::IsLegacyKey((ImGuiKey)native_legacy_index)); // >= 0 && <= 511
   1621     IM_UNUSED(native_keycode);  // Yet unused
   1622     IM_UNUSED(native_scancode); // Yet unused
   1623 
   1624     // Build native->imgui map so old user code can still call key functions with native 0..511 values.
   1625 #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
   1626     const int legacy_key = (native_legacy_index != -1) ? native_legacy_index : native_keycode;
   1627     if (!ImGui::IsLegacyKey((ImGuiKey)legacy_key))
   1628         return;
   1629     KeyMap[legacy_key] = key;
   1630     KeyMap[key] = legacy_key;
   1631 #else
   1632     IM_UNUSED(key);
   1633     IM_UNUSED(native_legacy_index);
   1634 #endif
   1635 }
   1636 
   1637 // Set master flag for accepting key/mouse/text events (default to true). Useful if you have native dialog boxes that are interrupting your application loop/refresh, and you want to disable events being queued while your app is frozen.
   1638 void ImGuiIO::SetAppAcceptingEvents(bool accepting_events)
   1639 {
   1640     AppAcceptingEvents = accepting_events;
   1641 }
   1642 
   1643 // Queue a mouse move event
   1644 void ImGuiIO::AddMousePosEvent(float x, float y)
   1645 {
   1646     IM_ASSERT(Ctx != NULL);
   1647     ImGuiContext& g = *Ctx;
   1648     if (!AppAcceptingEvents)
   1649         return;
   1650 
   1651     // Apply same flooring as UpdateMouseInputs()
   1652     ImVec2 pos((x > -FLT_MAX) ? ImFloor(x) : x, (y > -FLT_MAX) ? ImFloor(y) : y);
   1653 
   1654     // Filter duplicate
   1655     const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MousePos);
   1656     const ImVec2 latest_pos = latest_event ? ImVec2(latest_event->MousePos.PosX, latest_event->MousePos.PosY) : g.IO.MousePos;
   1657     if (latest_pos.x == pos.x && latest_pos.y == pos.y)
   1658         return;
   1659 
   1660     ImGuiInputEvent e;
   1661     e.Type = ImGuiInputEventType_MousePos;
   1662     e.Source = ImGuiInputSource_Mouse;
   1663     e.EventId = g.InputEventsNextEventId++;
   1664     e.MousePos.PosX = pos.x;
   1665     e.MousePos.PosY = pos.y;
   1666     e.MousePos.MouseSource = g.InputEventsNextMouseSource;
   1667     g.InputEventsQueue.push_back(e);
   1668 }
   1669 
   1670 void ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down)
   1671 {
   1672     IM_ASSERT(Ctx != NULL);
   1673     ImGuiContext& g = *Ctx;
   1674     IM_ASSERT(mouse_button >= 0 && mouse_button < ImGuiMouseButton_COUNT);
   1675     if (!AppAcceptingEvents)
   1676         return;
   1677 
   1678     // On MacOS X: Convert Ctrl(Super)+Left click into Right-click: handle held button.
   1679     if (ConfigMacOSXBehaviors && mouse_button == 0 && MouseCtrlLeftAsRightClick)
   1680     {
   1681         // Order of both statements matterns: this event will still release mouse button 1
   1682         mouse_button = 1;
   1683         if (!down)
   1684             MouseCtrlLeftAsRightClick = false;
   1685     }
   1686 
   1687     // Filter duplicate
   1688     const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MouseButton, (int)mouse_button);
   1689     const bool latest_button_down = latest_event ? latest_event->MouseButton.Down : g.IO.MouseDown[mouse_button];
   1690     if (latest_button_down == down)
   1691         return;
   1692 
   1693     // On MacOS X: Convert Ctrl(Super)+Left click into Right-click.
   1694     // - Note that this is actual physical Ctrl which is ImGuiMod_Super for us.
   1695     // - At this point we want from !down to down, so this is handling the initial press.
   1696     if (ConfigMacOSXBehaviors && mouse_button == 0 && down)
   1697     {
   1698         const ImGuiInputEvent* latest_super_event = FindLatestInputEvent(&g, ImGuiInputEventType_Key, (int)ImGuiMod_Super);
   1699         if (latest_super_event ? latest_super_event->Key.Down : g.IO.KeySuper)
   1700         {
   1701             IMGUI_DEBUG_LOG_IO("[io] Super+Left Click aliased into Right Click\n");
   1702             MouseCtrlLeftAsRightClick = true;
   1703             AddMouseButtonEvent(1, true); // This is just quicker to write that passing through, as we need to filter duplicate again.
   1704             return;
   1705         }
   1706     }
   1707 
   1708     ImGuiInputEvent e;
   1709     e.Type = ImGuiInputEventType_MouseButton;
   1710     e.Source = ImGuiInputSource_Mouse;
   1711     e.EventId = g.InputEventsNextEventId++;
   1712     e.MouseButton.Button = mouse_button;
   1713     e.MouseButton.Down = down;
   1714     e.MouseButton.MouseSource = g.InputEventsNextMouseSource;
   1715     g.InputEventsQueue.push_back(e);
   1716 }
   1717 
   1718 // Queue a mouse wheel event (some mouse/API may only have a Y component)
   1719 void ImGuiIO::AddMouseWheelEvent(float wheel_x, float wheel_y)
   1720 {
   1721     IM_ASSERT(Ctx != NULL);
   1722     ImGuiContext& g = *Ctx;
   1723 
   1724     // Filter duplicate (unlike most events, wheel values are relative and easy to filter)
   1725     if (!AppAcceptingEvents || (wheel_x == 0.0f && wheel_y == 0.0f))
   1726         return;
   1727 
   1728     ImGuiInputEvent e;
   1729     e.Type = ImGuiInputEventType_MouseWheel;
   1730     e.Source = ImGuiInputSource_Mouse;
   1731     e.EventId = g.InputEventsNextEventId++;
   1732     e.MouseWheel.WheelX = wheel_x;
   1733     e.MouseWheel.WheelY = wheel_y;
   1734     e.MouseWheel.MouseSource = g.InputEventsNextMouseSource;
   1735     g.InputEventsQueue.push_back(e);
   1736 }
   1737 
   1738 // This is not a real event, the data is latched in order to be stored in actual Mouse events.
   1739 // This is so that duplicate events (e.g. Windows sending extraneous WM_MOUSEMOVE) gets filtered and are not leading to actual source changes.
   1740 void ImGuiIO::AddMouseSourceEvent(ImGuiMouseSource source)
   1741 {
   1742     IM_ASSERT(Ctx != NULL);
   1743     ImGuiContext& g = *Ctx;
   1744     g.InputEventsNextMouseSource = source;
   1745 }
   1746 
   1747 void ImGuiIO::AddFocusEvent(bool focused)
   1748 {
   1749     IM_ASSERT(Ctx != NULL);
   1750     ImGuiContext& g = *Ctx;
   1751 
   1752     // Filter duplicate
   1753     const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_Focus);
   1754     const bool latest_focused = latest_event ? latest_event->AppFocused.Focused : !g.IO.AppFocusLost;
   1755     if (latest_focused == focused || (ConfigDebugIgnoreFocusLoss && !focused))
   1756         return;
   1757 
   1758     ImGuiInputEvent e;
   1759     e.Type = ImGuiInputEventType_Focus;
   1760     e.EventId = g.InputEventsNextEventId++;
   1761     e.AppFocused.Focused = focused;
   1762     g.InputEventsQueue.push_back(e);
   1763 }
   1764 
   1765 //-----------------------------------------------------------------------------
   1766 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
   1767 //-----------------------------------------------------------------------------
   1768 
   1769 ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments)
   1770 {
   1771     IM_ASSERT(num_segments > 0); // Use ImBezierCubicClosestPointCasteljau()
   1772     ImVec2 p_last = p1;
   1773     ImVec2 p_closest;
   1774     float p_closest_dist2 = FLT_MAX;
   1775     float t_step = 1.0f / (float)num_segments;
   1776     for (int i_step = 1; i_step <= num_segments; i_step++)
   1777     {
   1778         ImVec2 p_current = ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step);
   1779         ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
   1780         float dist2 = ImLengthSqr(p - p_line);
   1781         if (dist2 < p_closest_dist2)
   1782         {
   1783             p_closest = p_line;
   1784             p_closest_dist2 = dist2;
   1785         }
   1786         p_last = p_current;
   1787     }
   1788     return p_closest;
   1789 }
   1790 
   1791 // Closely mimics PathBezierToCasteljau() in imgui_draw.cpp
   1792 static void ImBezierCubicClosestPointCasteljauStep(const ImVec2& p, ImVec2& p_closest, ImVec2& p_last, float& p_closest_dist2, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level)
   1793 {
   1794     float dx = x4 - x1;
   1795     float dy = y4 - y1;
   1796     float d2 = ((x2 - x4) * dy - (y2 - y4) * dx);
   1797     float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
   1798     d2 = (d2 >= 0) ? d2 : -d2;
   1799     d3 = (d3 >= 0) ? d3 : -d3;
   1800     if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))
   1801     {
   1802         ImVec2 p_current(x4, y4);
   1803         ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
   1804         float dist2 = ImLengthSqr(p - p_line);
   1805         if (dist2 < p_closest_dist2)
   1806         {
   1807             p_closest = p_line;
   1808             p_closest_dist2 = dist2;
   1809         }
   1810         p_last = p_current;
   1811     }
   1812     else if (level < 10)
   1813     {
   1814         float x12 = (x1 + x2)*0.5f,       y12 = (y1 + y2)*0.5f;
   1815         float x23 = (x2 + x3)*0.5f,       y23 = (y2 + y3)*0.5f;
   1816         float x34 = (x3 + x4)*0.5f,       y34 = (y3 + y4)*0.5f;
   1817         float x123 = (x12 + x23)*0.5f,    y123 = (y12 + y23)*0.5f;
   1818         float x234 = (x23 + x34)*0.5f,    y234 = (y23 + y34)*0.5f;
   1819         float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f;
   1820         ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);
   1821         ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);
   1822     }
   1823 }
   1824 
   1825 // tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol
   1826 // Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically.
   1827 ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol)
   1828 {
   1829     IM_ASSERT(tess_tol > 0.0f);
   1830     ImVec2 p_last = p1;
   1831     ImVec2 p_closest;
   1832     float p_closest_dist2 = FLT_MAX;
   1833     ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, tess_tol, 0);
   1834     return p_closest;
   1835 }
   1836 
   1837 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
   1838 {
   1839     ImVec2 ap = p - a;
   1840     ImVec2 ab_dir = b - a;
   1841     float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
   1842     if (dot < 0.0f)
   1843         return a;
   1844     float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
   1845     if (dot > ab_len_sqr)
   1846         return b;
   1847     return a + ab_dir * dot / ab_len_sqr;
   1848 }
   1849 
   1850 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
   1851 {
   1852     bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
   1853     bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
   1854     bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
   1855     return ((b1 == b2) && (b2 == b3));
   1856 }
   1857 
   1858 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
   1859 {
   1860     ImVec2 v0 = b - a;
   1861     ImVec2 v1 = c - a;
   1862     ImVec2 v2 = p - a;
   1863     const float denom = v0.x * v1.y - v1.x * v0.y;
   1864     out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
   1865     out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
   1866     out_u = 1.0f - out_v - out_w;
   1867 }
   1868 
   1869 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
   1870 {
   1871     ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
   1872     ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
   1873     ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
   1874     float dist2_ab = ImLengthSqr(p - proj_ab);
   1875     float dist2_bc = ImLengthSqr(p - proj_bc);
   1876     float dist2_ca = ImLengthSqr(p - proj_ca);
   1877     float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
   1878     if (m == dist2_ab)
   1879         return proj_ab;
   1880     if (m == dist2_bc)
   1881         return proj_bc;
   1882     return proj_ca;
   1883 }
   1884 
   1885 //-----------------------------------------------------------------------------
   1886 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
   1887 //-----------------------------------------------------------------------------
   1888 
   1889 // Consider using _stricmp/_strnicmp under Windows or strcasecmp/strncasecmp. We don't actually use either ImStricmp/ImStrnicmp in the codebase any more.
   1890 int ImStricmp(const char* str1, const char* str2)
   1891 {
   1892     int d;
   1893     while ((d = ImToUpper(*str2) - ImToUpper(*str1)) == 0 && *str1) { str1++; str2++; }
   1894     return d;
   1895 }
   1896 
   1897 int ImStrnicmp(const char* str1, const char* str2, size_t count)
   1898 {
   1899     int d = 0;
   1900     while (count > 0 && (d = ImToUpper(*str2) - ImToUpper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
   1901     return d;
   1902 }
   1903 
   1904 void ImStrncpy(char* dst, const char* src, size_t count)
   1905 {
   1906     if (count < 1)
   1907         return;
   1908     if (count > 1)
   1909         strncpy(dst, src, count - 1);
   1910     dst[count - 1] = 0;
   1911 }
   1912 
   1913 char* ImStrdup(const char* str)
   1914 {
   1915     size_t len = strlen(str);
   1916     void* buf = IM_ALLOC(len + 1);
   1917     return (char*)memcpy(buf, (const void*)str, len + 1);
   1918 }
   1919 
   1920 char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
   1921 {
   1922     size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1;
   1923     size_t src_size = strlen(src) + 1;
   1924     if (dst_buf_size < src_size)
   1925     {
   1926         IM_FREE(dst);
   1927         dst = (char*)IM_ALLOC(src_size);
   1928         if (p_dst_size)
   1929             *p_dst_size = src_size;
   1930     }
   1931     return (char*)memcpy(dst, (const void*)src, src_size);
   1932 }
   1933 
   1934 const char* ImStrchrRange(const char* str, const char* str_end, char c)
   1935 {
   1936     const char* p = (const char*)memchr(str, (int)c, str_end - str);
   1937     return p;
   1938 }
   1939 
   1940 int ImStrlenW(const ImWchar* str)
   1941 {
   1942     //return (int)wcslen((const wchar_t*)str);  // FIXME-OPT: Could use this when wchar_t are 16-bit
   1943     int n = 0;
   1944     while (*str++) n++;
   1945     return n;
   1946 }
   1947 
   1948 // Find end-of-line. Return pointer will point to either first \n, either str_end.
   1949 const char* ImStreolRange(const char* str, const char* str_end)
   1950 {
   1951     const char* p = (const char*)memchr(str, '\n', str_end - str);
   1952     return p ? p : str_end;
   1953 }
   1954 
   1955 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
   1956 {
   1957     while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
   1958         buf_mid_line--;
   1959     return buf_mid_line;
   1960 }
   1961 
   1962 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
   1963 {
   1964     if (!needle_end)
   1965         needle_end = needle + strlen(needle);
   1966 
   1967     const char un0 = (char)ImToUpper(*needle);
   1968     while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
   1969     {
   1970         if (ImToUpper(*haystack) == un0)
   1971         {
   1972             const char* b = needle + 1;
   1973             for (const char* a = haystack + 1; b < needle_end; a++, b++)
   1974                 if (ImToUpper(*a) != ImToUpper(*b))
   1975                     break;
   1976             if (b == needle_end)
   1977                 return haystack;
   1978         }
   1979         haystack++;
   1980     }
   1981     return NULL;
   1982 }
   1983 
   1984 // 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.
   1985 void ImStrTrimBlanks(char* buf)
   1986 {
   1987     char* p = buf;
   1988     while (p[0] == ' ' || p[0] == '\t')     // Leading blanks
   1989         p++;
   1990     char* p_start = p;
   1991     while (*p != 0)                         // Find end of string
   1992         p++;
   1993     while (p > p_start && (p[-1] == ' ' || p[-1] == '\t'))  // Trailing blanks
   1994         p--;
   1995     if (p_start != buf)                     // Copy memory if we had leading blanks
   1996         memmove(buf, p_start, p - p_start);
   1997     buf[p - p_start] = 0;                   // Zero terminate
   1998 }
   1999 
   2000 const char* ImStrSkipBlank(const char* str)
   2001 {
   2002     while (str[0] == ' ' || str[0] == '\t')
   2003         str++;
   2004     return str;
   2005 }
   2006 
   2007 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
   2008 // 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.
   2009 // B) When buf==NULL vsnprintf() will return the output size.
   2010 #ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
   2011 
   2012 // We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h)
   2013 // You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
   2014 // and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are
   2015 // designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.)
   2016 #ifdef IMGUI_USE_STB_SPRINTF
   2017 #ifndef IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION
   2018 #define STB_SPRINTF_IMPLEMENTATION
   2019 #define STB_SPRINTF_STATIC
   2020 #endif
   2021 #ifdef IMGUI_STB_SPRINTF_FILENAME
   2022 #include IMGUI_STB_SPRINTF_FILENAME
   2023 #else
   2024 #include "stb_sprintf.h"
   2025 #endif
   2026 #endif // #ifdef IMGUI_USE_STB_SPRINTF
   2027 
   2028 #if defined(_MSC_VER) && !defined(vsnprintf)
   2029 #define vsnprintf _vsnprintf
   2030 #endif
   2031 
   2032 int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
   2033 {
   2034     va_list args;
   2035     va_start(args, fmt);
   2036 #ifdef IMGUI_USE_STB_SPRINTF
   2037     int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
   2038 #else
   2039     int w = vsnprintf(buf, buf_size, fmt, args);
   2040 #endif
   2041     va_end(args);
   2042     if (buf == NULL)
   2043         return w;
   2044     if (w == -1 || w >= (int)buf_size)
   2045         w = (int)buf_size - 1;
   2046     buf[w] = 0;
   2047     return w;
   2048 }
   2049 
   2050 int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
   2051 {
   2052 #ifdef IMGUI_USE_STB_SPRINTF
   2053     int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
   2054 #else
   2055     int w = vsnprintf(buf, buf_size, fmt, args);
   2056 #endif
   2057     if (buf == NULL)
   2058         return w;
   2059     if (w == -1 || w >= (int)buf_size)
   2060         w = (int)buf_size - 1;
   2061     buf[w] = 0;
   2062     return w;
   2063 }
   2064 #endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
   2065 
   2066 void ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, const char* fmt, ...)
   2067 {
   2068     va_list args;
   2069     va_start(args, fmt);
   2070     ImFormatStringToTempBufferV(out_buf, out_buf_end, fmt, args);
   2071     va_end(args);
   2072 }
   2073 
   2074 // FIXME: Should rework API toward allowing multiple in-flight temp buffers (easier and safer for caller)
   2075 // by making the caller acquire a temp buffer token, with either explicit or destructor release, e.g.
   2076 //  ImGuiTempBufferToken token;
   2077 //  ImFormatStringToTempBuffer(token, ...);
   2078 void ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end, const char* fmt, va_list args)
   2079 {
   2080     ImGuiContext& g = *GImGui;
   2081     if (fmt[0] == '%' && fmt[1] == 's' && fmt[2] == 0)
   2082     {
   2083         const char* buf = va_arg(args, const char*); // Skip formatting when using "%s"
   2084         if (buf == NULL)
   2085             buf = "(null)";
   2086         *out_buf = buf;
   2087         if (out_buf_end) { *out_buf_end = buf + strlen(buf); }
   2088     }
   2089     else if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*' && fmt[3] == 's' && fmt[4] == 0)
   2090     {
   2091         int buf_len = va_arg(args, int); // Skip formatting when using "%.*s"
   2092         const char* buf = va_arg(args, const char*);
   2093         if (buf == NULL)
   2094         {
   2095             buf = "(null)";
   2096             buf_len = ImMin(buf_len, 6);
   2097         }
   2098         *out_buf = buf;
   2099         *out_buf_end = buf + buf_len; // Disallow not passing 'out_buf_end' here. User is expected to use it.
   2100     }
   2101     else
   2102     {
   2103         int buf_len = ImFormatStringV(g.TempBuffer.Data, g.TempBuffer.Size, fmt, args);
   2104         *out_buf = g.TempBuffer.Data;
   2105         if (out_buf_end) { *out_buf_end = g.TempBuffer.Data + buf_len; }
   2106     }
   2107 }
   2108 
   2109 // CRC32 needs a 1KB lookup table (not cache friendly)
   2110 // Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
   2111 // - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
   2112 static const ImU32 GCrc32LookupTable[256] =
   2113 {
   2114     0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
   2115     0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
   2116     0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
   2117     0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
   2118     0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
   2119     0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
   2120     0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
   2121     0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
   2122     0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
   2123     0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
   2124     0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
   2125     0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
   2126     0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
   2127     0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
   2128     0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
   2129     0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
   2130 };
   2131 
   2132 // Known size hash
   2133 // It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
   2134 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
   2135 ImGuiID ImHashData(const void* data_p, size_t data_size, ImGuiID seed)
   2136 {
   2137     ImU32 crc = ~seed;
   2138     const unsigned char* data = (const unsigned char*)data_p;
   2139     const ImU32* crc32_lut = GCrc32LookupTable;
   2140     while (data_size-- != 0)
   2141         crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
   2142     return ~crc;
   2143 }
   2144 
   2145 // Zero-terminated string hash, with support for ### to reset back to seed value
   2146 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
   2147 // Because this syntax is rarely used we are optimizing for the common case.
   2148 // - If we reach ### in the string we discard the hash so far and reset to the seed.
   2149 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
   2150 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
   2151 ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed)
   2152 {
   2153     seed = ~seed;
   2154     ImU32 crc = seed;
   2155     const unsigned char* data = (const unsigned char*)data_p;
   2156     const ImU32* crc32_lut = GCrc32LookupTable;
   2157     if (data_size != 0)
   2158     {
   2159         while (data_size-- != 0)
   2160         {
   2161             unsigned char c = *data++;
   2162             if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')
   2163                 crc = seed;
   2164             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
   2165         }
   2166     }
   2167     else
   2168     {
   2169         while (unsigned char c = *data++)
   2170         {
   2171             if (c == '#' && data[0] == '#' && data[1] == '#')
   2172                 crc = seed;
   2173             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
   2174         }
   2175     }
   2176     return ~crc;
   2177 }
   2178 
   2179 //-----------------------------------------------------------------------------
   2180 // [SECTION] MISC HELPERS/UTILITIES (File functions)
   2181 //-----------------------------------------------------------------------------
   2182 
   2183 // Default file functions
   2184 #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
   2185 
   2186 ImFileHandle ImFileOpen(const char* filename, const char* mode)
   2187 {
   2188 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__)
   2189     // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.
   2190     // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32!
   2191     const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
   2192     const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
   2193 
   2194     // Use stack buffer if possible, otherwise heap buffer. Sizes include zero terminator.
   2195     // We don't rely on current ImGuiContext as this is implied to be a helper function which doesn't depend on it (see #7314).
   2196     wchar_t local_temp_stack[FILENAME_MAX];
   2197     ImVector<wchar_t> local_temp_heap;
   2198     if (filename_wsize + mode_wsize > IM_ARRAYSIZE(local_temp_stack))
   2199         local_temp_heap.resize(filename_wsize + mode_wsize);
   2200     wchar_t* filename_wbuf = local_temp_heap.Data ? local_temp_heap.Data : local_temp_stack;
   2201     wchar_t* mode_wbuf = filename_wbuf + filename_wsize;
   2202     ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, filename_wbuf, filename_wsize);
   2203     ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, mode_wbuf, mode_wsize);
   2204     return ::_wfopen(filename_wbuf, mode_wbuf);
   2205 #else
   2206     return fopen(filename, mode);
   2207 #endif
   2208 }
   2209 
   2210 // We should in theory be using fseeko()/ftello() with off_t and _fseeki64()/_ftelli64() with __int64, waiting for the PR that does that in a very portable pre-C++11 zero-warnings way.
   2211 bool    ImFileClose(ImFileHandle f)     { return fclose(f) == 0; }
   2212 ImU64   ImFileGetSize(ImFileHandle f)   { long off = 0, sz = 0; return ((off = ftell(f)) != -1 && !fseek(f, 0, SEEK_END) && (sz = ftell(f)) != -1 && !fseek(f, off, SEEK_SET)) ? (ImU64)sz : (ImU64)-1; }
   2213 ImU64   ImFileRead(void* data, ImU64 sz, ImU64 count, ImFileHandle f)           { return fread(data, (size_t)sz, (size_t)count, f); }
   2214 ImU64   ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f)    { return fwrite(data, (size_t)sz, (size_t)count, f); }
   2215 #endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
   2216 
   2217 // Helper: Load file content into memory
   2218 // Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree()
   2219 // This can't really be used with "rt" because fseek size won't match read size.
   2220 void*   ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes)
   2221 {
   2222     IM_ASSERT(filename && mode);
   2223     if (out_file_size)
   2224         *out_file_size = 0;
   2225 
   2226     ImFileHandle f;
   2227     if ((f = ImFileOpen(filename, mode)) == NULL)
   2228         return NULL;
   2229 
   2230     size_t file_size = (size_t)ImFileGetSize(f);
   2231     if (file_size == (size_t)-1)
   2232     {
   2233         ImFileClose(f);
   2234         return NULL;
   2235     }
   2236 
   2237     void* file_data = IM_ALLOC(file_size + padding_bytes);
   2238     if (file_data == NULL)
   2239     {
   2240         ImFileClose(f);
   2241         return NULL;
   2242     }
   2243     if (ImFileRead(file_data, 1, file_size, f) != file_size)
   2244     {
   2245         ImFileClose(f);
   2246         IM_FREE(file_data);
   2247         return NULL;
   2248     }
   2249     if (padding_bytes > 0)
   2250         memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
   2251 
   2252     ImFileClose(f);
   2253     if (out_file_size)
   2254         *out_file_size = file_size;
   2255 
   2256     return file_data;
   2257 }
   2258 
   2259 //-----------------------------------------------------------------------------
   2260 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
   2261 //-----------------------------------------------------------------------------
   2262 
   2263 IM_MSVC_RUNTIME_CHECKS_OFF
   2264 
   2265 // Convert UTF-8 to 32-bit character, process single character input.
   2266 // A nearly-branchless UTF-8 decoder, based on work of Christopher Wellons (https://github.com/skeeto/branchless-utf8).
   2267 // We handle UTF-8 decoding error by skipping forward.
   2268 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
   2269 {
   2270     static const char lengths[32] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0 };
   2271     static const int masks[]  = { 0x00, 0x7f, 0x1f, 0x0f, 0x07 };
   2272     static const uint32_t mins[] = { 0x400000, 0, 0x80, 0x800, 0x10000 };
   2273     static const int shiftc[] = { 0, 18, 12, 6, 0 };
   2274     static const int shifte[] = { 0, 6, 4, 2, 0 };
   2275     int len = lengths[*(const unsigned char*)in_text >> 3];
   2276     int wanted = len + (len ? 0 : 1);
   2277 
   2278     if (in_text_end == NULL)
   2279         in_text_end = in_text + wanted; // Max length, nulls will be taken into account.
   2280 
   2281     // Copy at most 'len' bytes, stop copying at 0 or past in_text_end. Branch predictor does a good job here,
   2282     // so it is fast even with excessive branching.
   2283     unsigned char s[4];
   2284     s[0] = in_text + 0 < in_text_end ? in_text[0] : 0;
   2285     s[1] = in_text + 1 < in_text_end ? in_text[1] : 0;
   2286     s[2] = in_text + 2 < in_text_end ? in_text[2] : 0;
   2287     s[3] = in_text + 3 < in_text_end ? in_text[3] : 0;
   2288 
   2289     // Assume a four-byte character and load four bytes. Unused bits are shifted out.
   2290     *out_char  = (uint32_t)(s[0] & masks[len]) << 18;
   2291     *out_char |= (uint32_t)(s[1] & 0x3f) << 12;
   2292     *out_char |= (uint32_t)(s[2] & 0x3f) <<  6;
   2293     *out_char |= (uint32_t)(s[3] & 0x3f) <<  0;
   2294     *out_char >>= shiftc[len];
   2295 
   2296     // Accumulate the various error conditions.
   2297     int e = 0;
   2298     e  = (*out_char < mins[len]) << 6; // non-canonical encoding
   2299     e |= ((*out_char >> 11) == 0x1b) << 7;  // surrogate half?
   2300     e |= (*out_char > IM_UNICODE_CODEPOINT_MAX) << 8;  // out of range?
   2301     e |= (s[1] & 0xc0) >> 2;
   2302     e |= (s[2] & 0xc0) >> 4;
   2303     e |= (s[3]       ) >> 6;
   2304     e ^= 0x2a; // top two bits of each tail byte correct?
   2305     e >>= shifte[len];
   2306 
   2307     if (e)
   2308     {
   2309         // No bytes are consumed when *in_text == 0 || in_text == in_text_end.
   2310         // One byte is consumed in case of invalid first byte of in_text.
   2311         // All available bytes (at most `len` bytes) are consumed on incomplete/invalid second to last bytes.
   2312         // Invalid or incomplete input may consume less bytes than wanted, therefore every byte has to be inspected in s.
   2313         wanted = ImMin(wanted, !!s[0] + !!s[1] + !!s[2] + !!s[3]);
   2314         *out_char = IM_UNICODE_CODEPOINT_INVALID;
   2315     }
   2316 
   2317     return wanted;
   2318 }
   2319 
   2320 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
   2321 {
   2322     ImWchar* buf_out = buf;
   2323     ImWchar* buf_end = buf + buf_size;
   2324     while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
   2325     {
   2326         unsigned int c;
   2327         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
   2328         *buf_out++ = (ImWchar)c;
   2329     }
   2330     *buf_out = 0;
   2331     if (in_text_remaining)
   2332         *in_text_remaining = in_text;
   2333     return (int)(buf_out - buf);
   2334 }
   2335 
   2336 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
   2337 {
   2338     int char_count = 0;
   2339     while ((!in_text_end || in_text < in_text_end) && *in_text)
   2340     {
   2341         unsigned int c;
   2342         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
   2343         char_count++;
   2344     }
   2345     return char_count;
   2346 }
   2347 
   2348 // Based on stb_to_utf8() from github.com/nothings/stb/
   2349 static inline int ImTextCharToUtf8_inline(char* buf, int buf_size, unsigned int c)
   2350 {
   2351     if (c < 0x80)
   2352     {
   2353         buf[0] = (char)c;
   2354         return 1;
   2355     }
   2356     if (c < 0x800)
   2357     {
   2358         if (buf_size < 2) return 0;
   2359         buf[0] = (char)(0xc0 + (c >> 6));
   2360         buf[1] = (char)(0x80 + (c & 0x3f));
   2361         return 2;
   2362     }
   2363     if (c < 0x10000)
   2364     {
   2365         if (buf_size < 3) return 0;
   2366         buf[0] = (char)(0xe0 + (c >> 12));
   2367         buf[1] = (char)(0x80 + ((c >> 6) & 0x3f));
   2368         buf[2] = (char)(0x80 + ((c ) & 0x3f));
   2369         return 3;
   2370     }
   2371     if (c <= 0x10FFFF)
   2372     {
   2373         if (buf_size < 4) return 0;
   2374         buf[0] = (char)(0xf0 + (c >> 18));
   2375         buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
   2376         buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
   2377         buf[3] = (char)(0x80 + ((c ) & 0x3f));
   2378         return 4;
   2379     }
   2380     // Invalid code point, the max unicode is 0x10FFFF
   2381     return 0;
   2382 }
   2383 
   2384 const char* ImTextCharToUtf8(char out_buf[5], unsigned int c)
   2385 {
   2386     int count = ImTextCharToUtf8_inline(out_buf, 5, c);
   2387     out_buf[count] = 0;
   2388     return out_buf;
   2389 }
   2390 
   2391 // Not optimal but we very rarely use this function.
   2392 int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
   2393 {
   2394     unsigned int unused = 0;
   2395     return ImTextCharFromUtf8(&unused, in_text, in_text_end);
   2396 }
   2397 
   2398 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
   2399 {
   2400     if (c < 0x80) return 1;
   2401     if (c < 0x800) return 2;
   2402     if (c < 0x10000) return 3;
   2403     if (c <= 0x10FFFF) return 4;
   2404     return 3;
   2405 }
   2406 
   2407 int ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
   2408 {
   2409     char* buf_p = out_buf;
   2410     const char* buf_end = out_buf + out_buf_size;
   2411     while (buf_p < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
   2412     {
   2413         unsigned int c = (unsigned int)(*in_text++);
   2414         if (c < 0x80)
   2415             *buf_p++ = (char)c;
   2416         else
   2417             buf_p += ImTextCharToUtf8_inline(buf_p, (int)(buf_end - buf_p - 1), c);
   2418     }
   2419     *buf_p = 0;
   2420     return (int)(buf_p - out_buf);
   2421 }
   2422 
   2423 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
   2424 {
   2425     int bytes_count = 0;
   2426     while ((!in_text_end || in_text < in_text_end) && *in_text)
   2427     {
   2428         unsigned int c = (unsigned int)(*in_text++);
   2429         if (c < 0x80)
   2430             bytes_count++;
   2431         else
   2432             bytes_count += ImTextCountUtf8BytesFromChar(c);
   2433     }
   2434     return bytes_count;
   2435 }
   2436 
   2437 const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_text_curr)
   2438 {
   2439     while (in_text_curr > in_text_start)
   2440     {
   2441         in_text_curr--;
   2442         if ((*in_text_curr & 0xC0) != 0x80)
   2443             return in_text_curr;
   2444     }
   2445     return in_text_start;
   2446 }
   2447 
   2448 int ImTextCountLines(const char* in_text, const char* in_text_end)
   2449 {
   2450     if (in_text_end == NULL)
   2451         in_text_end = in_text + strlen(in_text); // FIXME-OPT: Not optimal approach, discourage use for now.
   2452     int count = 0;
   2453     while (in_text < in_text_end)
   2454     {
   2455         const char* line_end = (const char*)memchr(in_text, '\n', in_text_end - in_text);
   2456         in_text = line_end ? line_end + 1 : in_text_end;
   2457         count++;
   2458     }
   2459     return count;
   2460 }
   2461 
   2462 IM_MSVC_RUNTIME_CHECKS_RESTORE
   2463 
   2464 //-----------------------------------------------------------------------------
   2465 // [SECTION] MISC HELPERS/UTILITIES (Color functions)
   2466 // Note: The Convert functions are early design which are not consistent with other API.
   2467 //-----------------------------------------------------------------------------
   2468 
   2469 IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b)
   2470 {
   2471     float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;
   2472     int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);
   2473     int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);
   2474     int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);
   2475     return IM_COL32(r, g, b, 0xFF);
   2476 }
   2477 
   2478 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
   2479 {
   2480     float s = 1.0f / 255.0f;
   2481     return ImVec4(
   2482         ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
   2483         ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
   2484         ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
   2485         ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
   2486 }
   2487 
   2488 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
   2489 {
   2490     ImU32 out;
   2491     out  = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
   2492     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
   2493     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
   2494     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
   2495     return out;
   2496 }
   2497 
   2498 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
   2499 // Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
   2500 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
   2501 {
   2502     float K = 0.f;
   2503     if (g < b)
   2504     {
   2505         ImSwap(g, b);
   2506         K = -1.f;
   2507     }
   2508     if (r < g)
   2509     {
   2510         ImSwap(r, g);
   2511         K = -2.f / 6.f - K;
   2512     }
   2513 
   2514     const float chroma = r - (g < b ? g : b);
   2515     out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
   2516     out_s = chroma / (r + 1e-20f);
   2517     out_v = r;
   2518 }
   2519 
   2520 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
   2521 // also http://en.wikipedia.org/wiki/HSL_and_HSV
   2522 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
   2523 {
   2524     if (s == 0.0f)
   2525     {
   2526         // gray
   2527         out_r = out_g = out_b = v;
   2528         return;
   2529     }
   2530 
   2531     h = ImFmod(h, 1.0f) / (60.0f / 360.0f);
   2532     int   i = (int)h;
   2533     float f = h - (float)i;
   2534     float p = v * (1.0f - s);
   2535     float q = v * (1.0f - s * f);
   2536     float t = v * (1.0f - s * (1.0f - f));
   2537 
   2538     switch (i)
   2539     {
   2540     case 0: out_r = v; out_g = t; out_b = p; break;
   2541     case 1: out_r = q; out_g = v; out_b = p; break;
   2542     case 2: out_r = p; out_g = v; out_b = t; break;
   2543     case 3: out_r = p; out_g = q; out_b = v; break;
   2544     case 4: out_r = t; out_g = p; out_b = v; break;
   2545     case 5: default: out_r = v; out_g = p; out_b = q; break;
   2546     }
   2547 }
   2548 
   2549 //-----------------------------------------------------------------------------
   2550 // [SECTION] ImGuiStorage
   2551 // Helper: Key->value storage
   2552 //-----------------------------------------------------------------------------
   2553 
   2554 // std::lower_bound but without the bullshit
   2555 ImGuiStoragePair* ImLowerBound(ImGuiStoragePair* in_begin, ImGuiStoragePair* in_end, ImGuiID key)
   2556 {
   2557     ImGuiStoragePair* in_p = in_begin;
   2558     for (size_t count = (size_t)(in_end - in_p); count > 0; )
   2559     {
   2560         size_t count2 = count >> 1;
   2561         ImGuiStoragePair* mid = in_p + count2;
   2562         if (mid->key < key)
   2563         {
   2564             in_p = ++mid;
   2565             count -= count2 + 1;
   2566         }
   2567         else
   2568         {
   2569             count = count2;
   2570         }
   2571     }
   2572     return in_p;
   2573 }
   2574 
   2575 IM_MSVC_RUNTIME_CHECKS_OFF
   2576 static int IMGUI_CDECL PairComparerByID(const void* lhs, const void* rhs)
   2577 {
   2578     // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
   2579     ImGuiID lhs_v = ((const ImGuiStoragePair*)lhs)->key;
   2580     ImGuiID rhs_v = ((const ImGuiStoragePair*)rhs)->key;
   2581     return (lhs_v > rhs_v ? +1 : lhs_v < rhs_v ? -1 : 0);
   2582 }
   2583 
   2584 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
   2585 void ImGuiStorage::BuildSortByKey()
   2586 {
   2587     ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), PairComparerByID);
   2588 }
   2589 
   2590 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
   2591 {
   2592     ImGuiStoragePair* it = ImLowerBound(const_cast<ImGuiStoragePair*>(Data.Data), const_cast<ImGuiStoragePair*>(Data.Data + Data.Size), key);
   2593     if (it == Data.Data + Data.Size || it->key != key)
   2594         return default_val;
   2595     return it->val_i;
   2596 }
   2597 
   2598 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
   2599 {
   2600     return GetInt(key, default_val ? 1 : 0) != 0;
   2601 }
   2602 
   2603 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
   2604 {
   2605     ImGuiStoragePair* it = ImLowerBound(const_cast<ImGuiStoragePair*>(Data.Data), const_cast<ImGuiStoragePair*>(Data.Data + Data.Size), key);
   2606     if (it == Data.Data + Data.Size || it->key != key)
   2607         return default_val;
   2608     return it->val_f;
   2609 }
   2610 
   2611 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
   2612 {
   2613     ImGuiStoragePair* it = ImLowerBound(const_cast<ImGuiStoragePair*>(Data.Data), const_cast<ImGuiStoragePair*>(Data.Data + Data.Size), key);
   2614     if (it == Data.Data + Data.Size || it->key != key)
   2615         return NULL;
   2616     return it->val_p;
   2617 }
   2618 
   2619 // 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.
   2620 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
   2621 {
   2622     ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
   2623     if (it == Data.Data + Data.Size || it->key != key)
   2624         it = Data.insert(it, ImGuiStoragePair(key, default_val));
   2625     return &it->val_i;
   2626 }
   2627 
   2628 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
   2629 {
   2630     return (bool*)GetIntRef(key, default_val ? 1 : 0);
   2631 }
   2632 
   2633 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
   2634 {
   2635     ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
   2636     if (it == Data.Data + Data.Size || it->key != key)
   2637         it = Data.insert(it, ImGuiStoragePair(key, default_val));
   2638     return &it->val_f;
   2639 }
   2640 
   2641 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
   2642 {
   2643     ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
   2644     if (it == Data.Data + Data.Size || it->key != key)
   2645         it = Data.insert(it, ImGuiStoragePair(key, default_val));
   2646     return &it->val_p;
   2647 }
   2648 
   2649 // 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)
   2650 void ImGuiStorage::SetInt(ImGuiID key, int val)
   2651 {
   2652     ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
   2653     if (it == Data.Data + Data.Size || it->key != key)
   2654         Data.insert(it, ImGuiStoragePair(key, val));
   2655     else
   2656         it->val_i = val;
   2657 }
   2658 
   2659 void ImGuiStorage::SetBool(ImGuiID key, bool val)
   2660 {
   2661     SetInt(key, val ? 1 : 0);
   2662 }
   2663 
   2664 void ImGuiStorage::SetFloat(ImGuiID key, float val)
   2665 {
   2666     ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
   2667     if (it == Data.Data + Data.Size || it->key != key)
   2668         Data.insert(it, ImGuiStoragePair(key, val));
   2669     else
   2670         it->val_f = val;
   2671 }
   2672 
   2673 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
   2674 {
   2675     ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
   2676     if (it == Data.Data + Data.Size || it->key != key)
   2677         Data.insert(it, ImGuiStoragePair(key, val));
   2678     else
   2679         it->val_p = val;
   2680 }
   2681 
   2682 void ImGuiStorage::SetAllInt(int v)
   2683 {
   2684     for (int i = 0; i < Data.Size; i++)
   2685         Data[i].val_i = v;
   2686 }
   2687 IM_MSVC_RUNTIME_CHECKS_RESTORE
   2688 
   2689 //-----------------------------------------------------------------------------
   2690 // [SECTION] ImGuiTextFilter
   2691 //-----------------------------------------------------------------------------
   2692 
   2693 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
   2694 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) //-V1077
   2695 {
   2696     InputBuf[0] = 0;
   2697     CountGrep = 0;
   2698     if (default_filter)
   2699     {
   2700         ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
   2701         Build();
   2702     }
   2703 }
   2704 
   2705 bool ImGuiTextFilter::Draw(const char* label, float width)
   2706 {
   2707     if (width != 0.0f)
   2708         ImGui::SetNextItemWidth(width);
   2709     bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
   2710     if (value_changed)
   2711         Build();
   2712     return value_changed;
   2713 }
   2714 
   2715 void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const
   2716 {
   2717     out->resize(0);
   2718     const char* wb = b;
   2719     const char* we = wb;
   2720     while (we < e)
   2721     {
   2722         if (*we == separator)
   2723         {
   2724             out->push_back(ImGuiTextRange(wb, we));
   2725             wb = we + 1;
   2726         }
   2727         we++;
   2728     }
   2729     if (wb != we)
   2730         out->push_back(ImGuiTextRange(wb, we));
   2731 }
   2732 
   2733 void ImGuiTextFilter::Build()
   2734 {
   2735     Filters.resize(0);
   2736     ImGuiTextRange input_range(InputBuf, InputBuf + strlen(InputBuf));
   2737     input_range.split(',', &Filters);
   2738 
   2739     CountGrep = 0;
   2740     for (ImGuiTextRange& f : Filters)
   2741     {
   2742         while (f.b < f.e && ImCharIsBlankA(f.b[0]))
   2743             f.b++;
   2744         while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
   2745             f.e--;
   2746         if (f.empty())
   2747             continue;
   2748         if (f.b[0] != '-')
   2749             CountGrep += 1;
   2750     }
   2751 }
   2752 
   2753 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
   2754 {
   2755     if (Filters.Size == 0)
   2756         return true;
   2757 
   2758     if (text == NULL)
   2759         text = text_end = "";
   2760 
   2761     for (const ImGuiTextRange& f : Filters)
   2762     {
   2763         if (f.b == f.e)
   2764             continue;
   2765         if (f.b[0] == '-')
   2766         {
   2767             // Subtract
   2768             if (ImStristr(text, text_end, f.b + 1, f.e) != NULL)
   2769                 return false;
   2770         }
   2771         else
   2772         {
   2773             // Grep
   2774             if (ImStristr(text, text_end, f.b, f.e) != NULL)
   2775                 return true;
   2776         }
   2777     }
   2778 
   2779     // Implicit * grep
   2780     if (CountGrep == 0)
   2781         return true;
   2782 
   2783     return false;
   2784 }
   2785 
   2786 //-----------------------------------------------------------------------------
   2787 // [SECTION] ImGuiTextBuffer, ImGuiTextIndex
   2788 //-----------------------------------------------------------------------------
   2789 
   2790 // On some platform vsnprintf() takes va_list by reference and modifies it.
   2791 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
   2792 #ifndef va_copy
   2793 #if defined(__GNUC__) || defined(__clang__)
   2794 #define va_copy(dest, src) __builtin_va_copy(dest, src)
   2795 #else
   2796 #define va_copy(dest, src) (dest = src)
   2797 #endif
   2798 #endif
   2799 
   2800 char ImGuiTextBuffer::EmptyString[1] = { 0 };
   2801 
   2802 void ImGuiTextBuffer::append(const char* str, const char* str_end)
   2803 {
   2804     int len = str_end ? (int)(str_end - str) : (int)strlen(str);
   2805 
   2806     // Add zero-terminator the first time
   2807     const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
   2808     const int needed_sz = write_off + len;
   2809     if (write_off + len >= Buf.Capacity)
   2810     {
   2811         int new_capacity = Buf.Capacity * 2;
   2812         Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
   2813     }
   2814 
   2815     Buf.resize(needed_sz);
   2816     memcpy(&Buf[write_off - 1], str, (size_t)len);
   2817     Buf[write_off - 1 + len] = 0;
   2818 }
   2819 
   2820 void ImGuiTextBuffer::appendf(const char* fmt, ...)
   2821 {
   2822     va_list args;
   2823     va_start(args, fmt);
   2824     appendfv(fmt, args);
   2825     va_end(args);
   2826 }
   2827 
   2828 // Helper: Text buffer for logging/accumulating text
   2829 void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
   2830 {
   2831     va_list args_copy;
   2832     va_copy(args_copy, args);
   2833 
   2834     int len = ImFormatStringV(NULL, 0, fmt, args);         // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
   2835     if (len <= 0)
   2836     {
   2837         va_end(args_copy);
   2838         return;
   2839     }
   2840 
   2841     // Add zero-terminator the first time
   2842     const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
   2843     const int needed_sz = write_off + len;
   2844     if (write_off + len >= Buf.Capacity)
   2845     {
   2846         int new_capacity = Buf.Capacity * 2;
   2847         Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
   2848     }
   2849 
   2850     Buf.resize(needed_sz);
   2851     ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
   2852     va_end(args_copy);
   2853 }
   2854 
   2855 void ImGuiTextIndex::append(const char* base, int old_size, int new_size)
   2856 {
   2857     IM_ASSERT(old_size >= 0 && new_size >= old_size && new_size >= EndOffset);
   2858     if (old_size == new_size)
   2859         return;
   2860     if (EndOffset == 0 || base[EndOffset - 1] == '\n')
   2861         LineOffsets.push_back(EndOffset);
   2862     const char* base_end = base + new_size;
   2863     for (const char* p = base + old_size; (p = (const char*)memchr(p, '\n', base_end - p)) != 0; )
   2864         if (++p < base_end) // Don't push a trailing offset on last \n
   2865             LineOffsets.push_back((int)(intptr_t)(p - base));
   2866     EndOffset = ImMax(EndOffset, new_size);
   2867 }
   2868 
   2869 //-----------------------------------------------------------------------------
   2870 // [SECTION] ImGuiListClipper
   2871 //-----------------------------------------------------------------------------
   2872 
   2873 // FIXME-TABLE: This prevents us from using ImGuiListClipper _inside_ a table cell.
   2874 // The problem we have is that without a Begin/End scheme for rows using the clipper is ambiguous.
   2875 static bool GetSkipItemForListClipping()
   2876 {
   2877     ImGuiContext& g = *GImGui;
   2878     return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems);
   2879 }
   2880 
   2881 static void ImGuiListClipper_SortAndFuseRanges(ImVector<ImGuiListClipperRange>& ranges, int offset = 0)
   2882 {
   2883     if (ranges.Size - offset <= 1)
   2884         return;
   2885 
   2886     // Helper to order ranges and fuse them together if possible (bubble sort is fine as we are only sorting 2-3 entries)
   2887     for (int sort_end = ranges.Size - offset - 1; sort_end > 0; --sort_end)
   2888         for (int i = offset; i < sort_end + offset; ++i)
   2889             if (ranges[i].Min > ranges[i + 1].Min)
   2890                 ImSwap(ranges[i], ranges[i + 1]);
   2891 
   2892     // Now fuse ranges together as much as possible.
   2893     for (int i = 1 + offset; i < ranges.Size; i++)
   2894     {
   2895         IM_ASSERT(!ranges[i].PosToIndexConvert && !ranges[i - 1].PosToIndexConvert);
   2896         if (ranges[i - 1].Max < ranges[i].Min)
   2897             continue;
   2898         ranges[i - 1].Min = ImMin(ranges[i - 1].Min, ranges[i].Min);
   2899         ranges[i - 1].Max = ImMax(ranges[i - 1].Max, ranges[i].Max);
   2900         ranges.erase(ranges.Data + i);
   2901         i--;
   2902     }
   2903 }
   2904 
   2905 static void ImGuiListClipper_SeekCursorAndSetupPrevLine(float pos_y, float line_height)
   2906 {
   2907     // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
   2908     // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
   2909     // The clipper should probably have a final step to display the last item in a regular manner, maybe with an opt-out flag for data sets which may have costly seek?
   2910     ImGuiContext& g = *GImGui;
   2911     ImGuiWindow* window = g.CurrentWindow;
   2912     float off_y = pos_y - window->DC.CursorPos.y;
   2913     window->DC.CursorPos.y = pos_y;
   2914     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y - g.Style.ItemSpacing.y);
   2915     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.
   2916     window->DC.PrevLineSize.y = (line_height - g.Style.ItemSpacing.y);      // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list.
   2917     if (ImGuiOldColumns* columns = window->DC.CurrentColumns)
   2918         columns->LineMinY = window->DC.CursorPos.y;                         // Setting this so that cell Y position are set properly
   2919     if (ImGuiTable* table = g.CurrentTable)
   2920     {
   2921         if (table->IsInsideRow)
   2922             ImGui::TableEndRow(table);
   2923         table->RowPosY2 = window->DC.CursorPos.y;
   2924         const int row_increase = (int)((off_y / line_height) + 0.5f);
   2925         //table->CurrentRow += row_increase; // Can't do without fixing TableEndRow()
   2926         table->RowBgColorCounter += row_increase;
   2927     }
   2928 }
   2929 
   2930 ImGuiListClipper::ImGuiListClipper()
   2931 {
   2932     memset(this, 0, sizeof(*this));
   2933 }
   2934 
   2935 ImGuiListClipper::~ImGuiListClipper()
   2936 {
   2937     End();
   2938 }
   2939 
   2940 void ImGuiListClipper::Begin(int items_count, float items_height)
   2941 {
   2942     if (Ctx == NULL)
   2943         Ctx = ImGui::GetCurrentContext();
   2944 
   2945     ImGuiContext& g = *Ctx;
   2946     ImGuiWindow* window = g.CurrentWindow;
   2947     IMGUI_DEBUG_LOG_CLIPPER("Clipper: Begin(%d,%.2f) in '%s'\n", items_count, items_height, window->Name);
   2948 
   2949     if (ImGuiTable* table = g.CurrentTable)
   2950         if (table->IsInsideRow)
   2951             ImGui::TableEndRow(table);
   2952 
   2953     StartPosY = window->DC.CursorPos.y;
   2954     ItemsHeight = items_height;
   2955     ItemsCount = items_count;
   2956     DisplayStart = -1;
   2957     DisplayEnd = 0;
   2958 
   2959     // Acquire temporary buffer
   2960     if (++g.ClipperTempDataStacked > g.ClipperTempData.Size)
   2961         g.ClipperTempData.resize(g.ClipperTempDataStacked, ImGuiListClipperData());
   2962     ImGuiListClipperData* data = &g.ClipperTempData[g.ClipperTempDataStacked - 1];
   2963     data->Reset(this);
   2964     data->LossynessOffset = window->DC.CursorStartPosLossyness.y;
   2965     TempData = data;
   2966     StartSeekOffsetY = data->LossynessOffset;
   2967 }
   2968 
   2969 void ImGuiListClipper::End()
   2970 {
   2971     if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData)
   2972     {
   2973         // In theory here we should assert that we are already at the right position, but it seems saner to just seek at the end and not assert/crash the user.
   2974         ImGuiContext& g = *Ctx;
   2975         IMGUI_DEBUG_LOG_CLIPPER("Clipper: End() in '%s'\n", g.CurrentWindow->Name);
   2976         if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0)
   2977             SeekCursorForItem(ItemsCount);
   2978 
   2979         // Restore temporary buffer and fix back pointers which may be invalidated when nesting
   2980         IM_ASSERT(data->ListClipper == this);
   2981         data->StepNo = data->Ranges.Size;
   2982         if (--g.ClipperTempDataStacked > 0)
   2983         {
   2984             data = &g.ClipperTempData[g.ClipperTempDataStacked - 1];
   2985             data->ListClipper->TempData = data;
   2986         }
   2987         TempData = NULL;
   2988     }
   2989     ItemsCount = -1;
   2990 }
   2991 
   2992 void ImGuiListClipper::IncludeItemsByIndex(int item_begin, int item_end)
   2993 {
   2994     ImGuiListClipperData* data = (ImGuiListClipperData*)TempData;
   2995     IM_ASSERT(DisplayStart < 0); // Only allowed after Begin() and if there has not been a specified range yet.
   2996     IM_ASSERT(item_begin <= item_end);
   2997     if (item_begin < item_end)
   2998         data->Ranges.push_back(ImGuiListClipperRange::FromIndices(item_begin, item_end));
   2999 }
   3000 
   3001 // This is already called while stepping.
   3002 // The ONLY reason you may want to call this is if you passed INT_MAX to ImGuiListClipper::Begin() because you couldn't step item count beforehand.
   3003 void ImGuiListClipper::SeekCursorForItem(int item_n)
   3004 {
   3005     // - Perform the add and multiply with double to allow seeking through larger ranges.
   3006     // - StartPosY starts from ItemsFrozen, by adding SeekOffsetY we generally cancel that out (SeekOffsetY == LossynessOffset - ItemsFrozen * ItemsHeight).
   3007     // - The reason we store SeekOffsetY instead of inferring it, is because we want to allow user to perform Seek after the last step, where ImGuiListClipperData is already done.
   3008     float pos_y = (float)((double)StartPosY + StartSeekOffsetY + (double)item_n * ItemsHeight);
   3009     ImGuiListClipper_SeekCursorAndSetupPrevLine(pos_y, ItemsHeight);
   3010 }
   3011 
   3012 static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper)
   3013 {
   3014     ImGuiContext& g = *clipper->Ctx;
   3015     ImGuiWindow* window = g.CurrentWindow;
   3016     ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData;
   3017     IM_ASSERT(data != NULL && "Called ImGuiListClipper::Step() too many times, or before ImGuiListClipper::Begin() ?");
   3018 
   3019     ImGuiTable* table = g.CurrentTable;
   3020     if (table && table->IsInsideRow)
   3021         ImGui::TableEndRow(table);
   3022 
   3023     // No items
   3024     if (clipper->ItemsCount == 0 || GetSkipItemForListClipping())
   3025         return false;
   3026 
   3027     // While we are in frozen row state, keep displaying items one by one, unclipped
   3028     // FIXME: Could be stored as a table-agnostic state.
   3029     if (data->StepNo == 0 && table != NULL && !table->IsUnfrozenRows)
   3030     {
   3031         clipper->DisplayStart = data->ItemsFrozen;
   3032         clipper->DisplayEnd = ImMin(data->ItemsFrozen + 1, clipper->ItemsCount);
   3033         if (clipper->DisplayStart < clipper->DisplayEnd)
   3034             data->ItemsFrozen++;
   3035         return true;
   3036     }
   3037 
   3038     // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height)
   3039     bool calc_clipping = false;
   3040     if (data->StepNo == 0)
   3041     {
   3042         clipper->StartPosY = window->DC.CursorPos.y;
   3043         if (clipper->ItemsHeight <= 0.0f)
   3044         {
   3045             // Submit the first item (or range) so we can measure its height (generally the first range is 0..1)
   3046             data->Ranges.push_front(ImGuiListClipperRange::FromIndices(data->ItemsFrozen, data->ItemsFrozen + 1));
   3047             clipper->DisplayStart = ImMax(data->Ranges[0].Min, data->ItemsFrozen);
   3048             clipper->DisplayEnd = ImMin(data->Ranges[0].Max, clipper->ItemsCount);
   3049             data->StepNo = 1;
   3050             return true;
   3051         }
   3052         calc_clipping = true;   // If on the first step with known item height, calculate clipping.
   3053     }
   3054 
   3055     // Step 1: Let the clipper infer height from first range
   3056     if (clipper->ItemsHeight <= 0.0f)
   3057     {
   3058         IM_ASSERT(data->StepNo == 1);
   3059         if (table)
   3060             IM_ASSERT(table->RowPosY1 == clipper->StartPosY && table->RowPosY2 == window->DC.CursorPos.y);
   3061 
   3062         clipper->ItemsHeight = (window->DC.CursorPos.y - clipper->StartPosY) / (float)(clipper->DisplayEnd - clipper->DisplayStart);
   3063         bool affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision(clipper->StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y);
   3064         if (affected_by_floating_point_precision)
   3065             clipper->ItemsHeight = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y; // FIXME: Technically wouldn't allow multi-line entries.
   3066         if (clipper->ItemsHeight == 0.0f && clipper->ItemsCount == INT_MAX) // Accept that no item have been submitted if in indeterminate mode.
   3067             return false;
   3068         IM_ASSERT(clipper->ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!");
   3069         calc_clipping = true;   // If item height had to be calculated, calculate clipping afterwards.
   3070     }
   3071 
   3072     // Step 0 or 1: Calculate the actual ranges of visible elements.
   3073     const int already_submitted = clipper->DisplayEnd;
   3074     if (calc_clipping)
   3075     {
   3076         // Record seek offset, this is so ImGuiListClipper::Seek() can be called after ImGuiListClipperData is done
   3077         clipper->StartSeekOffsetY = (double)data->LossynessOffset - data->ItemsFrozen * (double)clipper->ItemsHeight;
   3078 
   3079         if (g.LogEnabled)
   3080         {
   3081             // If logging is active, do not perform any clipping
   3082             data->Ranges.push_back(ImGuiListClipperRange::FromIndices(0, clipper->ItemsCount));
   3083         }
   3084         else
   3085         {
   3086             // Add range selected to be included for navigation
   3087             const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);
   3088             if (is_nav_request)
   3089                 data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, 0, 0));
   3090             if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && g.NavTabbingDir == -1)
   3091                 data->Ranges.push_back(ImGuiListClipperRange::FromIndices(clipper->ItemsCount - 1, clipper->ItemsCount));
   3092 
   3093             // Add focused/active item
   3094             ImRect nav_rect_abs = ImGui::WindowRectRelToAbs(window, window->NavRectRel[0]);
   3095             if (g.NavId != 0 && window->NavLastIds[0] == g.NavId)
   3096                 data->Ranges.push_back(ImGuiListClipperRange::FromPositions(nav_rect_abs.Min.y, nav_rect_abs.Max.y, 0, 0));
   3097 
   3098             // Add visible range
   3099             float min_y = window->ClipRect.Min.y;
   3100             float max_y = window->ClipRect.Max.y;
   3101 
   3102             // Add box selection range
   3103             ImGuiBoxSelectState* bs = &g.BoxSelectState;
   3104             if (bs->IsActive && bs->Window == window)
   3105             {
   3106                 // FIXME: Selectable() use of half-ItemSpacing isn't consistent in matter of layout, as ItemAdd(bb) stray above ItemSize()'s CursorPos.
   3107                 // RangeSelect's BoxSelect relies on comparing overlap of previous and current rectangle and is sensitive to that.
   3108                 // As a workaround we currently half ItemSpacing worth on each side.
   3109                 min_y -= g.Style.ItemSpacing.y;
   3110                 max_y += g.Style.ItemSpacing.y;
   3111 
   3112                 // Box-select on 2D area requires different clipping.
   3113                 if (bs->UnclipMode)
   3114                     data->Ranges.push_back(ImGuiListClipperRange::FromPositions(bs->UnclipRect.Min.y, bs->UnclipRect.Max.y, 0, 0));
   3115             }
   3116 
   3117             const int off_min = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0;
   3118             const int off_max = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) ? 1 : 0;
   3119             data->Ranges.push_back(ImGuiListClipperRange::FromPositions(min_y, max_y, off_min, off_max));
   3120         }
   3121 
   3122         // Convert position ranges to item index ranges
   3123         // - Very important: when a starting position is after our maximum item, we set Min to (ItemsCount - 1). This allows us to handle most forms of wrapping.
   3124         // - Due to how Selectable extra padding they tend to be "unaligned" with exact unit in the item list,
   3125         //   which with the flooring/ceiling tend to lead to 2 items instead of one being submitted.
   3126         for (ImGuiListClipperRange& range : data->Ranges)
   3127             if (range.PosToIndexConvert)
   3128             {
   3129                 int m1 = (int)(((double)range.Min - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight);
   3130                 int m2 = (int)((((double)range.Max - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight) + 0.999999f);
   3131                 range.Min = ImClamp(already_submitted + m1 + range.PosToIndexOffsetMin, already_submitted, clipper->ItemsCount - 1);
   3132                 range.Max = ImClamp(already_submitted + m2 + range.PosToIndexOffsetMax, range.Min + 1, clipper->ItemsCount);
   3133                 range.PosToIndexConvert = false;
   3134             }
   3135         ImGuiListClipper_SortAndFuseRanges(data->Ranges, data->StepNo);
   3136     }
   3137 
   3138     // Step 0+ (if item height is given in advance) or 1+: Display the next range in line.
   3139     while (data->StepNo < data->Ranges.Size)
   3140     {
   3141         clipper->DisplayStart = ImMax(data->Ranges[data->StepNo].Min, already_submitted);
   3142         clipper->DisplayEnd = ImMin(data->Ranges[data->StepNo].Max, clipper->ItemsCount);
   3143         if (clipper->DisplayStart > already_submitted) //-V1051
   3144             clipper->SeekCursorForItem(clipper->DisplayStart);
   3145         data->StepNo++;
   3146         if (clipper->DisplayStart == clipper->DisplayEnd && data->StepNo < data->Ranges.Size)
   3147             continue;
   3148         return true;
   3149     }
   3150 
   3151     // After the last step: Let the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd),
   3152     // Advance the cursor to the end of the list and then returns 'false' to end the loop.
   3153     if (clipper->ItemsCount < INT_MAX)
   3154         clipper->SeekCursorForItem(clipper->ItemsCount);
   3155 
   3156     return false;
   3157 }
   3158 
   3159 bool ImGuiListClipper::Step()
   3160 {
   3161     ImGuiContext& g = *Ctx;
   3162     bool need_items_height = (ItemsHeight <= 0.0f);
   3163     bool ret = ImGuiListClipper_StepInternal(this);
   3164     if (ret && (DisplayStart == DisplayEnd))
   3165         ret = false;
   3166     if (g.CurrentTable && g.CurrentTable->IsUnfrozenRows == false)
   3167         IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): inside frozen table row.\n");
   3168     if (need_items_height && ItemsHeight > 0.0f)
   3169         IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): computed ItemsHeight: %.2f.\n", ItemsHeight);
   3170     if (ret)
   3171     {
   3172         IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): display %d to %d.\n", DisplayStart, DisplayEnd);
   3173     }
   3174     else
   3175     {
   3176         IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): End.\n");
   3177         End();
   3178     }
   3179     return ret;
   3180 }
   3181 
   3182 //-----------------------------------------------------------------------------
   3183 // [SECTION] STYLING
   3184 //-----------------------------------------------------------------------------
   3185 
   3186 ImGuiStyle& ImGui::GetStyle()
   3187 {
   3188     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
   3189     return GImGui->Style;
   3190 }
   3191 
   3192 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
   3193 {
   3194     ImGuiStyle& style = GImGui->Style;
   3195     ImVec4 c = style.Colors[idx];
   3196     c.w *= style.Alpha * alpha_mul;
   3197     return ColorConvertFloat4ToU32(c);
   3198 }
   3199 
   3200 ImU32 ImGui::GetColorU32(const ImVec4& col)
   3201 {
   3202     ImGuiStyle& style = GImGui->Style;
   3203     ImVec4 c = col;
   3204     c.w *= style.Alpha;
   3205     return ColorConvertFloat4ToU32(c);
   3206 }
   3207 
   3208 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
   3209 {
   3210     ImGuiStyle& style = GImGui->Style;
   3211     return style.Colors[idx];
   3212 }
   3213 
   3214 ImU32 ImGui::GetColorU32(ImU32 col, float alpha_mul)
   3215 {
   3216     ImGuiStyle& style = GImGui->Style;
   3217     alpha_mul *= style.Alpha;
   3218     if (alpha_mul >= 1.0f)
   3219         return col;
   3220     ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
   3221     a = (ImU32)(a * alpha_mul); // We don't need to clamp 0..255 because alpha is in 0..1 range.
   3222     return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
   3223 }
   3224 
   3225 // 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
   3226 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
   3227 {
   3228     ImGuiContext& g = *GImGui;
   3229     ImGuiColorMod backup;
   3230     backup.Col = idx;
   3231     backup.BackupValue = g.Style.Colors[idx];
   3232     g.ColorStack.push_back(backup);
   3233     if (g.DebugFlashStyleColorIdx != idx)
   3234         g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
   3235 }
   3236 
   3237 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
   3238 {
   3239     ImGuiContext& g = *GImGui;
   3240     ImGuiColorMod backup;
   3241     backup.Col = idx;
   3242     backup.BackupValue = g.Style.Colors[idx];
   3243     g.ColorStack.push_back(backup);
   3244     if (g.DebugFlashStyleColorIdx != idx)
   3245         g.Style.Colors[idx] = col;
   3246 }
   3247 
   3248 void ImGui::PopStyleColor(int count)
   3249 {
   3250     ImGuiContext& g = *GImGui;
   3251     if (g.ColorStack.Size < count)
   3252     {
   3253         IM_ASSERT_USER_ERROR(g.ColorStack.Size > count, "Calling PopStyleColor() too many times!");
   3254         count = g.ColorStack.Size;
   3255     }
   3256     while (count > 0)
   3257     {
   3258         ImGuiColorMod& backup = g.ColorStack.back();
   3259         g.Style.Colors[backup.Col] = backup.BackupValue;
   3260         g.ColorStack.pop_back();
   3261         count--;
   3262     }
   3263 }
   3264 
   3265 static const ImGuiDataVarInfo GStyleVarInfo[] =
   3266 {
   3267     { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, Alpha) },                     // ImGuiStyleVar_Alpha
   3268     { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, DisabledAlpha) },             // ImGuiStyleVar_DisabledAlpha
   3269     { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowPadding) },             // ImGuiStyleVar_WindowPadding
   3270     { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, WindowRounding) },            // ImGuiStyleVar_WindowRounding
   3271     { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, WindowBorderSize) },          // ImGuiStyleVar_WindowBorderSize
   3272     { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowMinSize) },             // ImGuiStyleVar_WindowMinSize
   3273     { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowTitleAlign) },          // ImGuiStyleVar_WindowTitleAlign
   3274     { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ChildRounding) },             // ImGuiStyleVar_ChildRounding
   3275     { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ChildBorderSize) },           // ImGuiStyleVar_ChildBorderSize
   3276     { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, PopupRounding) },             // ImGuiStyleVar_PopupRounding
   3277     { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, PopupBorderSize) },           // ImGuiStyleVar_PopupBorderSize
   3278     { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, FramePadding) },              // ImGuiStyleVar_FramePadding
   3279     { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, FrameRounding) },             // ImGuiStyleVar_FrameRounding
   3280     { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, FrameBorderSize) },           // ImGuiStyleVar_FrameBorderSize
   3281     { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ItemSpacing) },               // ImGuiStyleVar_ItemSpacing
   3282     { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ItemInnerSpacing) },          // ImGuiStyleVar_ItemInnerSpacing
   3283     { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, IndentSpacing) },             // ImGuiStyleVar_IndentSpacing
   3284     { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, CellPadding) },               // ImGuiStyleVar_CellPadding
   3285     { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ScrollbarSize) },             // ImGuiStyleVar_ScrollbarSize
   3286     { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ScrollbarRounding) },         // ImGuiStyleVar_ScrollbarRounding
   3287     { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, GrabMinSize) },               // ImGuiStyleVar_GrabMinSize
   3288     { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, GrabRounding) },              // ImGuiStyleVar_GrabRounding
   3289     { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabRounding) },               // ImGuiStyleVar_TabRounding
   3290     { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBorderSize) },             // ImGuiStyleVar_TabBorderSize
   3291     { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBarBorderSize) },          // ImGuiStyleVar_TabBarBorderSize
   3292     { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBarOverlineSize) },        // ImGuiStyleVar_TabBarOverlineSize
   3293     { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersAngle)},    // ImGuiStyleVar_TableAngledHeadersAngle
   3294     { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersTextAlign)},// ImGuiStyleVar_TableAngledHeadersTextAlign
   3295     { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ButtonTextAlign) },           // ImGuiStyleVar_ButtonTextAlign
   3296     { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SelectableTextAlign) },       // ImGuiStyleVar_SelectableTextAlign
   3297     { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, SeparatorTextBorderSize)},    // ImGuiStyleVar_SeparatorTextBorderSize
   3298     { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextAlign) },        // ImGuiStyleVar_SeparatorTextAlign
   3299     { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextPadding) },      // ImGuiStyleVar_SeparatorTextPadding
   3300 };
   3301 
   3302 const ImGuiDataVarInfo* ImGui::GetStyleVarInfo(ImGuiStyleVar idx)
   3303 {
   3304     IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
   3305     IM_STATIC_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
   3306     return &GStyleVarInfo[idx];
   3307 }
   3308 
   3309 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
   3310 {
   3311     ImGuiContext& g = *GImGui;
   3312     const ImGuiDataVarInfo* var_info = GetStyleVarInfo(idx);
   3313     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
   3314     {
   3315         float* pvar = (float*)var_info->GetVarPtr(&g.Style);
   3316         g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
   3317         *pvar = val;
   3318         return;
   3319     }
   3320     IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
   3321 }
   3322 
   3323 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
   3324 {
   3325     ImGuiContext& g = *GImGui;
   3326     const ImGuiDataVarInfo* var_info = GetStyleVarInfo(idx);
   3327     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
   3328     {
   3329         ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
   3330         g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
   3331         *pvar = val;
   3332         return;
   3333     }
   3334     IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
   3335 }
   3336 
   3337 void ImGui::PopStyleVar(int count)
   3338 {
   3339     ImGuiContext& g = *GImGui;
   3340     if (g.StyleVarStack.Size < count)
   3341     {
   3342         IM_ASSERT_USER_ERROR(g.StyleVarStack.Size > count, "Calling PopStyleVar() too many times!");
   3343         count = g.StyleVarStack.Size;
   3344     }
   3345     while (count > 0)
   3346     {
   3347         // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
   3348         ImGuiStyleMod& backup = g.StyleVarStack.back();
   3349         const ImGuiDataVarInfo* info = GetStyleVarInfo(backup.VarIdx);
   3350         void* data = info->GetVarPtr(&g.Style);
   3351         if (info->Type == ImGuiDataType_Float && info->Count == 1)      { ((float*)data)[0] = backup.BackupFloat[0]; }
   3352         else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
   3353         g.StyleVarStack.pop_back();
   3354         count--;
   3355     }
   3356 }
   3357 
   3358 const char* ImGui::GetStyleColorName(ImGuiCol idx)
   3359 {
   3360     // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
   3361     switch (idx)
   3362     {
   3363     case ImGuiCol_Text: return "Text";
   3364     case ImGuiCol_TextDisabled: return "TextDisabled";
   3365     case ImGuiCol_WindowBg: return "WindowBg";
   3366     case ImGuiCol_ChildBg: return "ChildBg";
   3367     case ImGuiCol_PopupBg: return "PopupBg";
   3368     case ImGuiCol_Border: return "Border";
   3369     case ImGuiCol_BorderShadow: return "BorderShadow";
   3370     case ImGuiCol_FrameBg: return "FrameBg";
   3371     case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
   3372     case ImGuiCol_FrameBgActive: return "FrameBgActive";
   3373     case ImGuiCol_TitleBg: return "TitleBg";
   3374     case ImGuiCol_TitleBgActive: return "TitleBgActive";
   3375     case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
   3376     case ImGuiCol_MenuBarBg: return "MenuBarBg";
   3377     case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
   3378     case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
   3379     case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
   3380     case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
   3381     case ImGuiCol_CheckMark: return "CheckMark";
   3382     case ImGuiCol_SliderGrab: return "SliderGrab";
   3383     case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
   3384     case ImGuiCol_Button: return "Button";
   3385     case ImGuiCol_ButtonHovered: return "ButtonHovered";
   3386     case ImGuiCol_ButtonActive: return "ButtonActive";
   3387     case ImGuiCol_Header: return "Header";
   3388     case ImGuiCol_HeaderHovered: return "HeaderHovered";
   3389     case ImGuiCol_HeaderActive: return "HeaderActive";
   3390     case ImGuiCol_Separator: return "Separator";
   3391     case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
   3392     case ImGuiCol_SeparatorActive: return "SeparatorActive";
   3393     case ImGuiCol_ResizeGrip: return "ResizeGrip";
   3394     case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
   3395     case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
   3396     case ImGuiCol_TabHovered: return "TabHovered";
   3397     case ImGuiCol_Tab: return "Tab";
   3398     case ImGuiCol_TabSelected: return "TabSelected";
   3399     case ImGuiCol_TabSelectedOverline: return "TabSelectedOverline";
   3400     case ImGuiCol_TabDimmed: return "TabDimmed";
   3401     case ImGuiCol_TabDimmedSelected: return "TabDimmedSelected";
   3402     case ImGuiCol_TabDimmedSelectedOverline: return "TabDimmedSelectedOverline";
   3403     case ImGuiCol_PlotLines: return "PlotLines";
   3404     case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
   3405     case ImGuiCol_PlotHistogram: return "PlotHistogram";
   3406     case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
   3407     case ImGuiCol_TableHeaderBg: return "TableHeaderBg";
   3408     case ImGuiCol_TableBorderStrong: return "TableBorderStrong";
   3409     case ImGuiCol_TableBorderLight: return "TableBorderLight";
   3410     case ImGuiCol_TableRowBg: return "TableRowBg";
   3411     case ImGuiCol_TableRowBgAlt: return "TableRowBgAlt";
   3412     case ImGuiCol_TextLink: return "TextLink";
   3413     case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
   3414     case ImGuiCol_DragDropTarget: return "DragDropTarget";
   3415     case ImGuiCol_NavHighlight: return "NavHighlight";
   3416     case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
   3417     case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
   3418     case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
   3419     }
   3420     IM_ASSERT(0);
   3421     return "Unknown";
   3422 }
   3423 
   3424 
   3425 //-----------------------------------------------------------------------------
   3426 // [SECTION] RENDER HELPERS
   3427 // Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change,
   3428 // we need a nicer separation between low-level functions and high-level functions relying on the ImGui context.
   3429 // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context.
   3430 //-----------------------------------------------------------------------------
   3431 
   3432 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
   3433 {
   3434     const char* text_display_end = text;
   3435     if (!text_end)
   3436         text_end = (const char*)-1;
   3437 
   3438     while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
   3439         text_display_end++;
   3440     return text_display_end;
   3441 }
   3442 
   3443 // Internal ImGui functions to render text
   3444 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
   3445 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
   3446 {
   3447     ImGuiContext& g = *GImGui;
   3448     ImGuiWindow* window = g.CurrentWindow;
   3449 
   3450     // Hide anything after a '##' string
   3451     const char* text_display_end;
   3452     if (hide_text_after_hash)
   3453     {
   3454         text_display_end = FindRenderedTextEnd(text, text_end);
   3455     }
   3456     else
   3457     {
   3458         if (!text_end)
   3459             text_end = text + strlen(text); // FIXME-OPT
   3460         text_display_end = text_end;
   3461     }
   3462 
   3463     if (text != text_display_end)
   3464     {
   3465         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
   3466         if (g.LogEnabled)
   3467             LogRenderedText(&pos, text, text_display_end);
   3468     }
   3469 }
   3470 
   3471 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
   3472 {
   3473     ImGuiContext& g = *GImGui;
   3474     ImGuiWindow* window = g.CurrentWindow;
   3475 
   3476     if (!text_end)
   3477         text_end = text + strlen(text); // FIXME-OPT
   3478 
   3479     if (text != text_end)
   3480     {
   3481         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
   3482         if (g.LogEnabled)
   3483             LogRenderedText(&pos, text, text_end);
   3484     }
   3485 }
   3486 
   3487 // Default clip_rect uses (pos_min,pos_max)
   3488 // Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
   3489 // FIXME-OPT: Since we have or calculate text_size we could coarse clip whole block immediately, especally for text above draw_list->DrawList.
   3490 // Effectively as this is called from widget doing their own coarse clipping it's not very valuable presently. Next time function will take
   3491 // better advantage of the render function taking size into account for coarse clipping.
   3492 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)
   3493 {
   3494     // Perform CPU side clipping for single clipped element to avoid using scissor state
   3495     ImVec2 pos = pos_min;
   3496     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
   3497 
   3498     const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
   3499     const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
   3500     bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
   3501     if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
   3502         need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
   3503 
   3504     // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
   3505     if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
   3506     if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
   3507 
   3508     // Render
   3509     if (need_clipping)
   3510     {
   3511         ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
   3512         draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
   3513     }
   3514     else
   3515     {
   3516         draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
   3517     }
   3518 }
   3519 
   3520 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)
   3521 {
   3522     // Hide anything after a '##' string
   3523     const char* text_display_end = FindRenderedTextEnd(text, text_end);
   3524     const int text_len = (int)(text_display_end - text);
   3525     if (text_len == 0)
   3526         return;
   3527 
   3528     ImGuiContext& g = *GImGui;
   3529     ImGuiWindow* window = g.CurrentWindow;
   3530     RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
   3531     if (g.LogEnabled)
   3532         LogRenderedText(&pos_min, text, text_display_end);
   3533 }
   3534 
   3535 // Another overly complex function until we reorganize everything into a nice all-in-one helper.
   3536 // This is made more complex because we have dissociated the layout rectangle (pos_min..pos_max) which define _where_ the ellipsis is, from actual clipping of text and limit of the ellipsis display.
   3537 // This is because in the context of tabs we selectively hide part of the text when the Close Button appears, but we don't want the ellipsis to move.
   3538 void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end_full, const ImVec2* text_size_if_known)
   3539 {
   3540     ImGuiContext& g = *GImGui;
   3541     if (text_end_full == NULL)
   3542         text_end_full = FindRenderedTextEnd(text);
   3543     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);
   3544 
   3545     //draw_list->AddLine(ImVec2(pos_max.x, pos_min.y - 4), ImVec2(pos_max.x, pos_max.y + 4), IM_COL32(0, 0, 255, 255));
   3546     //draw_list->AddLine(ImVec2(ellipsis_max_x, pos_min.y-2), ImVec2(ellipsis_max_x, pos_max.y+2), IM_COL32(0, 255, 0, 255));
   3547     //draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255));
   3548     // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
   3549     if (text_size.x > pos_max.x - pos_min.x)
   3550     {
   3551         // Hello wo...
   3552         // |       |   |
   3553         // min   max   ellipsis_max
   3554         //          <-> this is generally some padding value
   3555 
   3556         const ImFont* font = draw_list->_Data->Font;
   3557         const float font_size = draw_list->_Data->FontSize;
   3558         const float font_scale = draw_list->_Data->FontScale;
   3559         const char* text_end_ellipsis = NULL;
   3560         const float ellipsis_width = font->EllipsisWidth * font_scale;
   3561 
   3562         // We can now claim the space between pos_max.x and ellipsis_max.x
   3563         const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_width) - pos_min.x, 1.0f);
   3564         float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
   3565         if (text == text_end_ellipsis && text_end_ellipsis < text_end_full)
   3566         {
   3567             // Always display at least 1 character if there's no room for character + ellipsis
   3568             text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full);
   3569             text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x;
   3570         }
   3571         while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1]))
   3572         {
   3573             // Trim trailing space before ellipsis (FIXME: Supporting non-ascii blanks would be nice, for this we need a function to backtrack in UTF-8 text)
   3574             text_end_ellipsis--;
   3575             text_size_clipped_x -= font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text_end_ellipsis, text_end_ellipsis + 1).x; // Ascii blanks are always 1 byte
   3576         }
   3577 
   3578         // Render text, render ellipsis
   3579         RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
   3580         ImVec2 ellipsis_pos = ImTrunc(ImVec2(pos_min.x + text_size_clipped_x, pos_min.y));
   3581         if (ellipsis_pos.x + ellipsis_width <= ellipsis_max_x)
   3582             for (int i = 0; i < font->EllipsisCharCount; i++, ellipsis_pos.x += font->EllipsisCharStep * font_scale)
   3583                 font->RenderChar(draw_list, font_size, ellipsis_pos, GetColorU32(ImGuiCol_Text), font->EllipsisChar);
   3584     }
   3585     else
   3586     {
   3587         RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));
   3588     }
   3589 
   3590     if (g.LogEnabled)
   3591         LogRenderedText(&pos_min, text, text_end_full);
   3592 }
   3593 
   3594 // Render a rectangle shaped with optional rounding and borders
   3595 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
   3596 {
   3597     ImGuiContext& g = *GImGui;
   3598     ImGuiWindow* window = g.CurrentWindow;
   3599     window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
   3600     const float border_size = g.Style.FrameBorderSize;
   3601     if (border && border_size > 0.0f)
   3602     {
   3603         window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);
   3604         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
   3605     }
   3606 }
   3607 
   3608 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
   3609 {
   3610     ImGuiContext& g = *GImGui;
   3611     ImGuiWindow* window = g.CurrentWindow;
   3612     const float border_size = g.Style.FrameBorderSize;
   3613     if (border_size > 0.0f)
   3614     {
   3615         window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);
   3616         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
   3617     }
   3618 }
   3619 
   3620 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
   3621 {
   3622     ImGuiContext& g = *GImGui;
   3623     if (id != g.NavId)
   3624         return;
   3625     if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
   3626         return;
   3627     ImGuiWindow* window = g.CurrentWindow;
   3628     if (window->DC.NavHideHighlightOneFrame)
   3629         return;
   3630 
   3631     float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
   3632     ImRect display_rect = bb;
   3633     display_rect.ClipWith(window->ClipRect);
   3634     const float thickness = 2.0f;
   3635     if (flags & ImGuiNavHighlightFlags_Compact)
   3636     {
   3637         window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, 0, thickness);
   3638     }
   3639     else
   3640     {
   3641         const float distance = 3.0f + thickness * 0.5f;
   3642         display_rect.Expand(ImVec2(distance, distance));
   3643         bool fully_visible = window->ClipRect.Contains(display_rect);
   3644         if (!fully_visible)
   3645             window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
   3646         window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, 0, thickness);
   3647         if (!fully_visible)
   3648             window->DrawList->PopClipRect();
   3649     }
   3650 }
   3651 
   3652 void ImGui::RenderMouseCursor(ImVec2 base_pos, float base_scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow)
   3653 {
   3654     ImGuiContext& g = *GImGui;
   3655     IM_ASSERT(mouse_cursor > ImGuiMouseCursor_None && mouse_cursor < ImGuiMouseCursor_COUNT);
   3656     ImFontAtlas* font_atlas = g.DrawListSharedData.Font->ContainerAtlas;
   3657     for (ImGuiViewportP* viewport : g.Viewports)
   3658     {
   3659         // We scale cursor with current viewport/monitor, however Windows 10 for its own hardware cursor seems to be using a different scale factor.
   3660         ImVec2 offset, size, uv[4];
   3661         if (!font_atlas->GetMouseCursorTexData(mouse_cursor, &offset, &size, &uv[0], &uv[2]))
   3662             continue;
   3663         const ImVec2 pos = base_pos - offset;
   3664         const float scale = base_scale;
   3665         if (!viewport->GetMainRect().Overlaps(ImRect(pos, pos + ImVec2(size.x + 2, size.y + 2) * scale)))
   3666             continue;
   3667         ImDrawList* draw_list = GetForegroundDrawList(viewport);
   3668         ImTextureID tex_id = font_atlas->TexID;
   3669         draw_list->PushTextureID(tex_id);
   3670         draw_list->AddImage(tex_id, pos + ImVec2(1, 0) * scale, pos + (ImVec2(1, 0) + size) * scale, uv[2], uv[3], col_shadow);
   3671         draw_list->AddImage(tex_id, pos + ImVec2(2, 0) * scale, pos + (ImVec2(2, 0) + size) * scale, uv[2], uv[3], col_shadow);
   3672         draw_list->AddImage(tex_id, pos,                        pos + size * scale,                  uv[2], uv[3], col_border);
   3673         draw_list->AddImage(tex_id, pos,                        pos + size * scale,                  uv[0], uv[1], col_fill);
   3674         draw_list->PopTextureID();
   3675     }
   3676 }
   3677 
   3678 //-----------------------------------------------------------------------------
   3679 // [SECTION] INITIALIZATION, SHUTDOWN
   3680 //-----------------------------------------------------------------------------
   3681 
   3682 // Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself
   3683 // 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
   3684 ImGuiContext* ImGui::GetCurrentContext()
   3685 {
   3686     return GImGui;
   3687 }
   3688 
   3689 void ImGui::SetCurrentContext(ImGuiContext* ctx)
   3690 {
   3691 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
   3692     IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
   3693 #else
   3694     GImGui = ctx;
   3695 #endif
   3696 }
   3697 
   3698 void ImGui::SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data)
   3699 {
   3700     GImAllocatorAllocFunc = alloc_func;
   3701     GImAllocatorFreeFunc = free_func;
   3702     GImAllocatorUserData = user_data;
   3703 }
   3704 
   3705 // This is provided to facilitate copying allocators from one static/DLL boundary to another (e.g. retrieve default allocator of your executable address space)
   3706 void ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data)
   3707 {
   3708     *p_alloc_func = GImAllocatorAllocFunc;
   3709     *p_free_func = GImAllocatorFreeFunc;
   3710     *p_user_data = GImAllocatorUserData;
   3711 }
   3712 
   3713 ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
   3714 {
   3715     ImGuiContext* prev_ctx = GetCurrentContext();
   3716     ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
   3717     SetCurrentContext(ctx);
   3718     Initialize();
   3719     if (prev_ctx != NULL)
   3720         SetCurrentContext(prev_ctx); // Restore previous context if any, else keep new one.
   3721     return ctx;
   3722 }
   3723 
   3724 void ImGui::DestroyContext(ImGuiContext* ctx)
   3725 {
   3726     ImGuiContext* prev_ctx = GetCurrentContext();
   3727     if (ctx == NULL) //-V1051
   3728         ctx = prev_ctx;
   3729     SetCurrentContext(ctx);
   3730     Shutdown();
   3731     SetCurrentContext((prev_ctx != ctx) ? prev_ctx : NULL);
   3732     IM_DELETE(ctx);
   3733 }
   3734 
   3735 // IMPORTANT: ###xxx suffixes must be same in ALL languages to allow for automation.
   3736 static const ImGuiLocEntry GLocalizationEntriesEnUS[] =
   3737 {
   3738     { ImGuiLocKey_VersionStr,           "Dear ImGui " IMGUI_VERSION " (" IM_STRINGIFY(IMGUI_VERSION_NUM) ")" },
   3739     { ImGuiLocKey_TableSizeOne,         "Size column to fit###SizeOne"          },
   3740     { ImGuiLocKey_TableSizeAllFit,      "Size all columns to fit###SizeAll"     },
   3741     { ImGuiLocKey_TableSizeAllDefault,  "Size all columns to default###SizeAll" },
   3742     { ImGuiLocKey_TableResetOrder,      "Reset order###ResetOrder"              },
   3743     { ImGuiLocKey_WindowingMainMenuBar, "(Main menu bar)"                       },
   3744     { ImGuiLocKey_WindowingPopup,       "(Popup)"                               },
   3745     { ImGuiLocKey_WindowingUntitled,    "(Untitled)"                            },
   3746     { ImGuiLocKey_CopyLink,             "Copy Link###CopyLink"                   },
   3747 };
   3748 
   3749 void ImGui::Initialize()
   3750 {
   3751     ImGuiContext& g = *GImGui;
   3752     IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
   3753 
   3754     // Add .ini handle for ImGuiWindow and ImGuiTable types
   3755     {
   3756         ImGuiSettingsHandler ini_handler;
   3757         ini_handler.TypeName = "Window";
   3758         ini_handler.TypeHash = ImHashStr("Window");
   3759         ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll;
   3760         ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen;
   3761         ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;
   3762         ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll;
   3763         ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll;
   3764         AddSettingsHandler(&ini_handler);
   3765     }
   3766     TableSettingsAddSettingsHandler();
   3767 
   3768     // Setup default localization table
   3769     LocalizeRegisterEntries(GLocalizationEntriesEnUS, IM_ARRAYSIZE(GLocalizationEntriesEnUS));
   3770 
   3771     // Setup default platform clipboard/IME handlers.
   3772     g.IO.GetClipboardTextFn = GetClipboardTextFn_DefaultImpl;    // Platform dependent default implementations
   3773     g.IO.SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
   3774     g.IO.ClipboardUserData = (void*)&g;                          // Default implementation use the ImGuiContext as user data (ideally those would be arguments to the function)
   3775     g.IO.PlatformOpenInShellFn = PlatformOpenInShellFn_DefaultImpl;
   3776     g.IO.PlatformSetImeDataFn = PlatformSetImeDataFn_DefaultImpl;
   3777 
   3778     // Create default viewport
   3779     ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)();
   3780     viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID;
   3781     g.Viewports.push_back(viewport);
   3782     g.TempBuffer.resize(1024 * 3 + 1, 0);
   3783 
   3784     // Build KeysMayBeCharInput[] lookup table (1 bool per named key)
   3785     for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
   3786         if ((key >= ImGuiKey_0 && key <= ImGuiKey_9) || (key >= ImGuiKey_A && key <= ImGuiKey_Z) || (key >= ImGuiKey_Keypad0 && key <= ImGuiKey_Keypad9)
   3787             || key == ImGuiKey_Tab || key == ImGuiKey_Space || key == ImGuiKey_Apostrophe || key == ImGuiKey_Comma || key == ImGuiKey_Minus || key == ImGuiKey_Period
   3788             || key == ImGuiKey_Slash || key == ImGuiKey_Semicolon || key == ImGuiKey_Equal || key == ImGuiKey_LeftBracket || key == ImGuiKey_RightBracket || key == ImGuiKey_GraveAccent
   3789             || key == ImGuiKey_KeypadDecimal || key == ImGuiKey_KeypadDivide || key == ImGuiKey_KeypadMultiply || key == ImGuiKey_KeypadSubtract || key == ImGuiKey_KeypadAdd || key == ImGuiKey_KeypadEqual)
   3790             g.KeysMayBeCharInput.SetBit(key);
   3791 
   3792 #ifdef IMGUI_HAS_DOCK
   3793 #endif
   3794 
   3795     g.Initialized = true;
   3796 }
   3797 
   3798 // This function is merely here to free heap allocations.
   3799 void ImGui::Shutdown()
   3800 {
   3801     ImGuiContext& g = *GImGui;
   3802     IM_ASSERT_USER_ERROR(g.IO.BackendPlatformUserData == NULL, "Forgot to shutdown Platform backend?");
   3803     IM_ASSERT_USER_ERROR(g.IO.BackendRendererUserData == NULL, "Forgot to shutdown Renderer backend?");
   3804 
   3805     // 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)
   3806     if (g.IO.Fonts && g.FontAtlasOwnedByContext)
   3807     {
   3808         g.IO.Fonts->Locked = false;
   3809         IM_DELETE(g.IO.Fonts);
   3810     }
   3811     g.IO.Fonts = NULL;
   3812     g.DrawListSharedData.TempBuffer.clear();
   3813 
   3814     // Cleanup of other data are conditional on actually having initialized Dear ImGui.
   3815     if (!g.Initialized)
   3816         return;
   3817 
   3818     // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
   3819     if (g.SettingsLoaded && g.IO.IniFilename != NULL)
   3820         SaveIniSettingsToDisk(g.IO.IniFilename);
   3821 
   3822     CallContextHooks(&g, ImGuiContextHookType_Shutdown);
   3823 
   3824     // Clear everything else
   3825     g.Windows.clear_delete();
   3826     g.WindowsFocusOrder.clear();
   3827     g.WindowsTempSortBuffer.clear();
   3828     g.CurrentWindow = NULL;
   3829     g.CurrentWindowStack.clear();
   3830     g.WindowsById.Clear();
   3831     g.NavWindow = NULL;
   3832     g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
   3833     g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
   3834     g.MovingWindow = NULL;
   3835 
   3836     g.KeysRoutingTable.Clear();
   3837 
   3838     g.ColorStack.clear();
   3839     g.StyleVarStack.clear();
   3840     g.FontStack.clear();
   3841     g.OpenPopupStack.clear();
   3842     g.BeginPopupStack.clear();
   3843     g.TreeNodeStack.clear();
   3844 
   3845     g.Viewports.clear_delete();
   3846 
   3847     g.TabBars.Clear();
   3848     g.CurrentTabBarStack.clear();
   3849     g.ShrinkWidthBuffer.clear();
   3850 
   3851     g.ClipperTempData.clear_destruct();
   3852 
   3853     g.Tables.Clear();
   3854     g.TablesTempData.clear_destruct();
   3855     g.DrawChannelsTempMergeBuffer.clear();
   3856 
   3857     g.MultiSelectStorage.Clear();
   3858     g.MultiSelectTempData.clear_destruct();
   3859 
   3860     g.ClipboardHandlerData.clear();
   3861     g.MenusIdSubmittedThisFrame.clear();
   3862     g.InputTextState.ClearFreeMemory();
   3863     g.InputTextDeactivatedState.ClearFreeMemory();
   3864 
   3865     g.SettingsWindows.clear();
   3866     g.SettingsHandlers.clear();
   3867 
   3868     if (g.LogFile)
   3869     {
   3870 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
   3871         if (g.LogFile != stdout)
   3872 #endif
   3873             ImFileClose(g.LogFile);
   3874         g.LogFile = NULL;
   3875     }
   3876     g.LogBuffer.clear();
   3877     g.DebugLogBuf.clear();
   3878     g.DebugLogIndex.clear();
   3879 
   3880     g.Initialized = false;
   3881 }
   3882 
   3883 // No specific ordering/dependency support, will see as needed
   3884 ImGuiID ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook)
   3885 {
   3886     ImGuiContext& g = *ctx;
   3887     IM_ASSERT(hook->Callback != NULL && hook->HookId == 0 && hook->Type != ImGuiContextHookType_PendingRemoval_);
   3888     g.Hooks.push_back(*hook);
   3889     g.Hooks.back().HookId = ++g.HookIdNext;
   3890     return g.HookIdNext;
   3891 }
   3892 
   3893 // Deferred removal, avoiding issue with changing vector while iterating it
   3894 void ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id)
   3895 {
   3896     ImGuiContext& g = *ctx;
   3897     IM_ASSERT(hook_id != 0);
   3898     for (ImGuiContextHook& hook : g.Hooks)
   3899         if (hook.HookId == hook_id)
   3900             hook.Type = ImGuiContextHookType_PendingRemoval_;
   3901 }
   3902 
   3903 // Call context hooks (used by e.g. test engine)
   3904 // We assume a small number of hooks so all stored in same array
   3905 void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type)
   3906 {
   3907     ImGuiContext& g = *ctx;
   3908     for (ImGuiContextHook& hook : g.Hooks)
   3909         if (hook.Type == hook_type)
   3910             hook.Callback(&g, &hook);
   3911 }
   3912 
   3913 
   3914 //-----------------------------------------------------------------------------
   3915 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
   3916 //-----------------------------------------------------------------------------
   3917 
   3918 // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
   3919 ImGuiWindow::ImGuiWindow(ImGuiContext* ctx, const char* name) : DrawListInst(NULL)
   3920 {
   3921     memset(this, 0, sizeof(*this));
   3922     Ctx = ctx;
   3923     Name = ImStrdup(name);
   3924     NameBufLen = (int)strlen(name) + 1;
   3925     ID = ImHashStr(name);
   3926     IDStack.push_back(ID);
   3927     MoveId = GetID("#MOVE");
   3928     ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
   3929     ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
   3930     AutoFitFramesX = AutoFitFramesY = -1;
   3931     AutoPosLastDirection = ImGuiDir_None;
   3932     SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = 0;
   3933     SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
   3934     LastFrameActive = -1;
   3935     LastTimeActive = -1.0f;
   3936     FontWindowScale = 1.0f;
   3937     SettingsOffset = -1;
   3938     DrawList = &DrawListInst;
   3939     DrawList->_Data = &Ctx->DrawListSharedData;
   3940     DrawList->_OwnerName = Name;
   3941     NavPreferredScoringPosRel[0] = NavPreferredScoringPosRel[1] = ImVec2(FLT_MAX, FLT_MAX);
   3942 }
   3943 
   3944 ImGuiWindow::~ImGuiWindow()
   3945 {
   3946     IM_ASSERT(DrawList == &DrawListInst);
   3947     IM_DELETE(Name);
   3948     ColumnsStorage.clear_destruct();
   3949 }
   3950 
   3951 static void SetCurrentWindow(ImGuiWindow* window)
   3952 {
   3953     ImGuiContext& g = *GImGui;
   3954     g.CurrentWindow = window;
   3955     g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL;
   3956     g.CurrentDpiScale = 1.0f; // FIXME-DPI: WIP this is modified in docking
   3957     if (window)
   3958     {
   3959         g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
   3960         g.FontScale = g.FontSize / g.Font->FontSize;
   3961         ImGui::NavUpdateCurrentWindowIsScrollPushableX();
   3962     }
   3963 }
   3964 
   3965 void ImGui::GcCompactTransientMiscBuffers()
   3966 {
   3967     ImGuiContext& g = *GImGui;
   3968     g.ItemFlagsStack.clear();
   3969     g.GroupStack.clear();
   3970     g.MultiSelectTempDataStacked = 0;
   3971     g.MultiSelectTempData.clear_destruct();
   3972     TableGcCompactSettings();
   3973 }
   3974 
   3975 // Free up/compact internal window buffers, we can use this when a window becomes unused.
   3976 // Not freed:
   3977 // - ImGuiWindow, ImGuiWindowSettings, Name, StateStorage, ColumnsStorage (may hold useful data)
   3978 // This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.
   3979 void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window)
   3980 {
   3981     window->MemoryCompacted = true;
   3982     window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;
   3983     window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;
   3984     window->IDStack.clear();
   3985     window->DrawList->_ClearFreeMemory();
   3986     window->DC.ChildWindows.clear();
   3987     window->DC.ItemWidthStack.clear();
   3988     window->DC.TextWrapPosStack.clear();
   3989 }
   3990 
   3991 void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window)
   3992 {
   3993     // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.
   3994     // The other buffers tends to amortize much faster.
   3995     window->MemoryCompacted = false;
   3996     window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity);
   3997     window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity);
   3998     window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0;
   3999 }
   4000 
   4001 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
   4002 {
   4003     ImGuiContext& g = *GImGui;
   4004 
   4005     // Clear previous active id
   4006     if (g.ActiveId != 0)
   4007     {
   4008         // While most behaved code would make an effort to not steal active id during window move/drag operations,
   4009         // we at least need to be resilient to it. Canceling the move is rather aggressive and users of 'master' branch
   4010         // may prefer the weird ill-defined half working situation ('docking' did assert), so may need to rework that.
   4011         if (g.MovingWindow != NULL && g.ActiveId == g.MovingWindow->MoveId)
   4012         {
   4013             IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() cancel MovingWindow\n");
   4014             g.MovingWindow = NULL;
   4015         }
   4016 
   4017         // This could be written in a more general way (e.g associate a hook to ActiveId),
   4018         // but since this is currently quite an exception we'll leave it as is.
   4019         // One common scenario leading to this is: pressing Key ->NavMoveRequestApplyResult() -> ClearActiveId()
   4020         if (g.InputTextState.ID == g.ActiveId)
   4021             InputTextDeactivateHook(g.ActiveId);
   4022     }
   4023 
   4024     // Set active id
   4025     g.ActiveIdIsJustActivated = (g.ActiveId != id);
   4026     if (g.ActiveIdIsJustActivated)
   4027     {
   4028         IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() old:0x%08X (window \"%s\") -> new:0x%08X (window \"%s\")\n", g.ActiveId, g.ActiveIdWindow ? g.ActiveIdWindow->Name : "", id, window ? window->Name : "");
   4029         g.ActiveIdTimer = 0.0f;
   4030         g.ActiveIdHasBeenPressedBefore = false;
   4031         g.ActiveIdHasBeenEditedBefore = false;
   4032         g.ActiveIdMouseButton = -1;
   4033         if (id != 0)
   4034         {
   4035             g.LastActiveId = id;
   4036             g.LastActiveIdTimer = 0.0f;
   4037         }
   4038     }
   4039     g.ActiveId = id;
   4040     g.ActiveIdAllowOverlap = false;
   4041     g.ActiveIdNoClearOnFocusLoss = false;
   4042     g.ActiveIdWindow = window;
   4043     g.ActiveIdHasBeenEditedThisFrame = false;
   4044     g.ActiveIdFromShortcut = false;
   4045     if (id)
   4046     {
   4047         g.ActiveIdIsAlive = id;
   4048         g.ActiveIdSource = (g.NavActivateId == id || g.NavJustMovedToId == id) ? g.NavInputSource : ImGuiInputSource_Mouse;
   4049         IM_ASSERT(g.ActiveIdSource != ImGuiInputSource_None);
   4050     }
   4051 
   4052     // Clear declaration of inputs claimed by the widget
   4053     // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
   4054     g.ActiveIdUsingNavDirMask = 0x00;
   4055     g.ActiveIdUsingAllKeyboardKeys = false;
   4056 }
   4057 
   4058 void ImGui::ClearActiveID()
   4059 {
   4060     SetActiveID(0, NULL); // g.ActiveId = 0;
   4061 }
   4062 
   4063 void ImGui::SetHoveredID(ImGuiID id)
   4064 {
   4065     ImGuiContext& g = *GImGui;
   4066     g.HoveredId = id;
   4067     g.HoveredIdAllowOverlap = false;
   4068     if (id != 0 && g.HoveredIdPreviousFrame != id)
   4069         g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
   4070 }
   4071 
   4072 ImGuiID ImGui::GetHoveredID()
   4073 {
   4074     ImGuiContext& g = *GImGui;
   4075     return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
   4076 }
   4077 
   4078 void ImGui::MarkItemEdited(ImGuiID id)
   4079 {
   4080     // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
   4081     // ActiveId might have been released by the time we call this (as in the typical press/release button behavior) but still need to fill the data.
   4082     ImGuiContext& g = *GImGui;
   4083     if (g.LockMarkEdited > 0)
   4084         return;
   4085     if (g.ActiveId == id || g.ActiveId == 0)
   4086     {
   4087         g.ActiveIdHasBeenEditedThisFrame = true;
   4088         g.ActiveIdHasBeenEditedBefore = true;
   4089     }
   4090 
   4091     // We accept a MarkItemEdited() on drag and drop targets (see https://github.com/ocornut/imgui/issues/1875#issuecomment-978243343)
   4092     // We accept 'ActiveIdPreviousFrame == id' for InputText() returning an edit after it has been taken ActiveId away (#4714)
   4093     IM_ASSERT(g.DragDropActive || g.ActiveId == id || g.ActiveId == 0 || g.ActiveIdPreviousFrame == id || (g.CurrentMultiSelect != NULL && g.BoxSelectState.IsActive));
   4094 
   4095     //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
   4096     g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;
   4097 }
   4098 
   4099 bool ImGui::IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
   4100 {
   4101     // An active popup disable hovering on other windows (apart from its own children)
   4102     // FIXME-OPT: This could be cached/stored within the window.
   4103     ImGuiContext& g = *GImGui;
   4104     if (g.NavWindow)
   4105         if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
   4106             if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
   4107             {
   4108                 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
   4109                 // NB: The 'else' is important because Modal windows are also Popups.
   4110                 bool want_inhibit = false;
   4111                 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
   4112                     want_inhibit = true;
   4113                 else if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
   4114                     want_inhibit = true;
   4115 
   4116                 // Inhibit hover unless the window is within the stack of our modal/popup
   4117                 if (want_inhibit)
   4118                     if (!IsWindowWithinBeginStackOf(window->RootWindow, focused_root_window))
   4119                         return false;
   4120             }
   4121     return true;
   4122 }
   4123 
   4124 static inline float CalcDelayFromHoveredFlags(ImGuiHoveredFlags flags)
   4125 {
   4126     ImGuiContext& g = *GImGui;
   4127     if (flags & ImGuiHoveredFlags_DelayNormal)
   4128         return g.Style.HoverDelayNormal;
   4129     if (flags & ImGuiHoveredFlags_DelayShort)
   4130         return g.Style.HoverDelayShort;
   4131     return 0.0f;
   4132 }
   4133 
   4134 static ImGuiHoveredFlags ApplyHoverFlagsForTooltip(ImGuiHoveredFlags user_flags, ImGuiHoveredFlags shared_flags)
   4135 {
   4136     // Allow instance flags to override shared flags
   4137     if (user_flags & (ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal))
   4138         shared_flags &= ~(ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal);
   4139     return user_flags | shared_flags;
   4140 }
   4141 
   4142 // This is roughly matching the behavior of internal-facing ItemHoverable()
   4143 // - 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()
   4144 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
   4145 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
   4146 {
   4147     ImGuiContext& g = *GImGui;
   4148     ImGuiWindow* window = g.CurrentWindow;
   4149     IM_ASSERT((flags & ~ImGuiHoveredFlags_AllowedMaskForIsItemHovered) == 0 && "Invalid flags for IsItemHovered()!");
   4150 
   4151     if (g.NavDisableMouseHover && !g.NavDisableHighlight && !(flags & ImGuiHoveredFlags_NoNavOverride))
   4152     {
   4153         if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
   4154             return false;
   4155         if (!IsItemFocused())
   4156             return false;
   4157 
   4158         if (flags & ImGuiHoveredFlags_ForTooltip)
   4159             flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipNav);
   4160     }
   4161     else
   4162     {
   4163         // Test for bounding box overlap, as updated as ItemAdd()
   4164         ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags;
   4165         if (!(status_flags & ImGuiItemStatusFlags_HoveredRect))
   4166             return false;
   4167 
   4168         if (flags & ImGuiHoveredFlags_ForTooltip)
   4169             flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse);
   4170 
   4171         IM_ASSERT((flags & (ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy)) == 0);   // Flags not supported by this function
   4172 
   4173         // Done with rectangle culling so we can perform heavier checks now
   4174         // Test if we are hovering the right window (our window could be behind another window)
   4175         // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851)
   4176         // [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
   4177         // to use IsItemHovered() after EndChild() itself. Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was
   4178         // the test that has been running for a long while.
   4179         if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0)
   4180             if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByWindow) == 0)
   4181                 return false;
   4182 
   4183         // Test if another item is active (e.g. being dragged)
   4184         const ImGuiID id = g.LastItemData.ID;
   4185         if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0)
   4186             if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
   4187                 return false;
   4188 
   4189         // Test if interactions on this window are blocked by an active popup or modal.
   4190         // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
   4191         if (!IsWindowContentHoverable(window, flags) && !(g.LastItemData.InFlags & ImGuiItemFlags_NoWindowHoverableCheck))
   4192             return false;
   4193 
   4194         // Test if the item is disabled
   4195         if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
   4196             return false;
   4197 
   4198         // Special handling for calling after Begin() which represent the title bar or tab.
   4199         // When the window is skipped/collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
   4200         if (id == window->MoveId && window->WriteAccessed)
   4201             return false;
   4202 
   4203         // Test if using AllowOverlap and overlapped
   4204         if ((g.LastItemData.InFlags & ImGuiItemFlags_AllowOverlap) && id != 0)
   4205             if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByItem) == 0)
   4206                 if (g.HoveredIdPreviousFrame != g.LastItemData.ID)
   4207                     return false;
   4208     }
   4209 
   4210     // Handle hover delay
   4211     // (some ideas: https://www.nngroup.com/articles/timing-exposing-content)
   4212     const float delay = CalcDelayFromHoveredFlags(flags);
   4213     if (delay > 0.0f || (flags & ImGuiHoveredFlags_Stationary))
   4214     {
   4215         ImGuiID hover_delay_id = (g.LastItemData.ID != 0) ? g.LastItemData.ID : window->GetIDFromRectangle(g.LastItemData.Rect);
   4216         if ((flags & ImGuiHoveredFlags_NoSharedDelay) && (g.HoverItemDelayIdPreviousFrame != hover_delay_id))
   4217             g.HoverItemDelayTimer = 0.0f;
   4218         g.HoverItemDelayId = hover_delay_id;
   4219 
   4220         // When changing hovered item we requires a bit of stationary delay before activating hover timer,
   4221         // but once unlocked on a given item we also moving.
   4222         //if (g.HoverDelayTimer >= delay && (g.HoverDelayTimer - g.IO.DeltaTime < delay || g.MouseStationaryTimer - g.IO.DeltaTime < g.Style.HoverStationaryDelay)) { IMGUI_DEBUG_LOG("HoverDelayTimer = %f/%f, MouseStationaryTimer = %f\n", g.HoverDelayTimer, delay, g.MouseStationaryTimer); }
   4223         if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverItemUnlockedStationaryId != hover_delay_id)
   4224             return false;
   4225 
   4226         if (g.HoverItemDelayTimer < delay)
   4227             return false;
   4228     }
   4229 
   4230     return true;
   4231 }
   4232 
   4233 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
   4234 // (this does not rely on LastItemData it can be called from a ButtonBehavior() call not following an ItemAdd() call)
   4235 // FIXME-LEGACY: the 'ImGuiItemFlags item_flags' parameter was added on 2023-06-28.
   4236 // If you used this in your legacy/custom widgets code:
   4237 // - Commonly: if your ItemHoverable() call comes after an ItemAdd() call: pass 'item_flags = g.LastItemData.InFlags'.
   4238 // - Rare: otherwise you may pass 'item_flags = 0' (ImGuiItemFlags_None) unless you want to benefit from special behavior handled by ItemHoverable.
   4239 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags)
   4240 {
   4241     ImGuiContext& g = *GImGui;
   4242     ImGuiWindow* window = g.CurrentWindow;
   4243     if (g.HoveredWindow != window)
   4244         return false;
   4245     if (!IsMouseHoveringRect(bb.Min, bb.Max))
   4246         return false;
   4247 
   4248     if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
   4249         return false;
   4250     if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
   4251         if (!g.ActiveIdFromShortcut)
   4252             return false;
   4253 
   4254     // Done with rectangle culling so we can perform heavier checks now.
   4255     if (!(item_flags & ImGuiItemFlags_NoWindowHoverableCheck) && !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
   4256     {
   4257         g.HoveredIdIsDisabled = true;
   4258         return false;
   4259     }
   4260 
   4261     // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level
   4262     // hover test in widgets code. We could also decide to split this function is two.
   4263     if (id != 0)
   4264     {
   4265         // Drag source doesn't report as hovered
   4266         if (g.DragDropActive && g.DragDropPayload.SourceId == id && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoDisableHover))
   4267             return false;
   4268 
   4269         SetHoveredID(id);
   4270 
   4271         // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match.
   4272         // This allows using patterns where a later submitted widget overlaps a previous one. Generally perceived as a front-to-back hit-test.
   4273         if (item_flags & ImGuiItemFlags_AllowOverlap)
   4274         {
   4275             g.HoveredIdAllowOverlap = true;
   4276             if (g.HoveredIdPreviousFrame != id)
   4277                 return false;
   4278         }
   4279 
   4280         // Display shortcut (only works with mouse)
   4281         // (ImGuiItemStatusFlags_HasShortcut in LastItemData denotes we want a tooltip)
   4282         if (id == g.LastItemData.ID && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasShortcut))
   4283             if (IsItemHovered(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_DelayNormal))
   4284                 SetTooltip("%s", GetKeyChordName(g.LastItemData.Shortcut));
   4285     }
   4286 
   4287     // When disabled we'll return false but still set HoveredId
   4288     if (item_flags & ImGuiItemFlags_Disabled)
   4289     {
   4290         // Release active id if turning disabled
   4291         if (g.ActiveId == id && id != 0)
   4292             ClearActiveID();
   4293         g.HoveredIdIsDisabled = true;
   4294         return false;
   4295     }
   4296 
   4297 #ifndef IMGUI_DISABLE_DEBUG_TOOLS
   4298     if (id != 0)
   4299     {
   4300         // [DEBUG] Item Picker tool!
   4301         // We perform the check here because reaching is path is rare (1~ time a frame),
   4302         // making the cost of this tool near-zero! We could get better call-stack and support picking non-hovered
   4303         // items if we performed the test in ItemAdd(), but that would incur a bigger runtime cost.
   4304         if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
   4305             GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
   4306         if (g.DebugItemPickerBreakId == id)
   4307             IM_DEBUG_BREAK();
   4308     }
   4309 #endif
   4310 
   4311     if (g.NavDisableMouseHover)
   4312         return false;
   4313 
   4314     return true;
   4315 }
   4316 
   4317 // FIXME: This is inlined/duplicated in ItemAdd()
   4318 // FIXME: The id != 0 path is not used by our codebase, may get rid of it?
   4319 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id)
   4320 {
   4321     ImGuiContext& g = *GImGui;
   4322     ImGuiWindow* window = g.CurrentWindow;
   4323     if (!bb.Overlaps(window->ClipRect))
   4324         if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId))
   4325             if (!g.ItemUnclipByLog)
   4326                 return true;
   4327     return false;
   4328 }
   4329 
   4330 // This is also inlined in ItemAdd()
   4331 // Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set g.LastItemData.DisplayRect.
   4332 void ImGui::SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags item_flags, const ImRect& item_rect)
   4333 {
   4334     ImGuiContext& g = *GImGui;
   4335     g.LastItemData.ID = item_id;
   4336     g.LastItemData.InFlags = in_flags;
   4337     g.LastItemData.StatusFlags = item_flags;
   4338     g.LastItemData.Rect = g.LastItemData.NavRect = item_rect;
   4339 }
   4340 
   4341 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
   4342 {
   4343     if (wrap_pos_x < 0.0f)
   4344         return 0.0f;
   4345 
   4346     ImGuiContext& g = *GImGui;
   4347     ImGuiWindow* window = g.CurrentWindow;
   4348     if (wrap_pos_x == 0.0f)
   4349     {
   4350         // We could decide to setup a default wrapping max point for auto-resizing windows,
   4351         // or have auto-wrap (with unspecified wrapping pos) behave as a ContentSize extending function?
   4352         //if (window->Hidden && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize))
   4353         //    wrap_pos_x = ImMax(window->WorkRect.Min.x + g.FontSize * 10.0f, window->WorkRect.Max.x);
   4354         //else
   4355         wrap_pos_x = window->WorkRect.Max.x;
   4356     }
   4357     else if (wrap_pos_x > 0.0f)
   4358     {
   4359         wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
   4360     }
   4361 
   4362     return ImMax(wrap_pos_x - pos.x, 1.0f);
   4363 }
   4364 
   4365 // IM_ALLOC() == ImGui::MemAlloc()
   4366 void* ImGui::MemAlloc(size_t size)
   4367 {
   4368     void* ptr = (*GImAllocatorAllocFunc)(size, GImAllocatorUserData);
   4369 #ifndef IMGUI_DISABLE_DEBUG_TOOLS
   4370     if (ImGuiContext* ctx = GImGui)
   4371         DebugAllocHook(&ctx->DebugAllocInfo, ctx->FrameCount, ptr, size);
   4372 #endif
   4373     return ptr;
   4374 }
   4375 
   4376 // IM_FREE() == ImGui::MemFree()
   4377 void ImGui::MemFree(void* ptr)
   4378 {
   4379 #ifndef IMGUI_DISABLE_DEBUG_TOOLS
   4380     if (ptr != NULL)
   4381         if (ImGuiContext* ctx = GImGui)
   4382             DebugAllocHook(&ctx->DebugAllocInfo, ctx->FrameCount, ptr, (size_t)-1);
   4383 #endif
   4384     return (*GImAllocatorFreeFunc)(ptr, GImAllocatorUserData);
   4385 }
   4386 
   4387 // We record the number of allocation in recent frames, as a way to audit/sanitize our guiding principles of "no allocations on idle/repeating frames"
   4388 void ImGui::DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size)
   4389 {
   4390     ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[info->LastEntriesIdx];
   4391     IM_UNUSED(ptr);
   4392     if (entry->FrameCount != frame_count)
   4393     {
   4394         info->LastEntriesIdx = (info->LastEntriesIdx + 1) % IM_ARRAYSIZE(info->LastEntriesBuf);
   4395         entry = &info->LastEntriesBuf[info->LastEntriesIdx];
   4396         entry->FrameCount = frame_count;
   4397         entry->AllocCount = entry->FreeCount = 0;
   4398     }
   4399     if (size != (size_t)-1)
   4400     {
   4401         entry->AllocCount++;
   4402         info->TotalAllocCount++;
   4403         //printf("[%05d] MemAlloc(%d) -> 0x%p\n", frame_count, size, ptr);
   4404     }
   4405     else
   4406     {
   4407         entry->FreeCount++;
   4408         info->TotalFreeCount++;
   4409         //printf("[%05d] MemFree(0x%p)\n", frame_count, ptr);
   4410     }
   4411 }
   4412 
   4413 const char* ImGui::GetClipboardText()
   4414 {
   4415     ImGuiContext& g = *GImGui;
   4416     return g.IO.GetClipboardTextFn ? g.IO.GetClipboardTextFn(g.IO.ClipboardUserData) : "";
   4417 }
   4418 
   4419 void ImGui::SetClipboardText(const char* text)
   4420 {
   4421     ImGuiContext& g = *GImGui;
   4422     if (g.IO.SetClipboardTextFn)
   4423         g.IO.SetClipboardTextFn(g.IO.ClipboardUserData, text);
   4424 }
   4425 
   4426 const char* ImGui::GetVersion()
   4427 {
   4428     return IMGUI_VERSION;
   4429 }
   4430 
   4431 ImGuiIO& ImGui::GetIO()
   4432 {
   4433     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
   4434     return GImGui->IO;
   4435 }
   4436 
   4437 // Pass this to your backend rendering function! Valid after Render() and until the next call to NewFrame()
   4438 ImDrawData* ImGui::GetDrawData()
   4439 {
   4440     ImGuiContext& g = *GImGui;
   4441     ImGuiViewportP* viewport = g.Viewports[0];
   4442     return viewport->DrawDataP.Valid ? &viewport->DrawDataP : NULL;
   4443 }
   4444 
   4445 double ImGui::GetTime()
   4446 {
   4447     return GImGui->Time;
   4448 }
   4449 
   4450 int ImGui::GetFrameCount()
   4451 {
   4452     return GImGui->FrameCount;
   4453 }
   4454 
   4455 static ImDrawList* GetViewportBgFgDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name)
   4456 {
   4457     // Create the draw list on demand, because they are not frequently used for all viewports
   4458     ImGuiContext& g = *GImGui;
   4459     IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->BgFgDrawLists));
   4460     ImDrawList* draw_list = viewport->BgFgDrawLists[drawlist_no];
   4461     if (draw_list == NULL)
   4462     {
   4463         draw_list = IM_NEW(ImDrawList)(&g.DrawListSharedData);
   4464         draw_list->_OwnerName = drawlist_name;
   4465         viewport->BgFgDrawLists[drawlist_no] = draw_list;
   4466     }
   4467 
   4468     // Our ImDrawList system requires that there is always a command
   4469     if (viewport->BgFgDrawListsLastFrame[drawlist_no] != g.FrameCount)
   4470     {
   4471         draw_list->_ResetForNewFrame();
   4472         draw_list->PushTextureID(g.IO.Fonts->TexID);
   4473         draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false);
   4474         viewport->BgFgDrawListsLastFrame[drawlist_no] = g.FrameCount;
   4475     }
   4476     return draw_list;
   4477 }
   4478 
   4479 ImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport)
   4480 {
   4481     return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 0, "##Background");
   4482 }
   4483 
   4484 ImDrawList* ImGui::GetBackgroundDrawList()
   4485 {
   4486     ImGuiContext& g = *GImGui;
   4487     return GetBackgroundDrawList(g.Viewports[0]);
   4488 }
   4489 
   4490 ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport)
   4491 {
   4492     return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 1, "##Foreground");
   4493 }
   4494 
   4495 ImDrawList* ImGui::GetForegroundDrawList()
   4496 {
   4497     ImGuiContext& g = *GImGui;
   4498     return GetForegroundDrawList(g.Viewports[0]);
   4499 }
   4500 
   4501 ImDrawListSharedData* ImGui::GetDrawListSharedData()
   4502 {
   4503     return &GImGui->DrawListSharedData;
   4504 }
   4505 
   4506 void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
   4507 {
   4508     // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
   4509     // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
   4510     // This is because we want ActiveId to be set even when the window is not permitted to move.
   4511     ImGuiContext& g = *GImGui;
   4512     FocusWindow(window);
   4513     SetActiveID(window->MoveId, window);
   4514     g.NavDisableHighlight = true;
   4515     g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos;
   4516     g.ActiveIdNoClearOnFocusLoss = true;
   4517     SetActiveIdUsingAllKeyboardKeys();
   4518 
   4519     bool can_move_window = true;
   4520     if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
   4521         can_move_window = false;
   4522     if (can_move_window)
   4523         g.MovingWindow = window;
   4524 }
   4525 
   4526 // Handle mouse moving window
   4527 // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
   4528 // FIXME: We don't have strong guarantee that g.MovingWindow stay synched with g.ActiveId == g.MovingWindow->MoveId.
   4529 // This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs,
   4530 // but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other.
   4531 void ImGui::UpdateMouseMovingWindowNewFrame()
   4532 {
   4533     ImGuiContext& g = *GImGui;
   4534     if (g.MovingWindow != NULL)
   4535     {
   4536         // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
   4537         // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
   4538         KeepAliveID(g.ActiveId);
   4539         IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
   4540         ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
   4541         if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
   4542         {
   4543             ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
   4544             SetWindowPos(moving_window, pos, ImGuiCond_Always);
   4545             FocusWindow(g.MovingWindow);
   4546         }
   4547         else
   4548         {
   4549             g.MovingWindow = NULL;
   4550             ClearActiveID();
   4551         }
   4552     }
   4553     else
   4554     {
   4555         // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
   4556         if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
   4557         {
   4558             KeepAliveID(g.ActiveId);
   4559             if (!g.IO.MouseDown[0])
   4560                 ClearActiveID();
   4561         }
   4562     }
   4563 }
   4564 
   4565 // Initiate moving window when clicking on empty space or title bar.
   4566 // Handle left-click and right-click focus.
   4567 void ImGui::UpdateMouseMovingWindowEndFrame()
   4568 {
   4569     ImGuiContext& g = *GImGui;
   4570     if (g.ActiveId != 0 || g.HoveredId != 0)
   4571         return;
   4572 
   4573     // Unless we just made a window/popup appear
   4574     if (g.NavWindow && g.NavWindow->Appearing)
   4575         return;
   4576 
   4577     // Click on empty space to focus window and start moving
   4578     // (after we're done with all our widgets)
   4579     if (g.IO.MouseClicked[0])
   4580     {
   4581         // Handle the edge case of a popup being closed while clicking in its empty space.
   4582         // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more.
   4583         ImGuiWindow* root_window = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
   4584         const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(root_window->PopupId, ImGuiPopupFlags_AnyPopupLevel);
   4585 
   4586         if (root_window != NULL && !is_closed_popup)
   4587         {
   4588             StartMouseMovingWindow(g.HoveredWindow); //-V595
   4589 
   4590             // Cancel moving if clicked outside of title bar
   4591             if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(root_window->Flags & ImGuiWindowFlags_NoTitleBar))
   4592                 if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
   4593                     g.MovingWindow = NULL;
   4594 
   4595             // Cancel moving if clicked over an item which was disabled or inhibited by popups (note that we know HoveredId == 0 already)
   4596             if (g.HoveredIdIsDisabled)
   4597                 g.MovingWindow = NULL;
   4598         }
   4599         else if (root_window == NULL && g.NavWindow != NULL)
   4600         {
   4601             // Clicking on void disable focus
   4602             FocusWindow(NULL, ImGuiFocusRequestFlags_UnlessBelowModal);
   4603         }
   4604     }
   4605 
   4606     // With right mouse button we close popups without changing focus based on where the mouse is aimed
   4607     // Instead, focus will be restored to the window under the bottom-most closed popup.
   4608     // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
   4609     if (g.IO.MouseClicked[1])
   4610     {
   4611         // Find the top-most window between HoveredWindow and the top-most Modal Window.
   4612         // This is where we can trim the popup stack.
   4613         ImGuiWindow* modal = GetTopMostPopupModal();
   4614         bool hovered_window_above_modal = g.HoveredWindow && (modal == NULL || IsWindowAbove(g.HoveredWindow, modal));
   4615         ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true);
   4616     }
   4617 }
   4618 
   4619 static bool IsWindowActiveAndVisible(ImGuiWindow* window)
   4620 {
   4621     return (window->Active) && (!window->Hidden);
   4622 }
   4623 
   4624 // 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)
   4625 void ImGui::UpdateHoveredWindowAndCaptureFlags()
   4626 {
   4627     ImGuiContext& g = *GImGui;
   4628     ImGuiIO& io = g.IO;
   4629 
   4630     // FIXME-DPI: This storage was added on 2021/03/31 for test engine, but if we want to multiply WINDOWS_HOVER_PADDING
   4631     // by DpiScale, we need to make this window-agnostic anyhow, maybe need storing inside ImGuiWindow.
   4632     g.WindowsHoverPadding = ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_HOVER_PADDING, WINDOWS_HOVER_PADDING));
   4633 
   4634     // Find the window hovered by mouse:
   4635     // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
   4636     // - 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.
   4637     // - 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.
   4638     bool clear_hovered_windows = false;
   4639     FindHoveredWindowEx(g.IO.MousePos, false, &g.HoveredWindow, &g.HoveredWindowUnderMovingWindow);
   4640     g.HoveredWindowBeforeClear = g.HoveredWindow;
   4641 
   4642     // Modal windows prevents mouse from hovering behind them.
   4643     ImGuiWindow* modal_window = GetTopMostPopupModal();
   4644     if (modal_window && g.HoveredWindow && !IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, modal_window))
   4645         clear_hovered_windows = true;
   4646 
   4647     // Disabled mouse hovering (we don't currently clear MousePos, we could)
   4648     if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)
   4649         clear_hovered_windows = true;
   4650 
   4651     // We track click ownership. When clicked outside of a window the click is owned by the application and
   4652     // won't report hovering nor request capture even while dragging over our windows afterward.
   4653     const bool has_open_popup = (g.OpenPopupStack.Size > 0);
   4654     const bool has_open_modal = (modal_window != NULL);
   4655     int mouse_earliest_down = -1;
   4656     bool mouse_any_down = false;
   4657     for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
   4658     {
   4659         if (io.MouseClicked[i])
   4660         {
   4661             io.MouseDownOwned[i] = (g.HoveredWindow != NULL) || has_open_popup;
   4662             io.MouseDownOwnedUnlessPopupClose[i] = (g.HoveredWindow != NULL) || has_open_modal;
   4663         }
   4664         mouse_any_down |= io.MouseDown[i];
   4665         if (io.MouseDown[i] || io.MouseReleased[i]) // Increase release frame for our evaluation of earliest button (#1392)
   4666             if (mouse_earliest_down == -1 || io.MouseClickedTime[i] < io.MouseClickedTime[mouse_earliest_down])
   4667                 mouse_earliest_down = i;
   4668     }
   4669     const bool mouse_avail = (mouse_earliest_down == -1) || io.MouseDownOwned[mouse_earliest_down];
   4670     const bool mouse_avail_unless_popup_close = (mouse_earliest_down == -1) || io.MouseDownOwnedUnlessPopupClose[mouse_earliest_down];
   4671 
   4672     // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
   4673     // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
   4674     const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
   4675     if (!mouse_avail && !mouse_dragging_extern_payload)
   4676         clear_hovered_windows = true;
   4677 
   4678     if (clear_hovered_windows)
   4679         g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
   4680 
   4681     // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to Dear ImGui only, false = dispatch mouse to Dear ImGui + underlying app)
   4682     // Update io.WantCaptureMouseAllowPopupClose (experimental) to give a chance for app to react to popup closure with a drag
   4683     if (g.WantCaptureMouseNextFrame != -1)
   4684     {
   4685         io.WantCaptureMouse = io.WantCaptureMouseUnlessPopupClose = (g.WantCaptureMouseNextFrame != 0);
   4686     }
   4687     else
   4688     {
   4689         io.WantCaptureMouse = (mouse_avail && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_popup;
   4690         io.WantCaptureMouseUnlessPopupClose = (mouse_avail_unless_popup_close && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_modal;
   4691     }
   4692 
   4693     // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to Dear ImGui only, false = dispatch keyboard info to Dear ImGui + underlying app)
   4694     io.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
   4695     if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
   4696         io.WantCaptureKeyboard = true;
   4697     if (g.WantCaptureKeyboardNextFrame != -1) // Manual override
   4698         io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
   4699 
   4700     // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
   4701     io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
   4702 }
   4703 
   4704 // Calling SetupDrawListSharedData() is followed by SetCurrentFont() which sets up the remaining data.
   4705 static void SetupDrawListSharedData()
   4706 {
   4707     ImGuiContext& g = *GImGui;
   4708     ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
   4709     for (ImGuiViewportP* viewport : g.Viewports)
   4710         virtual_space.Add(viewport->GetMainRect());
   4711     g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4();
   4712     g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
   4713     g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError);
   4714     g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
   4715     if (g.Style.AntiAliasedLines)
   4716         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
   4717     if (g.Style.AntiAliasedLinesUseTex && !(g.IO.Fonts->Flags & ImFontAtlasFlags_NoBakedLines))
   4718         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex;
   4719     if (g.Style.AntiAliasedFill)
   4720         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
   4721     if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
   4722         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
   4723 }
   4724 
   4725 void ImGui::NewFrame()
   4726 {
   4727     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
   4728     ImGuiContext& g = *GImGui;
   4729 
   4730     // Remove pending delete hooks before frame start.
   4731     // This deferred removal avoid issues of removal while iterating the hook vector
   4732     for (int n = g.Hooks.Size - 1; n >= 0; n--)
   4733         if (g.Hooks[n].Type == ImGuiContextHookType_PendingRemoval_)
   4734             g.Hooks.erase(&g.Hooks[n]);
   4735 
   4736     CallContextHooks(&g, ImGuiContextHookType_NewFramePre);
   4737 
   4738     // Check and assert for various common IO and Configuration mistakes
   4739     ErrorCheckNewFrameSanityChecks();
   4740 
   4741     // Load settings on first frame, save settings when modified (after a delay)
   4742     UpdateSettings();
   4743 
   4744     g.Time += g.IO.DeltaTime;
   4745     g.WithinFrameScope = true;
   4746     g.FrameCount += 1;
   4747     g.TooltipOverrideCount = 0;
   4748     g.WindowsActiveCount = 0;
   4749     g.MenusIdSubmittedThisFrame.resize(0);
   4750 
   4751     // Calculate frame-rate for the user, as a purely luxurious feature
   4752     g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
   4753     g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
   4754     g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
   4755     g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, IM_ARRAYSIZE(g.FramerateSecPerFrame));
   4756     g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)g.FramerateSecPerFrameCount)) : FLT_MAX;
   4757 
   4758     // Process input queue (trickle as many events as possible), turn events into writes to IO structure
   4759     g.InputEventsTrail.resize(0);
   4760     UpdateInputEvents(g.IO.ConfigInputTrickleEventQueue);
   4761 
   4762     // Update viewports (after processing input queue, so io.MouseHoveredViewport is set)
   4763     UpdateViewportsNewFrame();
   4764 
   4765     // Setup current font and draw list shared data
   4766     g.IO.Fonts->Locked = true;
   4767     SetupDrawListSharedData();
   4768     SetCurrentFont(GetDefaultFont());
   4769     IM_ASSERT(g.Font->IsLoaded());
   4770 
   4771     // Mark rendering data as invalid to prevent user who may have a handle on it to use it.
   4772     for (ImGuiViewportP* viewport : g.Viewports)
   4773         viewport->DrawDataP.Valid = false;
   4774 
   4775     // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
   4776     if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
   4777         KeepAliveID(g.DragDropPayload.SourceId);
   4778 
   4779     // Update HoveredId data
   4780     if (!g.HoveredIdPreviousFrame)
   4781         g.HoveredIdTimer = 0.0f;
   4782     if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
   4783         g.HoveredIdNotActiveTimer = 0.0f;
   4784     if (g.HoveredId)
   4785         g.HoveredIdTimer += g.IO.DeltaTime;
   4786     if (g.HoveredId && g.ActiveId != g.HoveredId)
   4787         g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
   4788     g.HoveredIdPreviousFrame = g.HoveredId;
   4789     g.HoveredId = 0;
   4790     g.HoveredIdAllowOverlap = false;
   4791     g.HoveredIdIsDisabled = false;
   4792 
   4793     // Clear ActiveID if the item is not alive anymore.
   4794     // In 1.87, the common most call to KeepAliveID() was moved from GetID() to ItemAdd().
   4795     // As a result, custom widget using ButtonBehavior() _without_ ItemAdd() need to call KeepAliveID() themselves.
   4796     if (g.ActiveId != 0 && g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId)
   4797     {
   4798         IMGUI_DEBUG_LOG_ACTIVEID("NewFrame(): ClearActiveID() because it isn't marked alive anymore!\n");
   4799         ClearActiveID();
   4800     }
   4801 
   4802     // Update ActiveId data (clear reference to active widget if the widget isn't alive anymore)
   4803     if (g.ActiveId)
   4804         g.ActiveIdTimer += g.IO.DeltaTime;
   4805     g.LastActiveIdTimer += g.IO.DeltaTime;
   4806     g.ActiveIdPreviousFrame = g.ActiveId;
   4807     g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
   4808     g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore;
   4809     g.ActiveIdIsAlive = 0;
   4810     g.ActiveIdHasBeenEditedThisFrame = false;
   4811     g.ActiveIdPreviousFrameIsAlive = false;
   4812     g.ActiveIdIsJustActivated = false;
   4813     if (g.TempInputId != 0 && g.ActiveId != g.TempInputId)
   4814         g.TempInputId = 0;
   4815     if (g.ActiveId == 0)
   4816     {
   4817         g.ActiveIdUsingNavDirMask = 0x00;
   4818         g.ActiveIdUsingAllKeyboardKeys = false;
   4819     }
   4820 
   4821     // Record when we have been stationary as this state is preserved while over same item.
   4822     // FIXME: The way this is expressed means user cannot alter HoverStationaryDelay during the frame to use varying values.
   4823     // To allow this we should store HoverItemMaxStationaryTime+ID and perform the >= check in IsItemHovered() function.
   4824     if (g.HoverItemDelayId != 0 && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay)
   4825         g.HoverItemUnlockedStationaryId = g.HoverItemDelayId;
   4826     else if (g.HoverItemDelayId == 0)
   4827         g.HoverItemUnlockedStationaryId = 0;
   4828     if (g.HoveredWindow != NULL && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay)
   4829         g.HoverWindowUnlockedStationaryId = g.HoveredWindow->ID;
   4830     else if (g.HoveredWindow == NULL)
   4831         g.HoverWindowUnlockedStationaryId = 0;
   4832 
   4833     // Update hover delay for IsItemHovered() with delays and tooltips
   4834     g.HoverItemDelayIdPreviousFrame = g.HoverItemDelayId;
   4835     if (g.HoverItemDelayId != 0)
   4836     {
   4837         g.HoverItemDelayTimer += g.IO.DeltaTime;
   4838         g.HoverItemDelayClearTimer = 0.0f;
   4839         g.HoverItemDelayId = 0;
   4840     }
   4841     else if (g.HoverItemDelayTimer > 0.0f)
   4842     {
   4843         // This gives a little bit of leeway before clearing the hover timer, allowing mouse to cross gaps
   4844         // We could expose 0.25f as style.HoverClearDelay but I am not sure of the logic yet, this is particularly subtle.
   4845         g.HoverItemDelayClearTimer += g.IO.DeltaTime;
   4846         if (g.HoverItemDelayClearTimer >= ImMax(0.25f, g.IO.DeltaTime * 2.0f)) // ~7 frames at 30 Hz + allow for low framerate
   4847             g.HoverItemDelayTimer = g.HoverItemDelayClearTimer = 0.0f; // May want a decaying timer, in which case need to clamp at max first, based on max of caller last requested timer.
   4848     }
   4849 
   4850     // Drag and drop
   4851     g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
   4852     g.DragDropAcceptIdCurr = 0;
   4853     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
   4854     g.DragDropWithinSource = false;
   4855     g.DragDropWithinTarget = false;
   4856     g.DragDropHoldJustPressedId = 0;
   4857 
   4858     // Close popups on focus lost (currently wip/opt-in)
   4859     //if (g.IO.AppFocusLost)
   4860     //    ClosePopupsExceptModals();
   4861 
   4862     // Update keyboard input state
   4863     UpdateKeyboardInputs();
   4864 
   4865     //IM_ASSERT(g.IO.KeyCtrl == IsKeyDown(ImGuiKey_LeftCtrl) || IsKeyDown(ImGuiKey_RightCtrl));
   4866     //IM_ASSERT(g.IO.KeyShift == IsKeyDown(ImGuiKey_LeftShift) || IsKeyDown(ImGuiKey_RightShift));
   4867     //IM_ASSERT(g.IO.KeyAlt == IsKeyDown(ImGuiKey_LeftAlt) || IsKeyDown(ImGuiKey_RightAlt));
   4868     //IM_ASSERT(g.IO.KeySuper == IsKeyDown(ImGuiKey_LeftSuper) || IsKeyDown(ImGuiKey_RightSuper));
   4869 
   4870     // Update gamepad/keyboard navigation
   4871     NavUpdate();
   4872 
   4873     // Update mouse input state
   4874     UpdateMouseInputs();
   4875 
   4876     // Find hovered window
   4877     // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
   4878     UpdateHoveredWindowAndCaptureFlags();
   4879 
   4880     // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
   4881     UpdateMouseMovingWindowNewFrame();
   4882 
   4883     // Background darkening/whitening
   4884     if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
   4885         g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
   4886     else
   4887         g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
   4888 
   4889     g.MouseCursor = ImGuiMouseCursor_Arrow;
   4890     g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
   4891 
   4892     // Platform IME data: reset for the frame
   4893     g.PlatformImeDataPrev = g.PlatformImeData;
   4894     g.PlatformImeData.WantVisible = false;
   4895 
   4896     // Mouse wheel scrolling, scale
   4897     UpdateMouseWheel();
   4898 
   4899     // Mark all windows as not visible and compact unused memory.
   4900     IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size);
   4901     const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer;
   4902     for (ImGuiWindow* window : g.Windows)
   4903     {
   4904         window->WasActive = window->Active;
   4905         window->Active = false;
   4906         window->WriteAccessed = false;
   4907         window->BeginCountPreviousFrame = window->BeginCount;
   4908         window->BeginCount = 0;
   4909 
   4910         // Garbage collect transient buffers of recently unused windows
   4911         if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
   4912             GcCompactTransientWindowBuffers(window);
   4913     }
   4914 
   4915     // Garbage collect transient buffers of recently unused tables
   4916     for (int i = 0; i < g.TablesLastTimeActive.Size; i++)
   4917         if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time)
   4918             TableGcCompactTransientBuffers(g.Tables.GetByIndex(i));
   4919     for (ImGuiTableTempData& table_temp_data : g.TablesTempData)
   4920         if (table_temp_data.LastTimeActive >= 0.0f && table_temp_data.LastTimeActive < memory_compact_start_time)
   4921             TableGcCompactTransientBuffers(&table_temp_data);
   4922     if (g.GcCompactAll)
   4923         GcCompactTransientMiscBuffers();
   4924     g.GcCompactAll = false;
   4925 
   4926     // Closing the focused window restore focus to the first active root window in descending z-order
   4927     if (g.NavWindow && !g.NavWindow->WasActive)
   4928         FocusTopMostWindowUnderOne(NULL, NULL, NULL, ImGuiFocusRequestFlags_RestoreFocusedChild);
   4929 
   4930     // No window should be open at the beginning of the frame.
   4931     // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
   4932     g.CurrentWindowStack.resize(0);
   4933     g.BeginPopupStack.resize(0);
   4934     g.ItemFlagsStack.resize(0);
   4935     g.ItemFlagsStack.push_back(ImGuiItemFlags_AutoClosePopups); // Default flags
   4936     g.CurrentItemFlags = g.ItemFlagsStack.back();
   4937     g.GroupStack.resize(0);
   4938 
   4939     // [DEBUG] Update debug features
   4940 #ifndef IMGUI_DISABLE_DEBUG_TOOLS
   4941     UpdateDebugToolItemPicker();
   4942     UpdateDebugToolStackQueries();
   4943     UpdateDebugToolFlashStyleColor();
   4944     if (g.DebugLocateFrames > 0 && --g.DebugLocateFrames == 0)
   4945     {
   4946         g.DebugLocateId = 0;
   4947         g.DebugBreakInLocateId = false;
   4948     }
   4949     if (g.DebugLogAutoDisableFrames > 0 && --g.DebugLogAutoDisableFrames == 0)
   4950     {
   4951         DebugLog("(Debug Log: Auto-disabled some ImGuiDebugLogFlags after 2 frames)\n");
   4952         g.DebugLogFlags &= ~g.DebugLogAutoDisableFlags;
   4953         g.DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None;
   4954     }
   4955 #endif
   4956 
   4957     // Create implicit/fallback window - which we will only render it if the user has added something to it.
   4958     // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
   4959     // This fallback is particularly important as it prevents ImGui:: calls from crashing.
   4960     g.WithinFrameScopeWithImplicitWindow = true;
   4961     SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
   4962     Begin("Debug##Default");
   4963     IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true);
   4964 
   4965     // [DEBUG] When io.ConfigDebugBeginReturnValue is set, we make Begin()/BeginChild() return false at different level of the window-stack,
   4966     // allowing to validate correct Begin/End behavior in user code.
   4967 #ifndef IMGUI_DISABLE_DEBUG_TOOLS
   4968     if (g.IO.ConfigDebugBeginReturnValueLoop)
   4969         g.DebugBeginReturnValueCullDepth = (g.DebugBeginReturnValueCullDepth == -1) ? 0 : ((g.DebugBeginReturnValueCullDepth + ((g.FrameCount % 4) == 0 ? 1 : 0)) % 10);
   4970     else
   4971         g.DebugBeginReturnValueCullDepth = -1;
   4972 #endif
   4973 
   4974     CallContextHooks(&g, ImGuiContextHookType_NewFramePost);
   4975 }
   4976 
   4977 // FIXME: Add a more explicit sort order in the window structure.
   4978 static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
   4979 {
   4980     const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
   4981     const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
   4982     if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
   4983         return d;
   4984     if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
   4985         return d;
   4986     return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
   4987 }
   4988 
   4989 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
   4990 {
   4991     out_sorted_windows->push_back(window);
   4992     if (window->Active)
   4993     {
   4994         int count = window->DC.ChildWindows.Size;
   4995         ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
   4996         for (int i = 0; i < count; i++)
   4997         {
   4998             ImGuiWindow* child = window->DC.ChildWindows[i];
   4999             if (child->Active)
   5000                 AddWindowToSortBuffer(out_sorted_windows, child);
   5001         }
   5002     }
   5003 }
   5004 
   5005 static void AddWindowToDrawData(ImGuiWindow* window, int layer)
   5006 {
   5007     ImGuiContext& g = *GImGui;
   5008     ImGuiViewportP* viewport = g.Viewports[0];
   5009     g.IO.MetricsRenderWindows++;
   5010     if (window->DrawList->_Splitter._Count > 1)
   5011         window->DrawList->ChannelsMerge(); // Merge if user forgot to merge back. Also required in Docking branch for ImGuiWindowFlags_DockNodeHost windows.
   5012     ImGui::AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[layer], window->DrawList);
   5013     for (ImGuiWindow* child : window->DC.ChildWindows)
   5014         if (IsWindowActiveAndVisible(child)) // Clipped children may have been marked not active
   5015             AddWindowToDrawData(child, layer);
   5016 }
   5017 
   5018 static inline int GetWindowDisplayLayer(ImGuiWindow* window)
   5019 {
   5020     return (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0;
   5021 }
   5022 
   5023 // Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)
   5024 static inline void AddRootWindowToDrawData(ImGuiWindow* window)
   5025 {
   5026     AddWindowToDrawData(window, GetWindowDisplayLayer(window));
   5027 }
   5028 
   5029 static void FlattenDrawDataIntoSingleLayer(ImDrawDataBuilder* builder)
   5030 {
   5031     int n = builder->Layers[0]->Size;
   5032     int full_size = n;
   5033     for (int i = 1; i < IM_ARRAYSIZE(builder->Layers); i++)
   5034         full_size += builder->Layers[i]->Size;
   5035     builder->Layers[0]->resize(full_size);
   5036     for (int layer_n = 1; layer_n < IM_ARRAYSIZE(builder->Layers); layer_n++)
   5037     {
   5038         ImVector<ImDrawList*>* layer = builder->Layers[layer_n];
   5039         if (layer->empty())
   5040             continue;
   5041         memcpy(builder->Layers[0]->Data + n, layer->Data, layer->Size * sizeof(ImDrawList*));
   5042         n += layer->Size;
   5043         layer->resize(0);
   5044     }
   5045 }
   5046 
   5047 static void InitViewportDrawData(ImGuiViewportP* viewport)
   5048 {
   5049     ImGuiIO& io = ImGui::GetIO();
   5050     ImDrawData* draw_data = &viewport->DrawDataP;
   5051 
   5052     viewport->DrawDataBuilder.Layers[0] = &draw_data->CmdLists;
   5053     viewport->DrawDataBuilder.Layers[1] = &viewport->DrawDataBuilder.LayerData1;
   5054     viewport->DrawDataBuilder.Layers[0]->resize(0);
   5055     viewport->DrawDataBuilder.Layers[1]->resize(0);
   5056 
   5057     draw_data->Valid = true;
   5058     draw_data->CmdListsCount = 0;
   5059     draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
   5060     draw_data->DisplayPos = viewport->Pos;
   5061     draw_data->DisplaySize = viewport->Size;
   5062     draw_data->FramebufferScale = io.DisplayFramebufferScale;
   5063     draw_data->OwnerViewport = viewport;
   5064 }
   5065 
   5066 // Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering.
   5067 // - When using this function it is sane to ensure that float are perfectly rounded to integer values,
   5068 //   so that e.g. (int)(max.x-min.x) in user's render produce correct result.
   5069 // - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect():
   5070 //   some frequently called functions which to modify both channels and clipping simultaneously tend to use the
   5071 //   more specialized SetWindowClipRectBeforeSetChannel() to avoid extraneous updates of underlying ImDrawCmds.
   5072 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
   5073 {
   5074     ImGuiWindow* window = GetCurrentWindow();
   5075     window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
   5076     window->ClipRect = window->DrawList->_ClipRectStack.back();
   5077 }
   5078 
   5079 void ImGui::PopClipRect()
   5080 {
   5081     ImGuiWindow* window = GetCurrentWindow();
   5082     window->DrawList->PopClipRect();
   5083     window->ClipRect = window->DrawList->_ClipRectStack.back();
   5084 }
   5085 
   5086 static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col)
   5087 {
   5088     if ((col & IM_COL32_A_MASK) == 0)
   5089         return;
   5090 
   5091     ImGuiViewportP* viewport = (ImGuiViewportP*)GetMainViewport();
   5092     ImRect viewport_rect = viewport->GetMainRect();
   5093 
   5094     // Draw behind window by moving the draw command at the FRONT of the draw list
   5095     {
   5096         // We've already called AddWindowToDrawData() which called DrawList->ChannelsMerge() on DockNodeHost windows,
   5097         // and draw list have been trimmed already, hence the explicit recreation of a draw command if missing.
   5098         // FIXME: This is creating complication, might be simpler if we could inject a drawlist in drawdata at a given position and not attempt to manipulate ImDrawCmd order.
   5099         ImDrawList* draw_list = window->RootWindow->DrawList;
   5100         if (draw_list->CmdBuffer.Size == 0)
   5101             draw_list->AddDrawCmd();
   5102         draw_list->PushClipRect(viewport_rect.Min - ImVec2(1, 1), viewport_rect.Max + ImVec2(1, 1), false); // FIXME: Need to stricty ensure ImDrawCmd are not merged (ElemCount==6 checks below will verify that)
   5103         draw_list->AddRectFilled(viewport_rect.Min, viewport_rect.Max, col);
   5104         ImDrawCmd cmd = draw_list->CmdBuffer.back();
   5105         IM_ASSERT(cmd.ElemCount == 6);
   5106         draw_list->CmdBuffer.pop_back();
   5107         draw_list->CmdBuffer.push_front(cmd);
   5108         draw_list->AddDrawCmd(); // We need to create a command as CmdBuffer.back().IdxOffset won't be correct if we append to same command.
   5109         draw_list->PopClipRect();
   5110     }
   5111 }
   5112 
   5113 ImGuiWindow* ImGui::FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* parent_window)
   5114 {
   5115     ImGuiContext& g = *GImGui;
   5116     ImGuiWindow* bottom_most_visible_window = parent_window;
   5117     for (int i = FindWindowDisplayIndex(parent_window); i >= 0; i--)
   5118     {
   5119         ImGuiWindow* window = g.Windows[i];
   5120         if (window->Flags & ImGuiWindowFlags_ChildWindow)
   5121             continue;
   5122         if (!IsWindowWithinBeginStackOf(window, parent_window))
   5123             break;
   5124         if (IsWindowActiveAndVisible(window) && GetWindowDisplayLayer(window) <= GetWindowDisplayLayer(parent_window))
   5125             bottom_most_visible_window = window;
   5126     }
   5127     return bottom_most_visible_window;
   5128 }
   5129 
   5130 static void ImGui::RenderDimmedBackgrounds()
   5131 {
   5132     ImGuiContext& g = *GImGui;
   5133     ImGuiWindow* modal_window = GetTopMostAndVisiblePopupModal();
   5134     if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
   5135         return;
   5136     const bool dim_bg_for_modal = (modal_window != NULL);
   5137     const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL && g.NavWindowingTargetAnim->Active);
   5138     if (!dim_bg_for_modal && !dim_bg_for_window_list)
   5139         return;
   5140 
   5141     if (dim_bg_for_modal)
   5142     {
   5143         // Draw dimming behind modal or a begin stack child, whichever comes first in draw order.
   5144         ImGuiWindow* dim_behind_window = FindBottomMostVisibleWindowWithinBeginStack(modal_window);
   5145         RenderDimmedBackgroundBehindWindow(dim_behind_window, GetColorU32(modal_window->DC.ModalDimBgColor, g.DimBgRatio));
   5146     }
   5147     else if (dim_bg_for_window_list)
   5148     {
   5149         // Draw dimming behind CTRL+Tab target window
   5150         RenderDimmedBackgroundBehindWindow(g.NavWindowingTargetAnim, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio));
   5151 
   5152         // Draw border around CTRL+Tab target window
   5153         ImGuiWindow* window = g.NavWindowingTargetAnim;
   5154         ImGuiViewport* viewport = GetMainViewport();
   5155         float distance = g.FontSize;
   5156         ImRect bb = window->Rect();
   5157         bb.Expand(distance);
   5158         if (bb.GetWidth() >= viewport->Size.x && bb.GetHeight() >= viewport->Size.y)
   5159             bb.Expand(-distance - 1.0f); // If a window fits the entire viewport, adjust its highlight inward
   5160         if (window->DrawList->CmdBuffer.Size == 0)
   5161             window->DrawList->AddDrawCmd();
   5162         window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size);
   5163         window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f);
   5164         window->DrawList->PopClipRect();
   5165     }
   5166 }
   5167 
   5168 // 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.
   5169 void ImGui::EndFrame()
   5170 {
   5171     ImGuiContext& g = *GImGui;
   5172     IM_ASSERT(g.Initialized);
   5173 
   5174     // Don't process EndFrame() multiple times.
   5175     if (g.FrameCountEnded == g.FrameCount)
   5176         return;
   5177     IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?");
   5178 
   5179     CallContextHooks(&g, ImGuiContextHookType_EndFramePre);
   5180 
   5181     ErrorCheckEndFrameSanityChecks();
   5182 
   5183     // Notify Platform/OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
   5184     ImGuiPlatformImeData* ime_data = &g.PlatformImeData;
   5185     if (g.IO.PlatformSetImeDataFn != NULL && memcmp(ime_data, &g.PlatformImeDataPrev, sizeof(ImGuiPlatformImeData)) != 0)
   5186     {
   5187         IMGUI_DEBUG_LOG_IO("[io] Calling io.PlatformSetImeDataFn(): WantVisible: %d, InputPos (%.2f,%.2f)\n", ime_data->WantVisible, ime_data->InputPos.x, ime_data->InputPos.y);
   5188         ImGuiViewport* viewport = GetMainViewport();
   5189         g.IO.PlatformSetImeDataFn(&g, viewport, ime_data);
   5190     }
   5191 
   5192     // Hide implicit/fallback "Debug" window if it hasn't been used
   5193     g.WithinFrameScopeWithImplicitWindow = false;
   5194     if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
   5195         g.CurrentWindow->Active = false;
   5196     End();
   5197 
   5198     // Update navigation: CTRL+Tab, wrap-around requests
   5199     NavEndFrame();
   5200 
   5201     // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
   5202     if (g.DragDropActive)
   5203     {
   5204         bool is_delivered = g.DragDropPayload.Delivery;
   5205         bool is_elapsed = (g.DragDropSourceFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_PayloadAutoExpire) || g.DragDropMouseButton == -1 || !IsMouseDown(g.DragDropMouseButton));
   5206         if (is_delivered || is_elapsed)
   5207             ClearDragDrop();
   5208     }
   5209 
   5210     // Drag and Drop: Fallback for missing source tooltip. This is not ideal but better than nothing.
   5211     // If you want to handle source item disappearing: instead of submitting your description tooltip
   5212     // in the BeginDragDropSource() block of the dragged item, you can submit them from a safe single spot
   5213     // (e.g. end of your item loop, or before EndFrame) by reading payload data.
   5214     // In the typical case, the contents of drag tooltip should be possible to infer solely from payload data.
   5215     if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
   5216     {
   5217         g.DragDropWithinSource = true;
   5218         SetTooltip("...");
   5219         g.DragDropWithinSource = false;
   5220     }
   5221 
   5222     // End frame
   5223     g.WithinFrameScope = false;
   5224     g.FrameCountEnded = g.FrameCount;
   5225 
   5226     // Initiate moving window + handle left-click and right-click focus
   5227     UpdateMouseMovingWindowEndFrame();
   5228 
   5229     // Sort the window list so that all child windows are after their parent
   5230     // We cannot do that on FocusWindow() because children may not exist yet
   5231     g.WindowsTempSortBuffer.resize(0);
   5232     g.WindowsTempSortBuffer.reserve(g.Windows.Size);
   5233     for (ImGuiWindow* window : g.Windows)
   5234     {
   5235         if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow))       // if a child is active its parent will add it
   5236             continue;
   5237         AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window);
   5238     }
   5239 
   5240     // This usually assert if there is a mismatch between the ImGuiWindowFlags_ChildWindow / ParentWindow values and DC.ChildWindows[] in parents, aka we've done something wrong.
   5241     IM_ASSERT(g.Windows.Size == g.WindowsTempSortBuffer.Size);
   5242     g.Windows.swap(g.WindowsTempSortBuffer);
   5243     g.IO.MetricsActiveWindows = g.WindowsActiveCount;
   5244 
   5245     // Unlock font atlas
   5246     g.IO.Fonts->Locked = false;
   5247 
   5248     // Clear Input data for next frame
   5249     g.IO.MousePosPrev = g.IO.MousePos;
   5250     g.IO.AppFocusLost = false;
   5251     g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
   5252     g.IO.InputQueueCharacters.resize(0);
   5253 
   5254     CallContextHooks(&g, ImGuiContextHookType_EndFramePost);
   5255 }
   5256 
   5257 // Prepare the data for rendering so you can call GetDrawData()
   5258 // (As with anything within the ImGui:: namspace this doesn't touch your GPU or graphics API at all:
   5259 // it is the role of the ImGui_ImplXXXX_RenderDrawData() function provided by the renderer backend)
   5260 void ImGui::Render()
   5261 {
   5262     ImGuiContext& g = *GImGui;
   5263     IM_ASSERT(g.Initialized);
   5264 
   5265     if (g.FrameCountEnded != g.FrameCount)
   5266         EndFrame();
   5267     if (g.FrameCountRendered == g.FrameCount)
   5268         return;
   5269     g.FrameCountRendered = g.FrameCount;
   5270 
   5271     g.IO.MetricsRenderWindows = 0;
   5272     CallContextHooks(&g, ImGuiContextHookType_RenderPre);
   5273 
   5274     // Draw modal/window whitening backgrounds
   5275     RenderDimmedBackgrounds();
   5276 
   5277     // Add background ImDrawList (for each active viewport)
   5278     for (ImGuiViewportP* viewport : g.Viewports)
   5279     {
   5280         InitViewportDrawData(viewport);
   5281         if (viewport->BgFgDrawLists[0] != NULL)
   5282             AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport));
   5283     }
   5284 
   5285     // Add ImDrawList to render
   5286     ImGuiWindow* windows_to_render_top_most[2];
   5287     windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
   5288     windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL);
   5289     for (ImGuiWindow* window : g.Windows)
   5290     {
   5291         IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'"
   5292         if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])
   5293             AddRootWindowToDrawData(window);
   5294     }
   5295     for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++)
   5296         if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window
   5297             AddRootWindowToDrawData(windows_to_render_top_most[n]);
   5298 
   5299     // Draw software mouse cursor if requested by io.MouseDrawCursor flag
   5300     if (g.IO.MouseDrawCursor && g.MouseCursor != ImGuiMouseCursor_None)
   5301         RenderMouseCursor(g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
   5302 
   5303     // Setup ImDrawData structures for end-user
   5304     g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0;
   5305     for (ImGuiViewportP* viewport : g.Viewports)
   5306     {
   5307         FlattenDrawDataIntoSingleLayer(&viewport->DrawDataBuilder);
   5308 
   5309         // Add foreground ImDrawList (for each active viewport)
   5310         if (viewport->BgFgDrawLists[1] != NULL)
   5311             AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport));
   5312 
   5313         // We call _PopUnusedDrawCmd() last thing, as RenderDimmedBackgrounds() rely on a valid command being there (especially in docking branch).
   5314         ImDrawData* draw_data = &viewport->DrawDataP;
   5315         IM_ASSERT(draw_data->CmdLists.Size == draw_data->CmdListsCount);
   5316         for (ImDrawList* draw_list : draw_data->CmdLists)
   5317             draw_list->_PopUnusedDrawCmd();
   5318 
   5319         g.IO.MetricsRenderVertices += draw_data->TotalVtxCount;
   5320         g.IO.MetricsRenderIndices += draw_data->TotalIdxCount;
   5321     }
   5322 
   5323     CallContextHooks(&g, ImGuiContextHookType_RenderPost);
   5324 }
   5325 
   5326 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
   5327 // CalcTextSize("") should return ImVec2(0.0f, g.FontSize)
   5328 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
   5329 {
   5330     ImGuiContext& g = *GImGui;
   5331 
   5332     const char* text_display_end;
   5333     if (hide_text_after_double_hash)
   5334         text_display_end = FindRenderedTextEnd(text, text_end);      // Hide anything after a '##' string
   5335     else
   5336         text_display_end = text_end;
   5337 
   5338     ImFont* font = g.Font;
   5339     const float font_size = g.FontSize;
   5340     if (text == text_display_end)
   5341         return ImVec2(0.0f, font_size);
   5342     ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
   5343 
   5344     // Round
   5345     // FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out.
   5346     // FIXME: Investigate using ceilf or e.g.
   5347     // - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c
   5348     // - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html
   5349     text_size.x = IM_TRUNC(text_size.x + 0.99999f);
   5350 
   5351     return text_size;
   5352 }
   5353 
   5354 // Find window given position, search front-to-back
   5355 // - Typically write output back to g.HoveredWindow and g.HoveredWindowUnderMovingWindow.
   5356 // - FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically
   5357 //   with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
   5358 //   called, aka before the next Begin(). Moving window isn't affected.
   5359 // - The 'find_first_and_in_any_viewport = true' mode is only used by TestEngine. It is simpler to maintain here.
   5360 void ImGui::FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_viewport, ImGuiWindow** out_hovered_window, ImGuiWindow** out_hovered_window_under_moving_window)
   5361 {
   5362     ImGuiContext& g = *GImGui;
   5363     ImGuiWindow* hovered_window = NULL;
   5364     ImGuiWindow* hovered_window_under_moving_window = NULL;
   5365 
   5366     if (find_first_and_in_any_viewport == false && g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
   5367         hovered_window = g.MovingWindow;
   5368 
   5369     ImVec2 padding_regular = g.Style.TouchExtraPadding;
   5370     ImVec2 padding_for_resize = g.IO.ConfigWindowsResizeFromEdges ? g.WindowsHoverPadding : padding_regular;
   5371     for (int i = g.Windows.Size - 1; i >= 0; i--)
   5372     {
   5373         ImGuiWindow* window = g.Windows[i];
   5374         IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
   5375         if (!window->Active || window->Hidden)
   5376             continue;
   5377         if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
   5378             continue;
   5379 
   5380         // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
   5381         ImVec2 hit_padding = (window->Flags & (ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) ? padding_regular : padding_for_resize;
   5382         if (!window->OuterRectClipped.ContainsWithPad(pos, hit_padding))
   5383             continue;
   5384 
   5385         // Support for one rectangular hole in any given window
   5386         // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512)
   5387         if (window->HitTestHoleSize.x != 0)
   5388         {
   5389             ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y);
   5390             ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y);
   5391             if (ImRect(hole_pos, hole_pos + hole_size).Contains(pos))
   5392                 continue;
   5393         }
   5394 
   5395         if (find_first_and_in_any_viewport)
   5396         {
   5397             hovered_window = window;
   5398             break;
   5399         }
   5400         else
   5401         {
   5402             if (hovered_window == NULL)
   5403                 hovered_window = window;
   5404             IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
   5405             if (hovered_window_under_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow))
   5406                 hovered_window_under_moving_window = window;
   5407             if (hovered_window && hovered_window_under_moving_window)
   5408                 break;
   5409         }
   5410     }
   5411 
   5412     *out_hovered_window = hovered_window;
   5413     if (out_hovered_window_under_moving_window != NULL)
   5414         *out_hovered_window_under_moving_window = hovered_window_under_moving_window;
   5415 }
   5416 
   5417 bool ImGui::IsItemActive()
   5418 {
   5419     ImGuiContext& g = *GImGui;
   5420     if (g.ActiveId)
   5421         return g.ActiveId == g.LastItemData.ID;
   5422     return false;
   5423 }
   5424 
   5425 bool ImGui::IsItemActivated()
   5426 {
   5427     ImGuiContext& g = *GImGui;
   5428     if (g.ActiveId)
   5429         if (g.ActiveId == g.LastItemData.ID && g.ActiveIdPreviousFrame != g.LastItemData.ID)
   5430             return true;
   5431     return false;
   5432 }
   5433 
   5434 bool ImGui::IsItemDeactivated()
   5435 {
   5436     ImGuiContext& g = *GImGui;
   5437     if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDeactivated)
   5438         return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Deactivated) != 0;
   5439     return (g.ActiveIdPreviousFrame == g.LastItemData.ID && g.ActiveIdPreviousFrame != 0 && g.ActiveId != g.LastItemData.ID);
   5440 }
   5441 
   5442 bool ImGui::IsItemDeactivatedAfterEdit()
   5443 {
   5444     ImGuiContext& g = *GImGui;
   5445     return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore));
   5446 }
   5447 
   5448 // == GetItemID() == GetFocusID()
   5449 bool ImGui::IsItemFocused()
   5450 {
   5451     ImGuiContext& g = *GImGui;
   5452     if (g.NavId != g.LastItemData.ID || g.NavId == 0)
   5453         return false;
   5454     return true;
   5455 }
   5456 
   5457 // Important: this can be useful but it is NOT equivalent to the behavior of e.g.Button()!
   5458 // Most widgets have specific reactions based on mouse-up/down state, mouse position etc.
   5459 bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button)
   5460 {
   5461     return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
   5462 }
   5463 
   5464 bool ImGui::IsItemToggledOpen()
   5465 {
   5466     ImGuiContext& g = *GImGui;
   5467     return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false;
   5468 }
   5469 
   5470 // Call after a Selectable() or TreeNode() involved in multi-selection.
   5471 // Useful if you need the per-item information before reaching EndMultiSelect(), e.g. for rendering purpose.
   5472 // This is only meant to be called inside a BeginMultiSelect()/EndMultiSelect() block.
   5473 // (Outside of multi-select, it would be misleading/ambiguous to report this signal, as widgets
   5474 // return e.g. a pressed event and user code is in charge of altering selection in ways we cannot predict.)
   5475 bool ImGui::IsItemToggledSelection()
   5476 {
   5477     ImGuiContext& g = *GImGui;
   5478     IM_ASSERT(g.CurrentMultiSelect != NULL); // Can only be used inside a BeginMultiSelect()/EndMultiSelect()
   5479     return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;
   5480 }
   5481 
   5482 // IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app,
   5483 // you should not use this function! Use the 'io.WantCaptureMouse' boolean for that!
   5484 // Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details.
   5485 bool ImGui::IsAnyItemHovered()
   5486 {
   5487     ImGuiContext& g = *GImGui;
   5488     return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
   5489 }
   5490 
   5491 bool ImGui::IsAnyItemActive()
   5492 {
   5493     ImGuiContext& g = *GImGui;
   5494     return g.ActiveId != 0;
   5495 }
   5496 
   5497 bool ImGui::IsAnyItemFocused()
   5498 {
   5499     ImGuiContext& g = *GImGui;
   5500     return g.NavId != 0 && !g.NavDisableHighlight;
   5501 }
   5502 
   5503 bool ImGui::IsItemVisible()
   5504 {
   5505     ImGuiContext& g = *GImGui;
   5506     return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) != 0;
   5507 }
   5508 
   5509 bool ImGui::IsItemEdited()
   5510 {
   5511     ImGuiContext& g = *GImGui;
   5512     return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Edited) != 0;
   5513 }
   5514 
   5515 // Allow next item to be overlapped by subsequent items.
   5516 // This works by requiring HoveredId to match for two subsequent frames,
   5517 // so if a following items overwrite it our interactions will naturally be disabled.
   5518 void ImGui::SetNextItemAllowOverlap()
   5519 {
   5520     ImGuiContext& g = *GImGui;
   5521     g.NextItemData.ItemFlags |= ImGuiItemFlags_AllowOverlap;
   5522 }
   5523 
   5524 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
   5525 // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
   5526 // FIXME-LEGACY: Use SetNextItemAllowOverlap() *before* your item instead.
   5527 void ImGui::SetItemAllowOverlap()
   5528 {
   5529     ImGuiContext& g = *GImGui;
   5530     ImGuiID id = g.LastItemData.ID;
   5531     if (g.HoveredId == id)
   5532         g.HoveredIdAllowOverlap = true;
   5533     if (g.ActiveId == id) // Before we made this obsolete, most calls to SetItemAllowOverlap() used to avoid this path by testing g.ActiveId != id.
   5534         g.ActiveIdAllowOverlap = true;
   5535 }
   5536 #endif
   5537 
   5538 // This is a shortcut for not taking ownership of 100+ keys, frequently used by drag operations.
   5539 // FIXME: It might be undesirable that this will likely disable KeyOwner-aware shortcuts systems. Consider a more fine-tuned version if needed?
   5540 void ImGui::SetActiveIdUsingAllKeyboardKeys()
   5541 {
   5542     ImGuiContext& g = *GImGui;
   5543     IM_ASSERT(g.ActiveId != 0);
   5544     g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_COUNT) - 1;
   5545     g.ActiveIdUsingAllKeyboardKeys = true;
   5546     NavMoveRequestCancel();
   5547 }
   5548 
   5549 ImGuiID ImGui::GetItemID()
   5550 {
   5551     ImGuiContext& g = *GImGui;
   5552     return g.LastItemData.ID;
   5553 }
   5554 
   5555 ImVec2 ImGui::GetItemRectMin()
   5556 {
   5557     ImGuiContext& g = *GImGui;
   5558     return g.LastItemData.Rect.Min;
   5559 }
   5560 
   5561 ImVec2 ImGui::GetItemRectMax()
   5562 {
   5563     ImGuiContext& g = *GImGui;
   5564     return g.LastItemData.Rect.Max;
   5565 }
   5566 
   5567 ImVec2 ImGui::GetItemRectSize()
   5568 {
   5569     ImGuiContext& g = *GImGui;
   5570     return g.LastItemData.Rect.GetSize();
   5571 }
   5572 
   5573 // Prior to v1.90 2023/10/16, the BeginChild() function took a 'bool border = false' parameter instead of 'ImGuiChildFlags child_flags = 0'.
   5574 // ImGuiChildFlags_Border is defined as always == 1 in order to allow old code passing 'true'. Read comments in imgui.h for details!
   5575 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
   5576 {
   5577     ImGuiID id = GetCurrentWindow()->GetID(str_id);
   5578     return BeginChildEx(str_id, id, size_arg, child_flags, window_flags);
   5579 }
   5580 
   5581 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
   5582 {
   5583     return BeginChildEx(NULL, id, size_arg, child_flags, window_flags);
   5584 }
   5585 
   5586 bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
   5587 {
   5588     ImGuiContext& g = *GImGui;
   5589     ImGuiWindow* parent_window = g.CurrentWindow;
   5590     IM_ASSERT(id != 0);
   5591 
   5592     // Sanity check as it is likely that some user will accidentally pass ImGuiWindowFlags into the ImGuiChildFlags argument.
   5593     const ImGuiChildFlags ImGuiChildFlags_SupportedMask_ = ImGuiChildFlags_Border | ImGuiChildFlags_AlwaysUseWindowPadding | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_FrameStyle | ImGuiChildFlags_NavFlattened;
   5594     IM_UNUSED(ImGuiChildFlags_SupportedMask_);
   5595     IM_ASSERT((child_flags & ~ImGuiChildFlags_SupportedMask_) == 0 && "Illegal ImGuiChildFlags value. Did you pass ImGuiWindowFlags values instead of ImGuiChildFlags?");
   5596     IM_ASSERT((window_flags & ImGuiWindowFlags_AlwaysAutoResize) == 0 && "Cannot specify ImGuiWindowFlags_AlwaysAutoResize for BeginChild(). Use ImGuiChildFlags_AlwaysAutoResize!");
   5597     if (child_flags & ImGuiChildFlags_AlwaysAutoResize)
   5598     {
   5599         IM_ASSERT((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0 && "Cannot use ImGuiChildFlags_ResizeX or ImGuiChildFlags_ResizeY with ImGuiChildFlags_AlwaysAutoResize!");
   5600         IM_ASSERT((child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY)) != 0 && "Must use ImGuiChildFlags_AutoResizeX or ImGuiChildFlags_AutoResizeY with ImGuiChildFlags_AlwaysAutoResize!");
   5601     }
   5602 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
   5603     if (window_flags & ImGuiWindowFlags_AlwaysUseWindowPadding)
   5604         child_flags |= ImGuiChildFlags_AlwaysUseWindowPadding;
   5605     if (window_flags & ImGuiWindowFlags_NavFlattened)
   5606         child_flags |= ImGuiChildFlags_NavFlattened;
   5607 #endif
   5608     if (child_flags & ImGuiChildFlags_AutoResizeX)
   5609         child_flags &= ~ImGuiChildFlags_ResizeX;
   5610     if (child_flags & ImGuiChildFlags_AutoResizeY)
   5611         child_flags &= ~ImGuiChildFlags_ResizeY;
   5612 
   5613     // Set window flags
   5614     window_flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar;
   5615     window_flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
   5616     if (child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize))
   5617         window_flags |= ImGuiWindowFlags_AlwaysAutoResize;
   5618     if ((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0)
   5619         window_flags |= ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings;
   5620 
   5621     // Special framed style
   5622     if (child_flags & ImGuiChildFlags_FrameStyle)
   5623     {
   5624         PushStyleColor(ImGuiCol_ChildBg, g.Style.Colors[ImGuiCol_FrameBg]);
   5625         PushStyleVar(ImGuiStyleVar_ChildRounding, g.Style.FrameRounding);
   5626         PushStyleVar(ImGuiStyleVar_ChildBorderSize, g.Style.FrameBorderSize);
   5627         PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.FramePadding);
   5628         child_flags |= ImGuiChildFlags_Border | ImGuiChildFlags_AlwaysUseWindowPadding;
   5629         window_flags |= ImGuiWindowFlags_NoMove;
   5630     }
   5631 
   5632     // Forward child flags
   5633     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasChildFlags;
   5634     g.NextWindowData.ChildFlags = child_flags;
   5635 
   5636     // Forward size
   5637     // Important: Begin() has special processing to switch condition to ImGuiCond_FirstUseEver for a given axis when ImGuiChildFlags_ResizeXXX is set.
   5638     // (the alternative would to store conditional flags per axis, which is possible but more code)
   5639     const ImVec2 size_avail = GetContentRegionAvail();
   5640     const ImVec2 size_default((child_flags & ImGuiChildFlags_AutoResizeX) ? 0.0f : size_avail.x, (child_flags & ImGuiChildFlags_AutoResizeY) ? 0.0f : size_avail.y);
   5641     const ImVec2 size = CalcItemSize(size_arg, size_default.x, size_default.y);
   5642     SetNextWindowSize(size);
   5643 
   5644     // 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.
   5645     // FIXME: 2023/11/14: commented out shorted version. We had an issue with multiple ### in child window path names, which the trailing hash helped workaround.
   5646     // e.g. "ParentName###ParentIdentifier/ChildName###ChildIdentifier" would get hashed incorrectly by ImHashStr(), trailing _%08X somehow fixes it.
   5647     const char* temp_window_name;
   5648     /*if (name && parent_window->IDStack.back() == parent_window->ID)
   5649         ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%s", parent_window->Name, name); // May omit ID if in root of ID stack
   5650     else*/
   5651     if (name)
   5652         ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%s_%08X", parent_window->Name, name, id);
   5653     else
   5654         ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%08X", parent_window->Name, id);
   5655 
   5656     // Set style
   5657     const float backup_border_size = g.Style.ChildBorderSize;
   5658     if ((child_flags & ImGuiChildFlags_Border) == 0)
   5659         g.Style.ChildBorderSize = 0.0f;
   5660 
   5661     // Begin into window
   5662     const bool ret = Begin(temp_window_name, NULL, window_flags);
   5663 
   5664     // Restore style
   5665     g.Style.ChildBorderSize = backup_border_size;
   5666     if (child_flags & ImGuiChildFlags_FrameStyle)
   5667     {
   5668         PopStyleVar(3);
   5669         PopStyleColor();
   5670     }
   5671 
   5672     ImGuiWindow* child_window = g.CurrentWindow;
   5673     child_window->ChildId = id;
   5674 
   5675     // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
   5676     // While this is not really documented/defined, it seems that the expected thing to do.
   5677     if (child_window->BeginCount == 1)
   5678         parent_window->DC.CursorPos = child_window->Pos;
   5679 
   5680     // Process navigation-in immediately so NavInit can run on first frame
   5681     // Can enter a child if (A) it has navigable items or (B) it can be scrolled.
   5682     const ImGuiID temp_id_for_activation = ImHashStr("##Child", 0, id);
   5683     if (g.ActiveId == temp_id_for_activation)
   5684         ClearActiveID();
   5685     if (g.NavActivateId == id && !(child_flags & ImGuiChildFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY))
   5686     {
   5687         FocusWindow(child_window);
   5688         NavInitWindow(child_window, false);
   5689         SetActiveID(temp_id_for_activation, child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item
   5690         g.ActiveIdSource = g.NavInputSource;
   5691     }
   5692     return ret;
   5693 }
   5694 
   5695 void ImGui::EndChild()
   5696 {
   5697     ImGuiContext& g = *GImGui;
   5698     ImGuiWindow* child_window = g.CurrentWindow;
   5699 
   5700     IM_ASSERT(g.WithinEndChild == false);
   5701     IM_ASSERT(child_window->Flags & ImGuiWindowFlags_ChildWindow);   // Mismatched BeginChild()/EndChild() calls
   5702 
   5703     g.WithinEndChild = true;
   5704     ImVec2 child_size = child_window->Size;
   5705     End();
   5706     if (child_window->BeginCount == 1)
   5707     {
   5708         ImGuiWindow* parent_window = g.CurrentWindow;
   5709         ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + child_size);
   5710         ItemSize(child_size);
   5711         const bool nav_flattened = (child_window->ChildFlags & ImGuiChildFlags_NavFlattened) != 0;
   5712         if ((child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY) && !nav_flattened)
   5713         {
   5714             ItemAdd(bb, child_window->ChildId);
   5715             RenderNavHighlight(bb, child_window->ChildId);
   5716 
   5717             // When browsing a window that has no activable items (scroll only) we keep a highlight on the child (pass g.NavId to trick into always displaying)
   5718             if (child_window->DC.NavLayersActiveMask == 0 && child_window == g.NavWindow)
   5719                 RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_Compact);
   5720         }
   5721         else
   5722         {
   5723             // Not navigable into
   5724             // - This is a bit of a fringe use case, mostly useful for undecorated, non-scrolling contents childs, or empty childs.
   5725             // - We could later decide to not apply this path if ImGuiChildFlags_FrameStyle or ImGuiChildFlags_Borders is set.
   5726             ItemAdd(bb, child_window->ChildId, NULL, ImGuiItemFlags_NoNav);
   5727 
   5728             // But when flattened we directly reach items, adjust active layer mask accordingly
   5729             if (nav_flattened)
   5730                 parent_window->DC.NavLayersActiveMaskNext |= child_window->DC.NavLayersActiveMaskNext;
   5731         }
   5732         if (g.HoveredWindow == child_window)
   5733             g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
   5734     }
   5735     g.WithinEndChild = false;
   5736     g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
   5737 }
   5738 
   5739 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
   5740 {
   5741     window->SetWindowPosAllowFlags       = enabled ? (window->SetWindowPosAllowFlags       | flags) : (window->SetWindowPosAllowFlags       & ~flags);
   5742     window->SetWindowSizeAllowFlags      = enabled ? (window->SetWindowSizeAllowFlags      | flags) : (window->SetWindowSizeAllowFlags      & ~flags);
   5743     window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
   5744 }
   5745 
   5746 ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
   5747 {
   5748     ImGuiContext& g = *GImGui;
   5749     return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
   5750 }
   5751 
   5752 ImGuiWindow* ImGui::FindWindowByName(const char* name)
   5753 {
   5754     ImGuiID id = ImHashStr(name);
   5755     return FindWindowByID(id);
   5756 }
   5757 
   5758 static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
   5759 {
   5760     window->Pos = ImTrunc(ImVec2(settings->Pos.x, settings->Pos.y));
   5761     if (settings->Size.x > 0 && settings->Size.y > 0)
   5762         window->Size = window->SizeFull = ImTrunc(ImVec2(settings->Size.x, settings->Size.y));
   5763     window->Collapsed = settings->Collapsed;
   5764 }
   5765 
   5766 static void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags)
   5767 {
   5768     ImGuiContext& g = *GImGui;
   5769 
   5770     const bool new_is_explicit_child = (new_flags & ImGuiWindowFlags_ChildWindow) != 0 && ((new_flags & ImGuiWindowFlags_Popup) == 0 || (new_flags & ImGuiWindowFlags_ChildMenu) != 0);
   5771     const bool child_flag_changed = new_is_explicit_child != window->IsExplicitChild;
   5772     if ((just_created || child_flag_changed) && !new_is_explicit_child)
   5773     {
   5774         IM_ASSERT(!g.WindowsFocusOrder.contains(window));
   5775         g.WindowsFocusOrder.push_back(window);
   5776         window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1);
   5777     }
   5778     else if (!just_created && child_flag_changed && new_is_explicit_child)
   5779     {
   5780         IM_ASSERT(g.WindowsFocusOrder[window->FocusOrder] == window);
   5781         for (int n = window->FocusOrder + 1; n < g.WindowsFocusOrder.Size; n++)
   5782             g.WindowsFocusOrder[n]->FocusOrder--;
   5783         g.WindowsFocusOrder.erase(g.WindowsFocusOrder.Data + window->FocusOrder);
   5784         window->FocusOrder = -1;
   5785     }
   5786     window->IsExplicitChild = new_is_explicit_child;
   5787 }
   5788 
   5789 static void InitOrLoadWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
   5790 {
   5791     // Initial window state with e.g. default/arbitrary window position
   5792     // Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
   5793     const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
   5794     window->Pos = main_viewport->Pos + ImVec2(60, 60);
   5795     window->Size = window->SizeFull = ImVec2(0, 0);
   5796     window->SetWindowPosAllowFlags = window->SetWindowSizeAllowFlags = window->SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
   5797 
   5798     if (settings != NULL)
   5799     {
   5800         SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
   5801         ApplyWindowSettings(window, settings);
   5802     }
   5803     window->DC.CursorStartPos = window->DC.CursorMaxPos = window->DC.IdealMaxPos = window->Pos; // So first call to CalcWindowContentSizes() doesn't return crazy values
   5804 
   5805     if ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
   5806     {
   5807         window->AutoFitFramesX = window->AutoFitFramesY = 2;
   5808         window->AutoFitOnlyGrows = false;
   5809     }
   5810     else
   5811     {
   5812         if (window->Size.x <= 0.0f)
   5813             window->AutoFitFramesX = 2;
   5814         if (window->Size.y <= 0.0f)
   5815             window->AutoFitFramesY = 2;
   5816         window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
   5817     }
   5818 }
   5819 
   5820 static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags)
   5821 {
   5822     // Create window the first time
   5823     //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags);
   5824     ImGuiContext& g = *GImGui;
   5825     ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
   5826     window->Flags = flags;
   5827     g.WindowsById.SetVoidPtr(window->ID, window);
   5828 
   5829     ImGuiWindowSettings* settings = NULL;
   5830     if (!(flags & ImGuiWindowFlags_NoSavedSettings))
   5831         if ((settings = ImGui::FindWindowSettingsByWindow(window)) != 0)
   5832             window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
   5833 
   5834     InitOrLoadWindowSettings(window, settings);
   5835 
   5836     if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
   5837         g.Windows.push_front(window); // Quite slow but rare and only once
   5838     else
   5839         g.Windows.push_back(window);
   5840 
   5841     return window;
   5842 }
   5843 
   5844 static inline ImVec2 CalcWindowMinSize(ImGuiWindow* window)
   5845 {
   5846     // We give windows non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups)
   5847     // FIXME: Essentially we want to restrict manual resizing to WindowMinSize+Decoration, and allow api resizing to be smaller.
   5848     // Perhaps should tend further a neater test for this.
   5849     ImGuiContext& g = *GImGui;
   5850     ImVec2 size_min;
   5851     if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_Popup))
   5852     {
   5853         size_min.x = (window->ChildFlags & ImGuiChildFlags_ResizeX) ? g.Style.WindowMinSize.x : 4.0f;
   5854         size_min.y = (window->ChildFlags & ImGuiChildFlags_ResizeY) ? g.Style.WindowMinSize.y : 4.0f;
   5855     }
   5856     else
   5857     {
   5858         size_min.x = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.x : 4.0f;
   5859         size_min.y = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.y : 4.0f;
   5860     }
   5861 
   5862     // Reduce artifacts with very small windows
   5863     ImGuiWindow* window_for_height = window;
   5864     size_min.y = ImMax(size_min.y, window_for_height->TitleBarHeight + window_for_height->MenuBarHeight + ImMax(0.0f, g.Style.WindowRounding - 1.0f));
   5865     return size_min;
   5866 }
   5867 
   5868 static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& size_desired)
   5869 {
   5870     ImGuiContext& g = *GImGui;
   5871     ImVec2 new_size = size_desired;
   5872     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)
   5873     {
   5874         // See comments in SetNextWindowSizeConstraints() for details about setting size_min an size_max.
   5875         ImRect cr = g.NextWindowData.SizeConstraintRect;
   5876         new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
   5877         new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
   5878         if (g.NextWindowData.SizeCallback)
   5879         {
   5880             ImGuiSizeCallbackData data;
   5881             data.UserData = g.NextWindowData.SizeCallbackUserData;
   5882             data.Pos = window->Pos;
   5883             data.CurrentSize = window->SizeFull;
   5884             data.DesiredSize = new_size;
   5885             g.NextWindowData.SizeCallback(&data);
   5886             new_size = data.DesiredSize;
   5887         }
   5888         new_size.x = IM_TRUNC(new_size.x);
   5889         new_size.y = IM_TRUNC(new_size.y);
   5890     }
   5891 
   5892     // Minimum size
   5893     ImVec2 size_min = CalcWindowMinSize(window);
   5894     return ImMax(new_size, size_min);
   5895 }
   5896 
   5897 static void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_current, ImVec2* content_size_ideal)
   5898 {
   5899     bool preserve_old_content_sizes = false;
   5900     if (window->Collapsed && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
   5901         preserve_old_content_sizes = true;
   5902     else if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0)
   5903         preserve_old_content_sizes = true;
   5904     if (preserve_old_content_sizes)
   5905     {
   5906         *content_size_current = window->ContentSize;
   5907         *content_size_ideal = window->ContentSizeIdeal;
   5908         return;
   5909     }
   5910 
   5911     content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_TRUNC(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);
   5912     content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_TRUNC(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);
   5913     content_size_ideal->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_TRUNC(ImMax(window->DC.CursorMaxPos.x, window->DC.IdealMaxPos.x) - window->DC.CursorStartPos.x);
   5914     content_size_ideal->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_TRUNC(ImMax(window->DC.CursorMaxPos.y, window->DC.IdealMaxPos.y) - window->DC.CursorStartPos.y);
   5915 }
   5916 
   5917 static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents)
   5918 {
   5919     ImGuiContext& g = *GImGui;
   5920     ImGuiStyle& style = g.Style;
   5921     const float decoration_w_without_scrollbars = window->DecoOuterSizeX1 + window->DecoOuterSizeX2 - window->ScrollbarSizes.x;
   5922     const float decoration_h_without_scrollbars = window->DecoOuterSizeY1 + window->DecoOuterSizeY2 - window->ScrollbarSizes.y;
   5923     ImVec2 size_pad = window->WindowPadding * 2.0f;
   5924     ImVec2 size_desired = size_contents + size_pad + ImVec2(decoration_w_without_scrollbars, decoration_h_without_scrollbars);
   5925     if (window->Flags & ImGuiWindowFlags_Tooltip)
   5926     {
   5927         // Tooltip always resize
   5928         return size_desired;
   5929     }
   5930     else
   5931     {
   5932         // Maximum window size is determined by the viewport size or monitor size
   5933         ImVec2 size_min = CalcWindowMinSize(window);
   5934         ImVec2 size_max = ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_Popup)) ? ImVec2(FLT_MAX, FLT_MAX) : ImGui::GetMainViewport()->WorkSize - style.DisplaySafeAreaPadding * 2.0f;
   5935         ImVec2 size_auto_fit = ImClamp(size_desired, size_min, size_max);
   5936 
   5937         // FIXME: CalcWindowAutoFitSize() doesn't take into account that only one axis may be auto-fit when calculating scrollbars,
   5938         // we may need to compute/store three variants of size_auto_fit, for x/y/xy.
   5939         // Here we implement a workaround for child windows only, but a full solution would apply to normal windows as well:
   5940         if ((window->ChildFlags & ImGuiChildFlags_ResizeX) && !(window->ChildFlags & ImGuiChildFlags_ResizeY))
   5941             size_auto_fit.y = window->SizeFull.y;
   5942         else if (!(window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->ChildFlags & ImGuiChildFlags_ResizeY))
   5943             size_auto_fit.x = window->SizeFull.x;
   5944 
   5945         // When the window cannot fit all contents (either because of constraints, either because screen is too small),
   5946         // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
   5947         ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);
   5948         bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - decoration_w_without_scrollbars < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar);
   5949         bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - decoration_h_without_scrollbars < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar);
   5950         if (will_have_scrollbar_x)
   5951             size_auto_fit.y += style.ScrollbarSize;
   5952         if (will_have_scrollbar_y)
   5953             size_auto_fit.x += style.ScrollbarSize;
   5954         return size_auto_fit;
   5955     }
   5956 }
   5957 
   5958 ImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window)
   5959 {
   5960     ImVec2 size_contents_current;
   5961     ImVec2 size_contents_ideal;
   5962     CalcWindowContentSizes(window, &size_contents_current, &size_contents_ideal);
   5963     ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents_ideal);
   5964     ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit);
   5965     return size_final;
   5966 }
   5967 
   5968 static ImGuiCol GetWindowBgColorIdx(ImGuiWindow* window)
   5969 {
   5970     if (window->Flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
   5971         return ImGuiCol_PopupBg;
   5972     if (window->Flags & ImGuiWindowFlags_ChildWindow)
   5973         return ImGuiCol_ChildBg;
   5974     return ImGuiCol_WindowBg;
   5975 }
   5976 
   5977 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
   5978 {
   5979     ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm);                // Expected window upper-left
   5980     ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
   5981     ImVec2 size_expected = pos_max - pos_min;
   5982     ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected);
   5983     *out_pos = pos_min;
   5984     if (corner_norm.x == 0.0f)
   5985         out_pos->x -= (size_constrained.x - size_expected.x);
   5986     if (corner_norm.y == 0.0f)
   5987         out_pos->y -= (size_constrained.y - size_expected.y);
   5988     *out_size = size_constrained;
   5989 }
   5990 
   5991 // Data for resizing from resize grip / corner
   5992 struct ImGuiResizeGripDef
   5993 {
   5994     ImVec2  CornerPosN;
   5995     ImVec2  InnerDir;
   5996     int     AngleMin12, AngleMax12;
   5997 };
   5998 static const ImGuiResizeGripDef resize_grip_def[4] =
   5999 {
   6000     { ImVec2(1, 1), ImVec2(-1, -1), 0, 3 },  // Lower-right
   6001     { ImVec2(0, 1), ImVec2(+1, -1), 3, 6 },  // Lower-left
   6002     { ImVec2(0, 0), ImVec2(+1, +1), 6, 9 },  // Upper-left (Unused)
   6003     { ImVec2(1, 0), ImVec2(-1, +1), 9, 12 }  // Upper-right (Unused)
   6004 };
   6005 
   6006 // Data for resizing from borders
   6007 struct ImGuiResizeBorderDef
   6008 {
   6009     ImVec2  InnerDir;               // Normal toward inside
   6010     ImVec2  SegmentN1, SegmentN2;   // End positions, normalized (0,0: upper left)
   6011     float   OuterAngle;             // Angle toward outside
   6012 };
   6013 static const ImGuiResizeBorderDef resize_border_def[4] =
   6014 {
   6015     { ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f }, // Left
   6016     { ImVec2(-1, 0), ImVec2(1, 0), ImVec2(1, 1), IM_PI * 0.00f }, // Right
   6017     { ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Up
   6018     { ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f }  // Down
   6019 };
   6020 
   6021 static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
   6022 {
   6023     ImRect rect = window->Rect();
   6024     if (thickness == 0.0f)
   6025         rect.Max -= ImVec2(1, 1);
   6026     if (border_n == ImGuiDir_Left)  { return ImRect(rect.Min.x - thickness,    rect.Min.y + perp_padding, rect.Min.x + thickness,    rect.Max.y - perp_padding); }
   6027     if (border_n == ImGuiDir_Right) { return ImRect(rect.Max.x - thickness,    rect.Min.y + perp_padding, rect.Max.x + thickness,    rect.Max.y - perp_padding); }
   6028     if (border_n == ImGuiDir_Up)    { return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness,    rect.Max.x - perp_padding, rect.Min.y + thickness);    }
   6029     if (border_n == ImGuiDir_Down)  { return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness,    rect.Max.x - perp_padding, rect.Max.y + thickness);    }
   6030     IM_ASSERT(0);
   6031     return ImRect();
   6032 }
   6033 
   6034 // 0..3: corners (Lower-right, Lower-left, Unused, Unused)
   6035 ImGuiID ImGui::GetWindowResizeCornerID(ImGuiWindow* window, int n)
   6036 {
   6037     IM_ASSERT(n >= 0 && n < 4);
   6038     ImGuiID id = window->ID;
   6039     id = ImHashStr("#RESIZE", 0, id);
   6040     id = ImHashData(&n, sizeof(int), id);
   6041     return id;
   6042 }
   6043 
   6044 // Borders (Left, Right, Up, Down)
   6045 ImGuiID ImGui::GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir)
   6046 {
   6047     IM_ASSERT(dir >= 0 && dir < 4);
   6048     int n = (int)dir + 4;
   6049     ImGuiID id = window->ID;
   6050     id = ImHashStr("#RESIZE", 0, id);
   6051     id = ImHashData(&n, sizeof(int), id);
   6052     return id;
   6053 }
   6054 
   6055 // Handle resize for: Resize Grips, Borders, Gamepad
   6056 // Return true when using auto-fit (double-click on resize grip)
   6057 static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect)
   6058 {
   6059     ImGuiContext& g = *GImGui;
   6060     ImGuiWindowFlags flags = window->Flags;
   6061 
   6062     if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
   6063         return false;
   6064     if (window->WasActive == false) // Early out to avoid running this code for e.g. a hidden implicit/fallback Debug window.
   6065         return false;
   6066 
   6067     int ret_auto_fit_mask = 0x00;
   6068     const float grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
   6069     const float grip_hover_inner_size = (resize_grip_count > 0) ? IM_TRUNC(grip_draw_size * 0.75f) : 0.0f;
   6070     const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_HOVER_PADDING : 0.0f;
   6071 
   6072     ImRect clamp_rect = visibility_rect;
   6073     const bool window_move_from_title_bar = g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar);
   6074     if (window_move_from_title_bar)
   6075         clamp_rect.Min.y -= window->TitleBarHeight;
   6076 
   6077     ImVec2 pos_target(FLT_MAX, FLT_MAX);
   6078     ImVec2 size_target(FLT_MAX, FLT_MAX);
   6079 
   6080     // Resize grips and borders are on layer 1
   6081     window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
   6082 
   6083     // Manual resize grips
   6084     PushID("#RESIZE");
   6085     for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
   6086     {
   6087         const ImGuiResizeGripDef& def = resize_grip_def[resize_grip_n];
   6088         const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, def.CornerPosN);
   6089 
   6090         // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
   6091         bool hovered, held;
   6092         ImRect resize_rect(corner - def.InnerDir * grip_hover_outer_size, corner + def.InnerDir * grip_hover_inner_size);
   6093         if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
   6094         if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
   6095         ImGuiID resize_grip_id = window->GetID(resize_grip_n); // == GetWindowResizeCornerID()
   6096         ItemAdd(resize_rect, resize_grip_id, NULL, ImGuiItemFlags_NoNav);
   6097         ButtonBehavior(resize_rect, resize_grip_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
   6098         //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
   6099         if (hovered || held)
   6100             g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
   6101 
   6102         if (held && g.IO.MouseDoubleClicked[0])
   6103         {
   6104             // Auto-fit when double-clicking
   6105             size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit);
   6106             ret_auto_fit_mask = 0x03; // Both axises
   6107             ClearActiveID();
   6108         }
   6109         else if (held)
   6110         {
   6111             // Resize from any of the four corners
   6112             // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
   6113             ImVec2 clamp_min = ImVec2(def.CornerPosN.x == 1.0f ? clamp_rect.Min.x : -FLT_MAX, (def.CornerPosN.y == 1.0f || (def.CornerPosN.y == 0.0f && window_move_from_title_bar)) ? clamp_rect.Min.y : -FLT_MAX);
   6114             ImVec2 clamp_max = ImVec2(def.CornerPosN.x == 0.0f ? clamp_rect.Max.x : +FLT_MAX, def.CornerPosN.y == 0.0f ? clamp_rect.Max.y : +FLT_MAX);
   6115             ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(def.InnerDir * grip_hover_outer_size, def.InnerDir * -grip_hover_inner_size, def.CornerPosN); // Corner of the window corresponding to our corner grip
   6116             corner_target = ImClamp(corner_target, clamp_min, clamp_max);
   6117             CalcResizePosSizeFromAnyCorner(window, corner_target, def.CornerPosN, &pos_target, &size_target);
   6118         }
   6119 
   6120         // Only lower-left grip is visible before hovering/activating
   6121         if (resize_grip_n == 0 || held || hovered)
   6122             resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
   6123     }
   6124 
   6125     int resize_border_mask = 0x00;
   6126     if (window->Flags & ImGuiWindowFlags_ChildWindow)
   6127         resize_border_mask |= ((window->ChildFlags & ImGuiChildFlags_ResizeX) ? 0x02 : 0) | ((window->ChildFlags & ImGuiChildFlags_ResizeY) ? 0x08 : 0);
   6128     else
   6129         resize_border_mask = g.IO.ConfigWindowsResizeFromEdges ? 0x0F : 0x00;
   6130     for (int border_n = 0; border_n < 4; border_n++)
   6131     {
   6132         if ((resize_border_mask & (1 << border_n)) == 0)
   6133             continue;
   6134         const ImGuiResizeBorderDef& def = resize_border_def[border_n];
   6135         const ImGuiAxis axis = (border_n == ImGuiDir_Left || border_n == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
   6136 
   6137         bool hovered, held;
   6138         ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_HOVER_PADDING);
   6139         ImGuiID border_id = window->GetID(border_n + 4); // == GetWindowResizeBorderID()
   6140         ItemAdd(border_rect, border_id, NULL, ImGuiItemFlags_NoNav);
   6141         ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
   6142         //GetForegroundDrawList(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
   6143         if (hovered && g.HoveredIdTimer <= WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER)
   6144             hovered = false;
   6145         if (hovered || held)
   6146             g.MouseCursor = (axis == ImGuiAxis_X) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
   6147         if (held && g.IO.MouseDoubleClicked[0])
   6148         {
   6149             // Double-clicking bottom or right border auto-fit on this axis
   6150             // FIXME: CalcWindowAutoFitSize() doesn't take into account that only one side may be auto-fit when calculating scrollbars.
   6151             // FIXME: Support top and right borders: rework CalcResizePosSizeFromAnyCorner() to be reusable in both cases.
   6152             if (border_n == 1 || border_n == 3) // Right and bottom border
   6153             {
   6154                 size_target[axis] = CalcWindowSizeAfterConstraint(window, size_auto_fit)[axis];
   6155                 ret_auto_fit_mask |= (1 << axis);
   6156                 hovered = held = false; // So border doesn't show highlighted at new position
   6157             }
   6158             ClearActiveID();
   6159         }
   6160         else if (held)
   6161         {
   6162             // Switch to relative resizing mode when border geometry moved (e.g. resizing a child altering parent scroll), in order to avoid resizing feedback loop.
   6163             // Currently only using relative mode on resizable child windows, as the problem to solve is more likely noticeable for them, but could apply for all windows eventually.
   6164             // FIXME: May want to generalize this idiom at lower-level, so more widgets can use it!
   6165             const bool just_scrolled_manually_while_resizing = (g.WheelingWindow != NULL && g.WheelingWindowScrolledFrame == g.FrameCount && IsWindowChildOf(window, g.WheelingWindow, false));
   6166             if (g.ActiveIdIsJustActivated || just_scrolled_manually_while_resizing)
   6167             {
   6168                 g.WindowResizeBorderExpectedRect = border_rect;
   6169                 g.WindowResizeRelativeMode = false;
   6170             }
   6171             if ((window->Flags & ImGuiWindowFlags_ChildWindow) && memcmp(&g.WindowResizeBorderExpectedRect, &border_rect, sizeof(ImRect)) != 0)
   6172                 g.WindowResizeRelativeMode = true;
   6173 
   6174             const ImVec2 border_curr = (window->Pos + ImMin(def.SegmentN1, def.SegmentN2) * window->Size);
   6175             const float border_target_rel_mode_for_axis = border_curr[axis] + g.IO.MouseDelta[axis];
   6176             const float border_target_abs_mode_for_axis = g.IO.MousePos[axis] - g.ActiveIdClickOffset[axis] + WINDOWS_HOVER_PADDING; // Match ButtonBehavior() padding above.
   6177 
   6178             // Use absolute mode position
   6179             ImVec2 border_target = window->Pos;
   6180             border_target[axis] = border_target_abs_mode_for_axis;
   6181 
   6182             // Use relative mode target for child window, ignore resize when moving back toward the ideal absolute position.
   6183             bool ignore_resize = false;
   6184             if (g.WindowResizeRelativeMode)
   6185             {
   6186                 //GetForegroundDrawList()->AddText(GetMainViewport()->WorkPos, IM_COL32_WHITE, "Relative Mode");
   6187                 border_target[axis] = border_target_rel_mode_for_axis;
   6188                 if (g.IO.MouseDelta[axis] == 0.0f || (g.IO.MouseDelta[axis] > 0.0f) == (border_target_rel_mode_for_axis > border_target_abs_mode_for_axis))
   6189                     ignore_resize = true;
   6190             }
   6191 
   6192             // Clamp, apply
   6193             ImVec2 clamp_min(border_n == ImGuiDir_Right ? clamp_rect.Min.x : -FLT_MAX, border_n == ImGuiDir_Down || (border_n == ImGuiDir_Up && window_move_from_title_bar) ? clamp_rect.Min.y : -FLT_MAX);
   6194             ImVec2 clamp_max(border_n == ImGuiDir_Left ? clamp_rect.Max.x : +FLT_MAX, border_n == ImGuiDir_Up ? clamp_rect.Max.y : +FLT_MAX);
   6195             border_target = ImClamp(border_target, clamp_min, clamp_max);
   6196             if (flags & ImGuiWindowFlags_ChildWindow) // Clamp resizing of childs within parent
   6197             {
   6198                 ImGuiWindow* parent_window = window->ParentWindow;
   6199                 ImGuiWindowFlags parent_flags = parent_window->Flags;
   6200                 ImRect border_limit_rect = parent_window->InnerRect;
   6201                 border_limit_rect.Expand(ImVec2(-ImMax(parent_window->WindowPadding.x, parent_window->WindowBorderSize), -ImMax(parent_window->WindowPadding.y, parent_window->WindowBorderSize)));
   6202                 if ((axis == ImGuiAxis_X) && ((parent_flags & (ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar)) == 0 || (parent_flags & ImGuiWindowFlags_NoScrollbar)))
   6203                     border_target.x = ImClamp(border_target.x, border_limit_rect.Min.x, border_limit_rect.Max.x);
   6204                 if ((axis == ImGuiAxis_Y) && (parent_flags & ImGuiWindowFlags_NoScrollbar))
   6205                     border_target.y = ImClamp(border_target.y, border_limit_rect.Min.y, border_limit_rect.Max.y);
   6206             }
   6207             if (!ignore_resize)
   6208                 CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2), &pos_target, &size_target);
   6209         }
   6210         if (hovered)
   6211             *border_hovered = border_n;
   6212         if (held)
   6213             *border_held = border_n;
   6214     }
   6215     PopID();
   6216 
   6217     // Restore nav layer
   6218     window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
   6219 
   6220     // Navigation resize (keyboard/gamepad)
   6221     // FIXME: This cannot be moved to NavUpdateWindowing() because CalcWindowSizeAfterConstraint() need to callback into user.
   6222     // Not even sure the callback works here.
   6223     if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
   6224     {
   6225         ImVec2 nav_resize_dir;
   6226         if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift)
   6227             nav_resize_dir = GetKeyMagnitude2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow);
   6228         if (g.NavInputSource == ImGuiInputSource_Gamepad)
   6229             nav_resize_dir = GetKeyMagnitude2d(ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown);
   6230         if (nav_resize_dir.x != 0.0f || nav_resize_dir.y != 0.0f)
   6231         {
   6232             const float NAV_RESIZE_SPEED = 600.0f;
   6233             const float resize_step = NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y);
   6234             g.NavWindowingAccumDeltaSize += nav_resize_dir * resize_step;
   6235             g.NavWindowingAccumDeltaSize = ImMax(g.NavWindowingAccumDeltaSize, clamp_rect.Min - window->Pos - window->Size); // We need Pos+Size >= clmap_rect.Min, so Size >= clmap_rect.Min - Pos, so size_delta >= clmap_rect.Min - window->Pos - window->Size
   6236             g.NavWindowingToggleLayer = false;
   6237             g.NavDisableMouseHover = true;
   6238             resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
   6239             ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaSize);
   6240             if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)
   6241             {
   6242                 // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
   6243                 size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + accum_floored);
   6244                 g.NavWindowingAccumDeltaSize -= accum_floored;
   6245             }
   6246         }
   6247     }
   6248 
   6249     // Apply back modified position/size to window
   6250     const ImVec2 curr_pos = window->Pos;
   6251     const ImVec2 curr_size = window->SizeFull;
   6252     if (size_target.x != FLT_MAX && (window->Size.x != size_target.x || window->SizeFull.x != size_target.x))
   6253         window->Size.x = window->SizeFull.x = size_target.x;
   6254     if (size_target.y != FLT_MAX && (window->Size.y != size_target.y || window->SizeFull.y != size_target.y))
   6255         window->Size.y = window->SizeFull.y = size_target.y;
   6256     if (pos_target.x != FLT_MAX && window->Pos.x != ImTrunc(pos_target.x))
   6257         window->Pos.x = ImTrunc(pos_target.x);
   6258     if (pos_target.y != FLT_MAX && window->Pos.y != ImTrunc(pos_target.y))
   6259         window->Pos.y = ImTrunc(pos_target.y);
   6260     if (curr_pos.x != window->Pos.x || curr_pos.y != window->Pos.y || curr_size.x != window->SizeFull.x || curr_size.y != window->SizeFull.y)
   6261         MarkIniSettingsDirty(window);
   6262 
   6263     // Recalculate next expected border expected coordinates
   6264     if (*border_held != -1)
   6265         g.WindowResizeBorderExpectedRect = GetResizeBorderRect(window, *border_held, grip_hover_inner_size, WINDOWS_HOVER_PADDING);
   6266 
   6267     return ret_auto_fit_mask;
   6268 }
   6269 
   6270 static inline void ClampWindowPos(ImGuiWindow* window, const ImRect& visibility_rect)
   6271 {
   6272     ImGuiContext& g = *GImGui;
   6273     ImVec2 size_for_clamping = window->Size;
   6274     if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
   6275         size_for_clamping.y = window->TitleBarHeight;
   6276     window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max);
   6277 }
   6278 
   6279 static void RenderWindowOuterSingleBorder(ImGuiWindow* window, int border_n, ImU32 border_col, float border_size)
   6280 {
   6281     const ImGuiResizeBorderDef& def = resize_border_def[border_n];
   6282     const float rounding = window->WindowRounding;
   6283     const ImRect border_r = GetResizeBorderRect(window, border_n, rounding, 0.0f);
   6284     window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI * 0.25f, def.OuterAngle);
   6285     window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f);
   6286     window->DrawList->PathStroke(border_col, ImDrawFlags_None, border_size);
   6287 }
   6288 
   6289 static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
   6290 {
   6291     ImGuiContext& g = *GImGui;
   6292     const float border_size = window->WindowBorderSize;
   6293     const ImU32 border_col = GetColorU32(ImGuiCol_Border);
   6294     if (border_size > 0.0f && (window->Flags & ImGuiWindowFlags_NoBackground) == 0)
   6295         window->DrawList->AddRect(window->Pos, window->Pos + window->Size, border_col, window->WindowRounding, 0, window->WindowBorderSize);
   6296     else if (border_size > 0.0f)
   6297     {
   6298         if (window->ChildFlags & ImGuiChildFlags_ResizeX) // Similar code as 'resize_border_mask' computation in UpdateWindowManualResize() but we specifically only always draw explicit child resize border.
   6299             RenderWindowOuterSingleBorder(window, 1, border_col, border_size);
   6300         if (window->ChildFlags & ImGuiChildFlags_ResizeY)
   6301             RenderWindowOuterSingleBorder(window, 3, border_col, border_size);
   6302     }
   6303     if (window->ResizeBorderHovered != -1 || window->ResizeBorderHeld != -1)
   6304     {
   6305         const int border_n = (window->ResizeBorderHeld != -1) ? window->ResizeBorderHeld : window->ResizeBorderHovered;
   6306         const ImU32 border_col_resizing = GetColorU32((window->ResizeBorderHeld != -1) ? ImGuiCol_SeparatorActive : ImGuiCol_SeparatorHovered);
   6307         RenderWindowOuterSingleBorder(window, border_n, border_col_resizing, ImMax(2.0f, window->WindowBorderSize)); // Thicker than usual
   6308     }
   6309     if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
   6310     {
   6311         float y = window->Pos.y + window->TitleBarHeight - 1;
   6312         window->DrawList->AddLine(ImVec2(window->Pos.x + border_size, y), ImVec2(window->Pos.x + window->Size.x - border_size, y), border_col, g.Style.FrameBorderSize);
   6313     }
   6314 }
   6315 
   6316 // Draw background and borders
   6317 // Draw and handle scrollbars
   6318 void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size)
   6319 {
   6320     ImGuiContext& g = *GImGui;
   6321     ImGuiStyle& style = g.Style;
   6322     ImGuiWindowFlags flags = window->Flags;
   6323 
   6324     // Ensure that ScrollBar doesn't read last frame's SkipItems
   6325     IM_ASSERT(window->BeginCount == 0);
   6326     window->SkipItems = false;
   6327 
   6328     // Draw window + handle manual resize
   6329     // As we highlight the title bar when want_focus is set, multiple reappearing windows will have their title bar highlighted on their reappearing frame.
   6330     const float window_rounding = window->WindowRounding;
   6331     const float window_border_size = window->WindowBorderSize;
   6332     if (window->Collapsed)
   6333     {
   6334         // Title bar only
   6335         const float backup_border_size = style.FrameBorderSize;
   6336         g.Style.FrameBorderSize = window->WindowBorderSize;
   6337         ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
   6338         RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
   6339         g.Style.FrameBorderSize = backup_border_size;
   6340     }
   6341     else
   6342     {
   6343         // Window background
   6344         if (!(flags & ImGuiWindowFlags_NoBackground))
   6345         {
   6346             ImU32 bg_col = GetColorU32(GetWindowBgColorIdx(window));
   6347             bool override_alpha = false;
   6348             float alpha = 1.0f;
   6349             if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha)
   6350             {
   6351                 alpha = g.NextWindowData.BgAlphaVal;
   6352                 override_alpha = true;
   6353             }
   6354             if (override_alpha)
   6355                 bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
   6356             window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom);
   6357         }
   6358 
   6359         // Title bar
   6360         if (!(flags & ImGuiWindowFlags_NoTitleBar))
   6361         {
   6362             ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
   6363             window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawFlags_RoundCornersTop);
   6364         }
   6365 
   6366         // Menu bar
   6367         if (flags & ImGuiWindowFlags_MenuBar)
   6368         {
   6369             ImRect menu_bar_rect = window->MenuBarRect();
   6370             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.
   6371             window->DrawList->AddRectFilled(menu_bar_rect.Min + ImVec2(window_border_size, 0), menu_bar_rect.Max - ImVec2(window_border_size, 0), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawFlags_RoundCornersTop);
   6372             if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
   6373                 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
   6374         }
   6375 
   6376         // Scrollbars
   6377         if (window->ScrollbarX)
   6378             Scrollbar(ImGuiAxis_X);
   6379         if (window->ScrollbarY)
   6380             Scrollbar(ImGuiAxis_Y);
   6381 
   6382         // Render resize grips (after their input handling so we don't have a frame of latency)
   6383         if (handle_borders_and_resize_grips && !(flags & ImGuiWindowFlags_NoResize))
   6384         {
   6385             for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
   6386             {
   6387                 const ImU32 col = resize_grip_col[resize_grip_n];
   6388                 if ((col & IM_COL32_A_MASK) == 0)
   6389                     continue;
   6390                 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
   6391                 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
   6392                 window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, window_border_size)));
   6393                 window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, window_border_size) : ImVec2(window_border_size, resize_grip_draw_size)));
   6394                 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);
   6395                 window->DrawList->PathFillConvex(col);
   6396             }
   6397         }
   6398 
   6399         // Borders
   6400         if (handle_borders_and_resize_grips)
   6401             RenderWindowOuterBorders(window);
   6402     }
   6403 }
   6404 
   6405 // Render title text, collapse button, close button
   6406 void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)
   6407 {
   6408     ImGuiContext& g = *GImGui;
   6409     ImGuiStyle& style = g.Style;
   6410     ImGuiWindowFlags flags = window->Flags;
   6411 
   6412     const bool has_close_button = (p_open != NULL);
   6413     const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None);
   6414 
   6415     // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)
   6416     // FIXME-NAV: Might want (or not?) to set the equivalent of ImGuiButtonFlags_NoNavFocus so that mouse clicks on standard title bar items don't necessarily set nav/keyboard ref?
   6417     const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags;
   6418     g.CurrentItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
   6419     window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
   6420 
   6421     // Layout buttons
   6422     // FIXME: Would be nice to generalize the subtleties expressed here into reusable code.
   6423     float pad_l = style.FramePadding.x;
   6424     float pad_r = style.FramePadding.x;
   6425     float button_sz = g.FontSize;
   6426     ImVec2 close_button_pos;
   6427     ImVec2 collapse_button_pos;
   6428     if (has_close_button)
   6429     {
   6430         close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y);
   6431         pad_r += button_sz + style.ItemInnerSpacing.x;
   6432     }
   6433     if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)
   6434     {
   6435         collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y);
   6436         pad_r += button_sz + style.ItemInnerSpacing.x;
   6437     }
   6438     if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)
   6439     {
   6440         collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y + style.FramePadding.y);
   6441         pad_l += button_sz + style.ItemInnerSpacing.x;
   6442     }
   6443 
   6444     // Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
   6445     if (has_collapse_button)
   6446         if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos))
   6447             window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function
   6448 
   6449     // Close button
   6450     if (has_close_button)
   6451         if (CloseButton(window->GetID("#CLOSE"), close_button_pos))
   6452             *p_open = false;
   6453 
   6454     window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
   6455     g.CurrentItemFlags = item_flags_backup;
   6456 
   6457     // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
   6458     // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
   6459     const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? button_sz * 0.80f : 0.0f;
   6460     const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
   6461 
   6462     // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
   6463     // while uncentered title text will still reach edges correctly.
   6464     if (pad_l > style.FramePadding.x)
   6465         pad_l += g.Style.ItemInnerSpacing.x;
   6466     if (pad_r > style.FramePadding.x)
   6467         pad_r += g.Style.ItemInnerSpacing.x;
   6468     if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f)
   6469     {
   6470         float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center
   6471         float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x);
   6472         pad_l = ImMax(pad_l, pad_extend * centerness);
   6473         pad_r = ImMax(pad_r, pad_extend * centerness);
   6474     }
   6475 
   6476     ImRect layout_r(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y, title_bar_rect.Max.x - pad_r, title_bar_rect.Max.y);
   6477     ImRect clip_r(layout_r.Min.x, layout_r.Min.y, ImMin(layout_r.Max.x + g.Style.ItemInnerSpacing.x, title_bar_rect.Max.x), layout_r.Max.y);
   6478     if (flags & ImGuiWindowFlags_UnsavedDocument)
   6479     {
   6480         ImVec2 marker_pos;
   6481         marker_pos.x = ImClamp(layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x + text_size.x, layout_r.Min.x, layout_r.Max.x);
   6482         marker_pos.y = (layout_r.Min.y + layout_r.Max.y) * 0.5f;
   6483         if (marker_pos.x > layout_r.Min.x)
   6484         {
   6485             RenderBullet(window->DrawList, marker_pos, GetColorU32(ImGuiCol_Text));
   6486             clip_r.Max.x = ImMin(clip_r.Max.x, marker_pos.x - (int)(marker_size_x * 0.5f));
   6487         }
   6488     }
   6489     //if (g.IO.KeyShift) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
   6490     //if (g.IO.KeyCtrl) window->DrawList->AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
   6491     RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r);
   6492 }
   6493 
   6494 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
   6495 {
   6496     window->ParentWindow = parent_window;
   6497     window->RootWindow = window->RootWindowPopupTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
   6498     if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
   6499         window->RootWindow = parent_window->RootWindow;
   6500     if (parent_window && (flags & ImGuiWindowFlags_Popup))
   6501         window->RootWindowPopupTree = parent_window->RootWindowPopupTree;
   6502     if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
   6503         window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
   6504     while (window->RootWindowForNav->ChildFlags & ImGuiChildFlags_NavFlattened)
   6505     {
   6506         IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);
   6507         window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
   6508     }
   6509 }
   6510 
   6511 // [EXPERIMENTAL] Called by Begin(). NextWindowData is valid at this point.
   6512 // This is designed as a toy/test-bed for
   6513 void ImGui::UpdateWindowSkipRefresh(ImGuiWindow* window)
   6514 {
   6515     ImGuiContext& g = *GImGui;
   6516     window->SkipRefresh = false;
   6517     if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasRefreshPolicy) == 0)
   6518         return;
   6519     if (g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_TryToAvoidRefresh)
   6520     {
   6521         // FIXME-IDLE: Tests for e.g. mouse clicks or keyboard while focused.
   6522         if (window->Appearing) // If currently appearing
   6523             return;
   6524         if (window->Hidden) // If was hidden (previous frame)
   6525             return;
   6526         if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnHover) && g.HoveredWindow && window->RootWindow == g.HoveredWindow->RootWindow)
   6527             return;
   6528         if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnFocus) && g.NavWindow && window->RootWindow == g.NavWindow->RootWindow)
   6529             return;
   6530         window->DrawList = NULL;
   6531         window->SkipRefresh = true;
   6532     }
   6533 }
   6534 
   6535 // When a modal popup is open, newly created windows that want focus (i.e. are not popups and do not specify ImGuiWindowFlags_NoFocusOnAppearing)
   6536 // should be positioned behind that modal window, unless the window was created inside the modal begin-stack.
   6537 // In case of multiple stacked modals newly created window honors begin stack order and does not go below its own modal parent.
   6538 // - WindowA            // FindBlockingModal() returns Modal1
   6539 //   - WindowB          //                  .. returns Modal1
   6540 //   - Modal1           //                  .. returns Modal2
   6541 //      - WindowC       //                  .. returns Modal2
   6542 //          - WindowD   //                  .. returns Modal2
   6543 //          - Modal2    //                  .. returns Modal2
   6544 //            - WindowE //                  .. returns NULL
   6545 // Notes:
   6546 // - FindBlockingModal(NULL) == NULL is generally equivalent to GetTopMostPopupModal() == NULL.
   6547 //   Only difference is here we check for ->Active/WasActive but it may be unnecessary.
   6548 ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window)
   6549 {
   6550     ImGuiContext& g = *GImGui;
   6551     if (g.OpenPopupStack.Size <= 0)
   6552         return NULL;
   6553 
   6554     // Find a modal that has common parent with specified window. Specified window should be positioned behind that modal.
   6555     for (ImGuiPopupData& popup_data : g.OpenPopupStack)
   6556     {
   6557         ImGuiWindow* popup_window = popup_data.Window;
   6558         if (popup_window == NULL || !(popup_window->Flags & ImGuiWindowFlags_Modal))
   6559             continue;
   6560         if (!popup_window->Active && !popup_window->WasActive)      // Check WasActive, because this code may run before popup renders on current frame, also check Active to handle newly created windows.
   6561             continue;
   6562         if (window == NULL)                                         // FindBlockingModal(NULL) test for if FocusWindow(NULL) is naturally possible via a mouse click.
   6563             return popup_window;
   6564         if (IsWindowWithinBeginStackOf(window, popup_window))       // Window may be over modal
   6565             continue;
   6566         return popup_window;                                        // Place window right below first block modal
   6567     }
   6568     return NULL;
   6569 }
   6570 
   6571 // Push a new Dear ImGui window to add widgets to.
   6572 // - 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.
   6573 // - Begin/End can be called multiple times during the frame with the same window name to append content.
   6574 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
   6575 //   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.
   6576 // - 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.
   6577 // - 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.
   6578 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
   6579 {
   6580     ImGuiContext& g = *GImGui;
   6581     const ImGuiStyle& style = g.Style;
   6582     IM_ASSERT(name != NULL && name[0] != '\0');     // Window name required
   6583     IM_ASSERT(g.WithinFrameScope);                  // Forgot to call ImGui::NewFrame()
   6584     IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
   6585 
   6586     // Find or create
   6587     ImGuiWindow* window = FindWindowByName(name);
   6588     const bool window_just_created = (window == NULL);
   6589     if (window_just_created)
   6590         window = CreateNewWindow(name, flags);
   6591 
   6592     // [DEBUG] Debug break requested by user
   6593     if (g.DebugBreakInWindow == window->ID)
   6594         IM_DEBUG_BREAK();
   6595 
   6596     // Automatically disable manual moving/resizing when NoInputs is set
   6597     if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
   6598         flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
   6599 
   6600     const int current_frame = g.FrameCount;
   6601     const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
   6602     window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow);
   6603 
   6604     // Update the Appearing flag
   6605     bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1);   // Not using !WasActive because the implicit "Debug" window would always toggle off->on
   6606     if (flags & ImGuiWindowFlags_Popup)
   6607     {
   6608         ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
   6609         window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
   6610         window_just_activated_by_user |= (window != popup_ref.Window);
   6611     }
   6612     window->Appearing = window_just_activated_by_user;
   6613     if (window->Appearing)
   6614         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
   6615 
   6616     // Update Flags, LastFrameActive, BeginOrderXXX fields
   6617     if (first_begin_of_the_frame)
   6618     {
   6619         UpdateWindowInFocusOrderList(window, window_just_created, flags);
   6620         window->Flags = (ImGuiWindowFlags)flags;
   6621         window->ChildFlags = (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlags : 0;
   6622         window->LastFrameActive = current_frame;
   6623         window->LastTimeActive = (float)g.Time;
   6624         window->BeginOrderWithinParent = 0;
   6625         window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
   6626     }
   6627     else
   6628     {
   6629         flags = window->Flags;
   6630     }
   6631 
   6632     // 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
   6633     ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back().Window;
   6634     ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
   6635     IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
   6636 
   6637     // We allow window memory to be compacted so recreate the base stack when needed.
   6638     if (window->IDStack.Size == 0)
   6639         window->IDStack.push_back(window->ID);
   6640 
   6641     // Add to stack
   6642     g.CurrentWindow = window;
   6643     ImGuiWindowStackData window_stack_data;
   6644     window_stack_data.Window = window;
   6645     window_stack_data.ParentLastItemDataBackup = g.LastItemData;
   6646     window_stack_data.StackSizesOnBegin.SetToContextState(&g);
   6647     window_stack_data.DisabledOverrideReenable = (flags & ImGuiWindowFlags_Tooltip) && (g.CurrentItemFlags & ImGuiItemFlags_Disabled);
   6648     g.CurrentWindowStack.push_back(window_stack_data);
   6649     if (flags & ImGuiWindowFlags_ChildMenu)
   6650         g.BeginMenuDepth++;
   6651 
   6652     // Update ->RootWindow and others pointers (before any possible call to FocusWindow)
   6653     if (first_begin_of_the_frame)
   6654     {
   6655         UpdateWindowParentAndRootLinks(window, flags, parent_window);
   6656         window->ParentWindowInBeginStack = parent_window_in_stack;
   6657 
   6658         // There's little point to expose a flag to set this: because the interesting cases won't be using parent_window_in_stack,
   6659         // e.g. linking a tool window in a standalone viewport to a document window, regardless of their Begin() stack parenting. (#6798)
   6660         window->ParentWindowForFocusRoute = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window_in_stack : NULL;
   6661     }
   6662 
   6663     // Add to focus scope stack
   6664     PushFocusScope((window->ChildFlags & ImGuiChildFlags_NavFlattened) ? g.CurrentFocusScopeId : window->ID);
   6665     window->NavRootFocusScopeId = g.CurrentFocusScopeId;
   6666 
   6667     // Add to popup stacks: update OpenPopupStack[] data, push to BeginPopupStack[]
   6668     if (flags & ImGuiWindowFlags_Popup)
   6669     {
   6670         ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
   6671         popup_ref.Window = window;
   6672         popup_ref.ParentNavLayer = parent_window_in_stack->DC.NavLayerCurrent;
   6673         g.BeginPopupStack.push_back(popup_ref);
   6674         window->PopupId = popup_ref.PopupId;
   6675     }
   6676 
   6677     // Process SetNextWindow***() calls
   6678     // (FIXME: Consider splitting the HasXXX flags into X/Y components
   6679     bool window_pos_set_by_api = false;
   6680     bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
   6681     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos)
   6682     {
   6683         window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
   6684         if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
   6685         {
   6686             // May be processed on the next frame if this is our first frame and we are measuring size
   6687             // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
   6688             window->SetWindowPosVal = g.NextWindowData.PosVal;
   6689             window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
   6690             window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
   6691         }
   6692         else
   6693         {
   6694             SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
   6695         }
   6696     }
   6697     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)
   6698     {
   6699         window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
   6700         window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
   6701         if ((window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0) // Axis-specific conditions for BeginChild()
   6702             g.NextWindowData.SizeVal.x = window->SizeFull.x;
   6703         if ((window->ChildFlags & ImGuiChildFlags_ResizeY) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0)
   6704             g.NextWindowData.SizeVal.y = window->SizeFull.y;
   6705         SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
   6706     }
   6707     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasScroll)
   6708     {
   6709         if (g.NextWindowData.ScrollVal.x >= 0.0f)
   6710         {
   6711             window->ScrollTarget.x = g.NextWindowData.ScrollVal.x;
   6712             window->ScrollTargetCenterRatio.x = 0.0f;
   6713         }
   6714         if (g.NextWindowData.ScrollVal.y >= 0.0f)
   6715         {
   6716             window->ScrollTarget.y = g.NextWindowData.ScrollVal.y;
   6717             window->ScrollTargetCenterRatio.y = 0.0f;
   6718         }
   6719     }
   6720     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize)
   6721         window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;
   6722     else if (first_begin_of_the_frame)
   6723         window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);
   6724     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed)
   6725         SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
   6726     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus)
   6727         FocusWindow(window);
   6728     if (window->Appearing)
   6729         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
   6730 
   6731     // [EXPERIMENTAL] Skip Refresh mode
   6732     UpdateWindowSkipRefresh(window);
   6733 
   6734     // Nested root windows (typically tooltips) override disabled state
   6735     if (window_stack_data.DisabledOverrideReenable && window->RootWindow == window)
   6736         BeginDisabledOverrideReenable();
   6737 
   6738     // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
   6739     g.CurrentWindow = NULL;
   6740 
   6741     // When reusing window again multiple times a frame, just append content (don't need to setup again)
   6742     if (first_begin_of_the_frame && !window->SkipRefresh)
   6743     {
   6744         // Initialize
   6745         const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
   6746         const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);
   6747         window->Active = true;
   6748         window->HasCloseButton = (p_open != NULL);
   6749         window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX);
   6750         window->IDStack.resize(1);
   6751         window->DrawList->_ResetForNewFrame();
   6752         window->DC.CurrentTableIdx = -1;
   6753 
   6754         // Restore buffer capacity when woken from a compacted state, to avoid
   6755         if (window->MemoryCompacted)
   6756             GcAwakeTransientWindowBuffers(window);
   6757 
   6758         // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
   6759         // 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.
   6760         bool window_title_visible_elsewhere = false;
   6761         if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0)   // Window titles visible when using CTRL+TAB
   6762             window_title_visible_elsewhere = true;
   6763         if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
   6764         {
   6765             size_t buf_len = (size_t)window->NameBufLen;
   6766             window->Name = ImStrdupcpy(window->Name, &buf_len, name);
   6767             window->NameBufLen = (int)buf_len;
   6768         }
   6769 
   6770         // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
   6771 
   6772         // Update contents size from last frame for auto-fitting (or use explicit size)
   6773         CalcWindowContentSizes(window, &window->ContentSize, &window->ContentSizeIdeal);
   6774         if (window->HiddenFramesCanSkipItems > 0)
   6775             window->HiddenFramesCanSkipItems--;
   6776         if (window->HiddenFramesCannotSkipItems > 0)
   6777             window->HiddenFramesCannotSkipItems--;
   6778         if (window->HiddenFramesForRenderOnly > 0)
   6779             window->HiddenFramesForRenderOnly--;
   6780 
   6781         // Hide new windows for one frame until they calculate their size
   6782         if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
   6783             window->HiddenFramesCannotSkipItems = 1;
   6784 
   6785         // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
   6786         // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
   6787         if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
   6788         {
   6789             window->HiddenFramesCannotSkipItems = 1;
   6790             if (flags & ImGuiWindowFlags_AlwaysAutoResize)
   6791             {
   6792                 if (!window_size_x_set_by_api)
   6793                     window->Size.x = window->SizeFull.x = 0.f;
   6794                 if (!window_size_y_set_by_api)
   6795                     window->Size.y = window->SizeFull.y = 0.f;
   6796                 window->ContentSize = window->ContentSizeIdeal = ImVec2(0.f, 0.f);
   6797             }
   6798         }
   6799 
   6800         // SELECT VIEWPORT
   6801         // FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style)
   6802 
   6803         ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport();
   6804         SetWindowViewport(window, viewport);
   6805         SetCurrentWindow(window);
   6806 
   6807         // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)
   6808 
   6809         if (flags & ImGuiWindowFlags_ChildWindow)
   6810             window->WindowBorderSize = style.ChildBorderSize;
   6811         else
   6812             window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
   6813         window->WindowPadding = style.WindowPadding;
   6814         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !(window->ChildFlags & ImGuiChildFlags_AlwaysUseWindowPadding) && window->WindowBorderSize == 0.0f)
   6815             window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
   6816 
   6817         // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size.
   6818         window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
   6819         window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
   6820         window->TitleBarHeight = (flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : g.FontSize + g.Style.FramePadding.y * 2.0f;
   6821         window->MenuBarHeight = (flags & ImGuiWindowFlags_MenuBar) ? window->DC.MenuBarOffset.y + g.FontSize + g.Style.FramePadding.y * 2.0f : 0.0f;
   6822 
   6823         // Depending on condition we use previous or current window size to compare against contents size to decide if a scrollbar should be visible.
   6824         // Those flags will be altered further down in the function depending on more conditions.
   6825         bool use_current_size_for_scrollbar_x = window_just_created;
   6826         bool use_current_size_for_scrollbar_y = window_just_created;
   6827         if (window_size_x_set_by_api && window->ContentSizeExplicit.x != 0.0f)
   6828             use_current_size_for_scrollbar_x = true;
   6829         if (window_size_y_set_by_api && window->ContentSizeExplicit.y != 0.0f) // #7252
   6830             use_current_size_for_scrollbar_y = true;
   6831 
   6832         // Collapse window by double-clicking on title bar
   6833         // 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
   6834         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
   6835         {
   6836             // 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.
   6837             ImRect title_bar_rect = window->TitleBarRect();
   6838             if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max))
   6839                 if (g.IO.MouseClickedCount[0] == 2 && GetKeyOwner(ImGuiKey_MouseLeft) == ImGuiKeyOwner_NoOwner)
   6840                     window->WantCollapseToggle = true;
   6841             if (window->WantCollapseToggle)
   6842             {
   6843                 window->Collapsed = !window->Collapsed;
   6844                 if (!window->Collapsed)
   6845                     use_current_size_for_scrollbar_y = true;
   6846                 MarkIniSettingsDirty(window);
   6847             }
   6848         }
   6849         else
   6850         {
   6851             window->Collapsed = false;
   6852         }
   6853         window->WantCollapseToggle = false;
   6854 
   6855         // SIZE
   6856 
   6857         // Outer Decoration Sizes
   6858         // (we need to clear ScrollbarSize immediately as CalcWindowAutoFitSize() needs it and can be called from other locations).
   6859         const ImVec2 scrollbar_sizes_from_last_frame = window->ScrollbarSizes;
   6860         window->DecoOuterSizeX1 = 0.0f;
   6861         window->DecoOuterSizeX2 = 0.0f;
   6862         window->DecoOuterSizeY1 = window->TitleBarHeight + window->MenuBarHeight;
   6863         window->DecoOuterSizeY2 = 0.0f;
   6864         window->ScrollbarSizes = ImVec2(0.0f, 0.0f);
   6865 
   6866         // Calculate auto-fit size, handle automatic resize
   6867         const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal);
   6868         if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
   6869         {
   6870             // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
   6871             if (!window_size_x_set_by_api)
   6872             {
   6873                 window->SizeFull.x = size_auto_fit.x;
   6874                 use_current_size_for_scrollbar_x = true;
   6875             }
   6876             if (!window_size_y_set_by_api)
   6877             {
   6878                 window->SizeFull.y = size_auto_fit.y;
   6879                 use_current_size_for_scrollbar_y = true;
   6880             }
   6881         }
   6882         else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
   6883         {
   6884             // Auto-fit may only grow window during the first few frames
   6885             // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
   6886             if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
   6887             {
   6888                 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
   6889                 use_current_size_for_scrollbar_x = true;
   6890             }
   6891             if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
   6892             {
   6893                 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
   6894                 use_current_size_for_scrollbar_y = true;
   6895             }
   6896             if (!window->Collapsed)
   6897                 MarkIniSettingsDirty(window);
   6898         }
   6899 
   6900         // Apply minimum/maximum window size constraints and final size
   6901         window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull);
   6902         window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
   6903 
   6904         // POSITION
   6905 
   6906         // Popup latch its initial position, will position itself when it appears next frame
   6907         if (window_just_activated_by_user)
   6908         {
   6909             window->AutoPosLastDirection = ImGuiDir_None;
   6910             if ((flags & ImGuiWindowFlags_Popup) != 0 && !(flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api) // FIXME: BeginPopup() could use SetNextWindowPos()
   6911                 window->Pos = g.BeginPopupStack.back().OpenPopupPos;
   6912         }
   6913 
   6914         // Position child window
   6915         if (flags & ImGuiWindowFlags_ChildWindow)
   6916         {
   6917             IM_ASSERT(parent_window && parent_window->Active);
   6918             window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
   6919             parent_window->DC.ChildWindows.push_back(window);
   6920             if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
   6921                 window->Pos = parent_window->DC.CursorPos;
   6922         }
   6923 
   6924         const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0);
   6925         if (window_pos_with_pivot)
   6926             SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering)
   6927         else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
   6928             window->Pos = FindBestWindowPosForPopup(window);
   6929         else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
   6930             window->Pos = FindBestWindowPosForPopup(window);
   6931         else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
   6932             window->Pos = FindBestWindowPosForPopup(window);
   6933 
   6934         // Calculate the range of allowed position for that window (to be movable and visible past safe area padding)
   6935         // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect.
   6936         ImRect viewport_rect(viewport->GetMainRect());
   6937         ImRect viewport_work_rect(viewport->GetWorkRect());
   6938         ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
   6939         ImRect visibility_rect(viewport_work_rect.Min + visibility_padding, viewport_work_rect.Max - visibility_padding);
   6940 
   6941         // Clamp position/size so window stays visible within its viewport or monitor
   6942         // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
   6943         if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow))
   6944             if (viewport_rect.GetWidth() > 0.0f && viewport_rect.GetHeight() > 0.0f)
   6945                 ClampWindowPos(window, visibility_rect);
   6946         window->Pos = ImTrunc(window->Pos);
   6947 
   6948         // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
   6949         // Large values tend to lead to variety of artifacts and are not recommended.
   6950         window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
   6951 
   6952         // For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts.
   6953         //if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar))
   6954         //    window->WindowRounding = ImMin(window->WindowRounding, g.FontSize + style.FramePadding.y * 2.0f);
   6955 
   6956         // Apply window focus (new and reactivated windows are moved to front)
   6957         bool want_focus = false;
   6958         if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
   6959         {
   6960             if (flags & ImGuiWindowFlags_Popup)
   6961                 want_focus = true;
   6962             else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0)
   6963                 want_focus = true;
   6964         }
   6965 
   6966         // [Test Engine] Register whole window in the item system (before submitting further decorations)
   6967 #ifdef IMGUI_ENABLE_TEST_ENGINE
   6968         if (g.TestEngineHookItems)
   6969         {
   6970             IM_ASSERT(window->IDStack.Size == 1);
   6971             window->IDStack.Size = 0; // As window->IDStack[0] == window->ID here, make sure TestEngine doesn't erroneously see window as parent of itself.
   6972             IMGUI_TEST_ENGINE_ITEM_ADD(window->ID, window->Rect(), NULL);
   6973             IMGUI_TEST_ENGINE_ITEM_INFO(window->ID, window->Name, (g.HoveredWindow == window) ? ImGuiItemStatusFlags_HoveredRect : 0);
   6974             window->IDStack.Size = 1;
   6975         }
   6976 #endif
   6977 
   6978         // Handle manual resize: Resize Grips, Borders, Gamepad
   6979         int border_hovered = -1, border_held = -1;
   6980         ImU32 resize_grip_col[4] = {};
   6981         const int resize_grip_count = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup)) ? 0 : g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it.
   6982         const float resize_grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
   6983         if (!window->Collapsed)
   6984             if (int auto_fit_mask = UpdateWindowManualResize(window, size_auto_fit, &border_hovered, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect))
   6985             {
   6986                 if (auto_fit_mask & (1 << ImGuiAxis_X))
   6987                     use_current_size_for_scrollbar_x = true;
   6988                 if (auto_fit_mask & (1 << ImGuiAxis_Y))
   6989                     use_current_size_for_scrollbar_y = true;
   6990             }
   6991         window->ResizeBorderHovered = (signed char)border_hovered;
   6992         window->ResizeBorderHeld = (signed char)border_held;
   6993 
   6994         // SCROLLBAR VISIBILITY
   6995 
   6996         // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).
   6997         if (!window->Collapsed)
   6998         {
   6999             // When reading the current size we need to read it after size constraints have been applied.
   7000             // Intentionally use previous frame values for InnerRect and ScrollbarSizes.
   7001             // And when we use window->DecorationUp here it doesn't have ScrollbarSizes.y applied yet.
   7002             ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2));
   7003             ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + scrollbar_sizes_from_last_frame;
   7004             ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;
   7005             float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;
   7006             float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;
   7007             //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?
   7008             window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
   7009             window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((needed_size_from_last_frame.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
   7010             if (window->ScrollbarX && !window->ScrollbarY)
   7011                 window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);
   7012             window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
   7013 
   7014             // Amend the partially filled window->DecorationXXX values.
   7015             window->DecoOuterSizeX2 += window->ScrollbarSizes.x;
   7016             window->DecoOuterSizeY2 += window->ScrollbarSizes.y;
   7017         }
   7018 
   7019         // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)
   7020         // Update various regions. Variables they depend on should be set above in this function.
   7021         // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.
   7022 
   7023         // Outer rectangle
   7024         // Not affected by window border size. Used by:
   7025         // - FindHoveredWindow() (w/ extra padding when border resize is enabled)
   7026         // - Begin() initial clipping rect for drawing window background and borders.
   7027         // - Begin() clipping whole child
   7028         const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;
   7029         const ImRect outer_rect = window->Rect();
   7030         const ImRect title_bar_rect = window->TitleBarRect();
   7031         window->OuterRectClipped = outer_rect;
   7032         window->OuterRectClipped.ClipWith(host_rect);
   7033 
   7034         // Inner rectangle
   7035         // Not affected by window border size. Used by:
   7036         // - InnerClipRect
   7037         // - ScrollToRectEx()
   7038         // - NavUpdatePageUpPageDown()
   7039         // - Scrollbar()
   7040         window->InnerRect.Min.x = window->Pos.x + window->DecoOuterSizeX1;
   7041         window->InnerRect.Min.y = window->Pos.y + window->DecoOuterSizeY1;
   7042         window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->DecoOuterSizeX2;
   7043         window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->DecoOuterSizeY2;
   7044 
   7045         // Inner clipping rectangle.
   7046         // - Extend a outside of normal work region up to borders.
   7047         // - This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
   7048         // - It also makes clipped items be more noticeable.
   7049         // - And is consistent on both axis (prior to 2024/05/03 ClipRect used WindowPadding.x * 0.5f on left and right edge), see #3312
   7050         // - Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
   7051         // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
   7052         // Affected by window/frame border size. Used by:
   7053         // - Begin() initial clip rect
   7054         float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
   7055         window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + window->WindowBorderSize);
   7056         window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size);
   7057         window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - window->WindowBorderSize);
   7058         window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize);
   7059         window->InnerClipRect.ClipWithFull(host_rect);
   7060 
   7061         // Default item width. Make it proportional to window size if window manually resizes
   7062         if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
   7063             window->ItemWidthDefault = ImTrunc(window->Size.x * 0.65f);
   7064         else
   7065             window->ItemWidthDefault = ImTrunc(g.FontSize * 16.0f);
   7066 
   7067         // SCROLLING
   7068 
   7069         // Lock down maximum scrolling
   7070         // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate
   7071         // for right/bottom aligned items without creating a scrollbar.
   7072         window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth());
   7073         window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());
   7074 
   7075         // Apply scrolling
   7076         window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);
   7077         window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
   7078         window->DecoInnerSizeX1 = window->DecoInnerSizeY1 = 0.0f;
   7079 
   7080         // DRAWING
   7081 
   7082         // Setup draw list and outer clipping rectangle
   7083         IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0);
   7084         window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
   7085         PushClipRect(host_rect.Min, host_rect.Max, false);
   7086 
   7087         // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71)
   7088         // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
   7089         // FIXME: User code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected (github #4493)
   7090         {
   7091             bool render_decorations_in_parent = false;
   7092             if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
   7093             {
   7094                 // - We test overlap with the previous child window only (testing all would end up being O(log N) not a good investment here)
   7095                 // - We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping childs
   7096                 ImGuiWindow* previous_child = parent_window->DC.ChildWindows.Size >= 2 ? parent_window->DC.ChildWindows[parent_window->DC.ChildWindows.Size - 2] : NULL;
   7097                 bool previous_child_overlapping = previous_child ? previous_child->Rect().Overlaps(window->Rect()) : false;
   7098                 bool parent_is_empty = (parent_window->DrawList->VtxBuffer.Size == 0);
   7099                 if (window->DrawList->CmdBuffer.back().ElemCount == 0 && !parent_is_empty && !previous_child_overlapping)
   7100                     render_decorations_in_parent = true;
   7101             }
   7102             if (render_decorations_in_parent)
   7103                 window->DrawList = parent_window->DrawList;
   7104 
   7105             // Handle title bar, scrollbar, resize grips and resize borders
   7106             const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
   7107             const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
   7108             const bool handle_borders_and_resize_grips = true; // This exists to facilitate merge with 'docking' branch.
   7109             RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, handle_borders_and_resize_grips, resize_grip_count, resize_grip_col, resize_grip_draw_size);
   7110 
   7111             if (render_decorations_in_parent)
   7112                 window->DrawList = &window->DrawListInst;
   7113         }
   7114 
   7115         // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
   7116 
   7117         // Work rectangle.
   7118         // Affected by window padding and border size. Used by:
   7119         // - Columns() for right-most edge
   7120         // - TreeNode(), CollapsingHeader() for right-most edge
   7121         // - BeginTabBar() for right-most edge
   7122         const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);
   7123         const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);
   7124         const float work_rect_size_x = (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : ImMax(allow_scrollbar_x ? window->ContentSize.x : 0.0f, window->Size.x - window->WindowPadding.x * 2.0f - (window->DecoOuterSizeX1 + window->DecoOuterSizeX2)));
   7125         const float work_rect_size_y = (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : ImMax(allow_scrollbar_y ? window->ContentSize.y : 0.0f, window->Size.y - window->WindowPadding.y * 2.0f - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2)));
   7126         window->WorkRect.Min.x = ImTrunc(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));
   7127         window->WorkRect.Min.y = ImTrunc(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
   7128         window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
   7129         window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
   7130         window->ParentWorkRect = window->WorkRect;
   7131 
   7132         // [LEGACY] Content Region
   7133         // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
   7134         // Unless explicit content size is specified by user, this currently represent the region leading to no scrolling.
   7135         // Used by:
   7136         // - Mouse wheel scrolling + many other things
   7137         window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x + window->DecoOuterSizeX1;
   7138         window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->DecoOuterSizeY1;
   7139         window->ContentRegionRect.Max.x = window->ContentRegionRect.Min.x + (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : (window->Size.x - window->WindowPadding.x * 2.0f - (window->DecoOuterSizeX1 + window->DecoOuterSizeX2)));
   7140         window->ContentRegionRect.Max.y = window->ContentRegionRect.Min.y + (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : (window->Size.y - window->WindowPadding.y * 2.0f - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2)));
   7141 
   7142         // Setup drawing context
   7143         // (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.)
   7144         window->DC.Indent.x = window->DecoOuterSizeX1 + window->WindowPadding.x - window->Scroll.x;
   7145         window->DC.GroupOffset.x = 0.0f;
   7146         window->DC.ColumnsOffset.x = 0.0f;
   7147 
   7148         // Record the loss of precision of CursorStartPos which can happen due to really large scrolling amount.
   7149         // This is used by clipper to compensate and fix the most common use case of large scroll area. Easy and cheap, next best thing compared to switching everything to double or ImU64.
   7150         double start_pos_highp_x = (double)window->Pos.x + window->WindowPadding.x - (double)window->Scroll.x + window->DecoOuterSizeX1 + window->DC.ColumnsOffset.x;
   7151         double start_pos_highp_y = (double)window->Pos.y + window->WindowPadding.y - (double)window->Scroll.y + window->DecoOuterSizeY1;
   7152         window->DC.CursorStartPos  = ImVec2((float)start_pos_highp_x, (float)start_pos_highp_y);
   7153         window->DC.CursorStartPosLossyness = ImVec2((float)(start_pos_highp_x - window->DC.CursorStartPos.x), (float)(start_pos_highp_y - window->DC.CursorStartPos.y));
   7154         window->DC.CursorPos = window->DC.CursorStartPos;
   7155         window->DC.CursorPosPrevLine = window->DC.CursorPos;
   7156         window->DC.CursorMaxPos = window->DC.CursorStartPos;
   7157         window->DC.IdealMaxPos = window->DC.CursorStartPos;
   7158         window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
   7159         window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
   7160         window->DC.IsSameLine = window->DC.IsSetPos = false;
   7161 
   7162         window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
   7163         window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext;
   7164         window->DC.NavLayersActiveMaskNext = 0x00;
   7165         window->DC.NavIsScrollPushableX = true;
   7166         window->DC.NavHideHighlightOneFrame = false;
   7167         window->DC.NavWindowHasScrollY = (window->ScrollMax.y > 0.0f);
   7168 
   7169         window->DC.MenuBarAppending = false;
   7170         window->DC.MenuColumns.Update(style.ItemSpacing.x, window_just_activated_by_user);
   7171         window->DC.TreeDepth = 0;
   7172         window->DC.TreeHasStackDataDepthMask = 0x00;
   7173         window->DC.ChildWindows.resize(0);
   7174         window->DC.StateStorage = &window->StateStorage;
   7175         window->DC.CurrentColumns = NULL;
   7176         window->DC.LayoutType = ImGuiLayoutType_Vertical;
   7177         window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
   7178 
   7179         window->DC.ItemWidth = window->ItemWidthDefault;
   7180         window->DC.TextWrapPos = -1.0f; // disabled
   7181         window->DC.ItemWidthStack.resize(0);
   7182         window->DC.TextWrapPosStack.resize(0);
   7183         if (flags & ImGuiWindowFlags_Modal)
   7184             window->DC.ModalDimBgColor = ColorConvertFloat4ToU32(GetStyleColorVec4(ImGuiCol_ModalWindowDimBg));
   7185 
   7186         if (window->AutoFitFramesX > 0)
   7187             window->AutoFitFramesX--;
   7188         if (window->AutoFitFramesY > 0)
   7189             window->AutoFitFramesY--;
   7190 
   7191         // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
   7192         // We ImGuiFocusRequestFlags_UnlessBelowModal to:
   7193         // - Avoid focusing a window that is created outside of a modal. This will prevent active modal from being closed.
   7194         // - Position window behind the modal that is not a begin-parent of this window.
   7195         if (want_focus)
   7196             FocusWindow(window, ImGuiFocusRequestFlags_UnlessBelowModal);
   7197         if (want_focus && window == g.NavWindow)
   7198             NavInitWindow(window, false); // <-- this is in the way for us to be able to defer and sort reappearing FocusWindow() calls
   7199 
   7200         // Title bar
   7201         if (!(flags & ImGuiWindowFlags_NoTitleBar))
   7202             RenderWindowTitleBarContents(window, ImRect(title_bar_rect.Min.x + window->WindowBorderSize, title_bar_rect.Min.y, title_bar_rect.Max.x - window->WindowBorderSize, title_bar_rect.Max.y), name, p_open);
   7203 
   7204         // Clear hit test shape every frame
   7205         window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0;
   7206 
   7207         // Pressing CTRL+C while holding on a window copy its content to the clipboard
   7208         // 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.
   7209         // Maybe we can support CTRL+C on every element?
   7210         /*
   7211         //if (g.NavWindow == window && g.ActiveId == 0)
   7212         if (g.ActiveId == window->MoveId)
   7213             if (g.IO.KeyCtrl && IsKeyPressed(ImGuiKey_C))
   7214                 LogToClipboard();
   7215         */
   7216 
   7217         // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
   7218         // This is useful to allow creating context menus on title bar only, etc.
   7219         SetLastItemDataForWindow(window, title_bar_rect);
   7220 
   7221         // [DEBUG]
   7222 #ifndef IMGUI_DISABLE_DEBUG_TOOLS
   7223         if (g.DebugLocateId != 0 && (window->ID == g.DebugLocateId || window->MoveId == g.DebugLocateId))
   7224             DebugLocateItemResolveWithLastItem();
   7225 #endif
   7226 
   7227         // [Test Engine] Register title bar / tab with MoveId.
   7228 #ifdef IMGUI_ENABLE_TEST_ENGINE
   7229         if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
   7230             IMGUI_TEST_ENGINE_ITEM_ADD(g.LastItemData.ID, g.LastItemData.Rect, &g.LastItemData);
   7231 #endif
   7232     }
   7233     else
   7234     {
   7235         // Skip refresh always mark active
   7236         if (window->SkipRefresh)
   7237             window->Active = true;
   7238 
   7239         // Append
   7240         SetCurrentWindow(window);
   7241         SetLastItemDataForWindow(window, window->TitleBarRect());
   7242     }
   7243 
   7244     if (!window->SkipRefresh)
   7245         PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
   7246 
   7247     // 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)
   7248     window->WriteAccessed = false;
   7249     window->BeginCount++;
   7250     g.NextWindowData.ClearFlags();
   7251 
   7252     // Update visibility
   7253     if (first_begin_of_the_frame && !window->SkipRefresh)
   7254     {
   7255         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_ChildMenu))
   7256         {
   7257             // Child window can be out of sight and have "negative" clip windows.
   7258             // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
   7259             IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
   7260             const bool nav_request = (window->ChildFlags & ImGuiChildFlags_NavFlattened) && (g.NavAnyRequest && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);
   7261             if (!g.LogEnabled && !nav_request)
   7262                 if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
   7263                 {
   7264                     if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
   7265                         window->HiddenFramesCannotSkipItems = 1;
   7266                     else
   7267                         window->HiddenFramesCanSkipItems = 1;
   7268                 }
   7269 
   7270             // Hide along with parent or if parent is collapsed
   7271             if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))
   7272                 window->HiddenFramesCanSkipItems = 1;
   7273             if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0))
   7274                 window->HiddenFramesCannotSkipItems = 1;
   7275         }
   7276 
   7277         // 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)
   7278         if (style.Alpha <= 0.0f)
   7279             window->HiddenFramesCanSkipItems = 1;
   7280 
   7281         // Update the Hidden flag
   7282         bool hidden_regular = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0);
   7283         window->Hidden = hidden_regular || (window->HiddenFramesForRenderOnly > 0);
   7284 
   7285         // Disable inputs for requested number of frames
   7286         if (window->DisableInputsFrames > 0)
   7287         {
   7288             window->DisableInputsFrames--;
   7289             window->Flags |= ImGuiWindowFlags_NoInputs;
   7290         }
   7291 
   7292         // Update the SkipItems flag, used to early out of all items functions (no layout required)
   7293         bool skip_items = false;
   7294         if (window->Collapsed || !window->Active || hidden_regular)
   7295             if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0)
   7296                 skip_items = true;
   7297         window->SkipItems = skip_items;
   7298     }
   7299     else if (first_begin_of_the_frame)
   7300     {
   7301         // Skip refresh mode
   7302         window->SkipItems = true;
   7303     }
   7304 
   7305     // [DEBUG] io.ConfigDebugBeginReturnValue override return value to test Begin/End and BeginChild/EndChild behaviors.
   7306     // (The implicit fallback window is NOT automatically ended allowing it to always be able to receive commands without crashing)
   7307 #ifndef IMGUI_DISABLE_DEBUG_TOOLS
   7308     if (!window->IsFallbackWindow)
   7309         if ((g.IO.ConfigDebugBeginReturnValueOnce && window_just_created) || (g.IO.ConfigDebugBeginReturnValueLoop && g.DebugBeginReturnValueCullDepth == g.CurrentWindowStack.Size))
   7310         {
   7311             if (window->AutoFitFramesX > 0) { window->AutoFitFramesX++; }
   7312             if (window->AutoFitFramesY > 0) { window->AutoFitFramesY++; }
   7313             return false;
   7314         }
   7315 #endif
   7316 
   7317     return !window->SkipItems;
   7318 }
   7319 
   7320 static void ImGui::SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect)
   7321 {
   7322     ImGuiContext& g = *GImGui;
   7323     SetLastItemData(window->MoveId, g.CurrentItemFlags, IsMouseHoveringRect(rect.Min, rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, rect);
   7324 }
   7325 
   7326 void ImGui::End()
   7327 {
   7328     ImGuiContext& g = *GImGui;
   7329     ImGuiWindow* window = g.CurrentWindow;
   7330 
   7331     // Error checking: verify that user hasn't called End() too many times!
   7332     if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow)
   7333     {
   7334         IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!");
   7335         return;
   7336     }
   7337     ImGuiWindowStackData& window_stack_data = g.CurrentWindowStack.back();
   7338 
   7339     // Error checking: verify that user doesn't directly call End() on a child window.
   7340     if (window->Flags & ImGuiWindowFlags_ChildWindow)
   7341         IM_ASSERT_USER_ERROR(g.WithinEndChild, "Must call EndChild() and not End()!");
   7342 
   7343     // Close anything that is open
   7344     if (window->DC.CurrentColumns)
   7345         EndColumns();
   7346     if (!window->SkipRefresh)
   7347         PopClipRect();   // Inner window clip rectangle
   7348     PopFocusScope();
   7349     if (window_stack_data.DisabledOverrideReenable && window->RootWindow == window)
   7350         EndDisabledOverrideReenable();
   7351 
   7352     if (window->SkipRefresh)
   7353     {
   7354         IM_ASSERT(window->DrawList == NULL);
   7355         window->DrawList = &window->DrawListInst;
   7356     }
   7357 
   7358     // Stop logging
   7359     if (!(window->Flags & ImGuiWindowFlags_ChildWindow))    // FIXME: add more options for scope of logging
   7360         LogFinish();
   7361 
   7362     if (window->DC.IsSetPos)
   7363         ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
   7364 
   7365     // Pop from window stack
   7366     g.LastItemData = window_stack_data.ParentLastItemDataBackup;
   7367     if (window->Flags & ImGuiWindowFlags_ChildMenu)
   7368         g.BeginMenuDepth--;
   7369     if (window->Flags & ImGuiWindowFlags_Popup)
   7370         g.BeginPopupStack.pop_back();
   7371     window_stack_data.StackSizesOnBegin.CompareWithContextState(&g);
   7372     g.CurrentWindowStack.pop_back();
   7373     SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window);
   7374 }
   7375 
   7376 void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
   7377 {
   7378     ImGuiContext& g = *GImGui;
   7379     IM_ASSERT(window == window->RootWindow);
   7380 
   7381     const int cur_order = window->FocusOrder;
   7382     IM_ASSERT(g.WindowsFocusOrder[cur_order] == window);
   7383     if (g.WindowsFocusOrder.back() == window)
   7384         return;
   7385 
   7386     const int new_order = g.WindowsFocusOrder.Size - 1;
   7387     for (int n = cur_order; n < new_order; n++)
   7388     {
   7389         g.WindowsFocusOrder[n] = g.WindowsFocusOrder[n + 1];
   7390         g.WindowsFocusOrder[n]->FocusOrder--;
   7391         IM_ASSERT(g.WindowsFocusOrder[n]->FocusOrder == n);
   7392     }
   7393     g.WindowsFocusOrder[new_order] = window;
   7394     window->FocusOrder = (short)new_order;
   7395 }
   7396 
   7397 void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
   7398 {
   7399     ImGuiContext& g = *GImGui;
   7400     ImGuiWindow* current_front_window = g.Windows.back();
   7401     if (current_front_window == window || current_front_window->RootWindow == window) // Cheap early out (could be better)
   7402         return;
   7403     for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window
   7404         if (g.Windows[i] == window)
   7405         {
   7406             memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
   7407             g.Windows[g.Windows.Size - 1] = window;
   7408             break;
   7409         }
   7410 }
   7411 
   7412 void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
   7413 {
   7414     ImGuiContext& g = *GImGui;
   7415     if (g.Windows[0] == window)
   7416         return;
   7417     for (int i = 0; i < g.Windows.Size; i++)
   7418         if (g.Windows[i] == window)
   7419         {
   7420             memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
   7421             g.Windows[0] = window;
   7422             break;
   7423         }
   7424 }
   7425 
   7426 void ImGui::BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* behind_window)
   7427 {
   7428     IM_ASSERT(window != NULL && behind_window != NULL);
   7429     ImGuiContext& g = *GImGui;
   7430     window = window->RootWindow;
   7431     behind_window = behind_window->RootWindow;
   7432     int pos_wnd = FindWindowDisplayIndex(window);
   7433     int pos_beh = FindWindowDisplayIndex(behind_window);
   7434     if (pos_wnd < pos_beh)
   7435     {
   7436         size_t copy_bytes = (pos_beh - pos_wnd - 1) * sizeof(ImGuiWindow*);
   7437         memmove(&g.Windows.Data[pos_wnd], &g.Windows.Data[pos_wnd + 1], copy_bytes);
   7438         g.Windows[pos_beh - 1] = window;
   7439     }
   7440     else
   7441     {
   7442         size_t copy_bytes = (pos_wnd - pos_beh) * sizeof(ImGuiWindow*);
   7443         memmove(&g.Windows.Data[pos_beh + 1], &g.Windows.Data[pos_beh], copy_bytes);
   7444         g.Windows[pos_beh] = window;
   7445     }
   7446 }
   7447 
   7448 int ImGui::FindWindowDisplayIndex(ImGuiWindow* window)
   7449 {
   7450     ImGuiContext& g = *GImGui;
   7451     return g.Windows.index_from_ptr(g.Windows.find(window));
   7452 }
   7453 
   7454 // Moving window to front of display and set focus (which happens to be back of our sorted list)
   7455 void ImGui::FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags)
   7456 {
   7457     ImGuiContext& g = *GImGui;
   7458 
   7459     // Modal check?
   7460     if ((flags & ImGuiFocusRequestFlags_UnlessBelowModal) && (g.NavWindow != window)) // Early out in common case.
   7461         if (ImGuiWindow* blocking_modal = FindBlockingModal(window))
   7462         {
   7463             // This block would typically be reached in two situations:
   7464             // - API call to FocusWindow() with a window under a modal and ImGuiFocusRequestFlags_UnlessBelowModal flag.
   7465             // - User clicking on void or anything behind a modal while a modal is open (window == NULL)
   7466             IMGUI_DEBUG_LOG_FOCUS("[focus] FocusWindow(\"%s\", UnlessBelowModal): prevented by \"%s\".\n", window ? window->Name : "<NULL>", blocking_modal->Name);
   7467             if (window && window == window->RootWindow && (window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
   7468                 BringWindowToDisplayBehind(window, blocking_modal); // Still bring right under modal. (FIXME: Could move in focus list too?)
   7469             ClosePopupsOverWindow(GetTopMostPopupModal(), false); // Note how we need to use GetTopMostPopupModal() aad NOT blocking_modal, to handle nested modals
   7470             return;
   7471         }
   7472 
   7473     // Find last focused child (if any) and focus it instead.
   7474     if ((flags & ImGuiFocusRequestFlags_RestoreFocusedChild) && window != NULL)
   7475         window = NavRestoreLastChildNavWindow(window);
   7476 
   7477     // Apply focus
   7478     if (g.NavWindow != window)
   7479     {
   7480         SetNavWindow(window);
   7481         if (window && g.NavDisableMouseHover)
   7482             g.NavMousePosDirty = true;
   7483         g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
   7484         g.NavLayer = ImGuiNavLayer_Main;
   7485         SetNavFocusScope(window ? window->NavRootFocusScopeId : 0);
   7486         g.NavIdIsAlive = false;
   7487         g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
   7488 
   7489         // Close popups if any
   7490         ClosePopupsOverWindow(window, false);
   7491     }
   7492 
   7493     // Move the root window to the top of the pile
   7494     IM_ASSERT(window == NULL || window->RootWindow != NULL);
   7495     ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL; // NB: In docking branch this is window->RootWindowDockStop
   7496     ImGuiWindow* display_front_window = window ? window->RootWindow : NULL;
   7497 
   7498     // Steal active widgets. Some of the cases it triggers includes:
   7499     // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run.
   7500     // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId)
   7501     if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window)
   7502         if (!g.ActiveIdNoClearOnFocusLoss)
   7503             ClearActiveID();
   7504 
   7505     // Passing NULL allow to disable keyboard focus
   7506     if (!window)
   7507         return;
   7508 
   7509     // Bring to front
   7510     BringWindowToFocusFront(focus_front_window);
   7511     if (((window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
   7512         BringWindowToDisplayFront(display_front_window);
   7513 }
   7514 
   7515 void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window, ImGuiViewport* filter_viewport, ImGuiFocusRequestFlags flags)
   7516 {
   7517     ImGuiContext& g = *GImGui;
   7518     IM_UNUSED(filter_viewport); // Unused in master branch.
   7519     int start_idx = g.WindowsFocusOrder.Size - 1;
   7520     if (under_this_window != NULL)
   7521     {
   7522         // Aim at root window behind us, if we are in a child window that's our own root (see #4640)
   7523         int offset = -1;
   7524         while (under_this_window->Flags & ImGuiWindowFlags_ChildWindow)
   7525         {
   7526             under_this_window = under_this_window->ParentWindow;
   7527             offset = 0;
   7528         }
   7529         start_idx = FindWindowFocusIndex(under_this_window) + offset;
   7530     }
   7531     for (int i = start_idx; i >= 0; i--)
   7532     {
   7533         // 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.
   7534         ImGuiWindow* window = g.WindowsFocusOrder[i];
   7535         if (window == ignore_window || !window->WasActive)
   7536             continue;
   7537         if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
   7538         {
   7539             FocusWindow(window, flags);
   7540             return;
   7541         }
   7542     }
   7543     FocusWindow(NULL, flags);
   7544 }
   7545 
   7546 // Important: this alone doesn't alter current ImDrawList state. This is called by PushFont/PopFont only.
   7547 void ImGui::SetCurrentFont(ImFont* font)
   7548 {
   7549     ImGuiContext& g = *GImGui;
   7550     IM_ASSERT(font && font->IsLoaded());    // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
   7551     IM_ASSERT(font->Scale > 0.0f);
   7552     g.Font = font;
   7553     g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale);
   7554     g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
   7555     g.FontScale = g.FontSize / g.Font->FontSize;
   7556 
   7557     ImFontAtlas* atlas = g.Font->ContainerAtlas;
   7558     g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
   7559     g.DrawListSharedData.TexUvLines = atlas->TexUvLines;
   7560     g.DrawListSharedData.Font = g.Font;
   7561     g.DrawListSharedData.FontSize = g.FontSize;
   7562     g.DrawListSharedData.FontScale = g.FontScale;
   7563 }
   7564 
   7565 void ImGui::PushFont(ImFont* font)
   7566 {
   7567     ImGuiContext& g = *GImGui;
   7568     if (!font)
   7569         font = GetDefaultFont();
   7570     SetCurrentFont(font);
   7571     g.FontStack.push_back(font);
   7572     g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
   7573 }
   7574 
   7575 void  ImGui::PopFont()
   7576 {
   7577     ImGuiContext& g = *GImGui;
   7578     g.CurrentWindow->DrawList->PopTextureID();
   7579     g.FontStack.pop_back();
   7580     SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
   7581 }
   7582 
   7583 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
   7584 {
   7585     ImGuiContext& g = *GImGui;
   7586     ImGuiItemFlags item_flags = g.CurrentItemFlags;
   7587     IM_ASSERT(item_flags == g.ItemFlagsStack.back());
   7588     if (enabled)
   7589         item_flags |= option;
   7590     else
   7591         item_flags &= ~option;
   7592     g.CurrentItemFlags = item_flags;
   7593     g.ItemFlagsStack.push_back(item_flags);
   7594 }
   7595 
   7596 void ImGui::PopItemFlag()
   7597 {
   7598     ImGuiContext& g = *GImGui;
   7599     IM_ASSERT(g.ItemFlagsStack.Size > 1); // Too many calls to PopItemFlag() - we always leave a 0 at the bottom of the stack.
   7600     g.ItemFlagsStack.pop_back();
   7601     g.CurrentItemFlags = g.ItemFlagsStack.back();
   7602 }
   7603 
   7604 // BeginDisabled()/EndDisabled()
   7605 // - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled)
   7606 // - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently.
   7607 // - Feedback welcome at https://github.com/ocornut/imgui/issues/211
   7608 // - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it.
   7609 // - Optimized shortcuts instead of PushStyleVar() + PushItemFlag()
   7610 void ImGui::BeginDisabled(bool disabled)
   7611 {
   7612     ImGuiContext& g = *GImGui;
   7613     bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
   7614     if (!was_disabled && disabled)
   7615     {
   7616         g.DisabledAlphaBackup = g.Style.Alpha;
   7617         g.Style.Alpha *= g.Style.DisabledAlpha; // PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha);
   7618     }
   7619     if (was_disabled || disabled)
   7620         g.CurrentItemFlags |= ImGuiItemFlags_Disabled;
   7621     g.ItemFlagsStack.push_back(g.CurrentItemFlags); // FIXME-OPT: can we simply skip this and use DisabledStackSize?
   7622     g.DisabledStackSize++;
   7623 }
   7624 
   7625 void ImGui::EndDisabled()
   7626 {
   7627     ImGuiContext& g = *GImGui;
   7628     IM_ASSERT(g.DisabledStackSize > 0);
   7629     g.DisabledStackSize--;
   7630     bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
   7631     //PopItemFlag();
   7632     g.ItemFlagsStack.pop_back();
   7633     g.CurrentItemFlags = g.ItemFlagsStack.back();
   7634     if (was_disabled && (g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0)
   7635         g.Style.Alpha = g.DisabledAlphaBackup; //PopStyleVar();
   7636 }
   7637 
   7638 // Could have been called BeginDisabledDisable() but it didn't want to be award nominated for most awkward function name.
   7639 // Ideally we would use a shared e.g. BeginDisabled()->BeginDisabledEx() but earlier needs to be optimal.
   7640 // The whole code for this is awkward, will reevaluate if we find a way to implement SetNextItemDisabled().
   7641 void ImGui::BeginDisabledOverrideReenable()
   7642 {
   7643     ImGuiContext& g = *GImGui;
   7644     IM_ASSERT(g.CurrentItemFlags & ImGuiItemFlags_Disabled);
   7645     g.Style.Alpha = g.DisabledAlphaBackup;
   7646     g.CurrentItemFlags &= ~ImGuiItemFlags_Disabled;
   7647     g.ItemFlagsStack.push_back(g.CurrentItemFlags);
   7648     g.DisabledStackSize++;
   7649 }
   7650 
   7651 void ImGui::EndDisabledOverrideReenable()
   7652 {
   7653     ImGuiContext& g = *GImGui;
   7654     g.DisabledStackSize--;
   7655     IM_ASSERT(g.DisabledStackSize > 0);
   7656     g.ItemFlagsStack.pop_back();
   7657     g.CurrentItemFlags = g.ItemFlagsStack.back();
   7658     g.Style.Alpha = g.DisabledAlphaBackup * g.Style.DisabledAlpha;
   7659 }
   7660 
   7661 void ImGui::PushTextWrapPos(float wrap_pos_x)
   7662 {
   7663     ImGuiWindow* window = GetCurrentWindow();
   7664     window->DC.TextWrapPosStack.push_back(window->DC.TextWrapPos);
   7665     window->DC.TextWrapPos = wrap_pos_x;
   7666 }
   7667 
   7668 void ImGui::PopTextWrapPos()
   7669 {
   7670     ImGuiWindow* window = GetCurrentWindow();
   7671     window->DC.TextWrapPos = window->DC.TextWrapPosStack.back();
   7672     window->DC.TextWrapPosStack.pop_back();
   7673 }
   7674 
   7675 static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy)
   7676 {
   7677     ImGuiWindow* last_window = NULL;
   7678     while (last_window != window)
   7679     {
   7680         last_window = window;
   7681         window = window->RootWindow;
   7682         if (popup_hierarchy)
   7683             window = window->RootWindowPopupTree;
   7684     }
   7685     return window;
   7686 }
   7687 
   7688 bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy)
   7689 {
   7690     ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy);
   7691     if (window_root == potential_parent)
   7692         return true;
   7693     while (window != NULL)
   7694     {
   7695         if (window == potential_parent)
   7696             return true;
   7697         if (window == window_root) // end of chain
   7698             return false;
   7699         window = window->ParentWindow;
   7700     }
   7701     return false;
   7702 }
   7703 
   7704 bool ImGui::IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
   7705 {
   7706     if (window->RootWindow == potential_parent)
   7707         return true;
   7708     while (window != NULL)
   7709     {
   7710         if (window == potential_parent)
   7711             return true;
   7712         window = window->ParentWindowInBeginStack;
   7713     }
   7714     return false;
   7715 }
   7716 
   7717 bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below)
   7718 {
   7719     ImGuiContext& g = *GImGui;
   7720 
   7721     // It would be saner to ensure that display layer is always reflected in the g.Windows[] order, which would likely requires altering all manipulations of that array
   7722     const int display_layer_delta = GetWindowDisplayLayer(potential_above) - GetWindowDisplayLayer(potential_below);
   7723     if (display_layer_delta != 0)
   7724         return display_layer_delta > 0;
   7725 
   7726     for (int i = g.Windows.Size - 1; i >= 0; i--)
   7727     {
   7728         ImGuiWindow* candidate_window = g.Windows[i];
   7729         if (candidate_window == potential_above)
   7730             return true;
   7731         if (candidate_window == potential_below)
   7732             return false;
   7733     }
   7734     return false;
   7735 }
   7736 
   7737 // Is current window hovered and hoverable (e.g. not blocked by a popup/modal)? See ImGuiHoveredFlags_ for options.
   7738 // IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app,
   7739 // you should not use this function! Use the 'io.WantCaptureMouse' boolean for that!
   7740 // Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details.
   7741 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
   7742 {
   7743     IM_ASSERT((flags & ~ImGuiHoveredFlags_AllowedMaskForIsWindowHovered) == 0 && "Invalid flags for IsWindowHovered()!");
   7744 
   7745     ImGuiContext& g = *GImGui;
   7746     ImGuiWindow* ref_window = g.HoveredWindow;
   7747     ImGuiWindow* cur_window = g.CurrentWindow;
   7748     if (ref_window == NULL)
   7749         return false;
   7750 
   7751     if ((flags & ImGuiHoveredFlags_AnyWindow) == 0)
   7752     {
   7753         IM_ASSERT(cur_window); // Not inside a Begin()/End()
   7754         const bool popup_hierarchy = (flags & ImGuiHoveredFlags_NoPopupHierarchy) == 0;
   7755         if (flags & ImGuiHoveredFlags_RootWindow)
   7756             cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy);
   7757 
   7758         bool result;
   7759         if (flags & ImGuiHoveredFlags_ChildWindows)
   7760             result = IsWindowChildOf(ref_window, cur_window, popup_hierarchy);
   7761         else
   7762             result = (ref_window == cur_window);
   7763         if (!result)
   7764             return false;
   7765     }
   7766 
   7767     if (!IsWindowContentHoverable(ref_window, flags))
   7768         return false;
   7769     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
   7770         if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId)
   7771             return false;
   7772 
   7773     // When changing hovered window we requires a bit of stationary delay before activating hover timer.
   7774     // FIXME: We don't support delay other than stationary one for now, other delay would need a way
   7775     // to fulfill the possibility that multiple IsWindowHovered() with varying flag could return true
   7776     // for different windows of the hierarchy. Possibly need a Hash(Current+Flags) ==> (Timer) cache.
   7777     // We can implement this for _Stationary because the data is linked to HoveredWindow rather than CurrentWindow.
   7778     if (flags & ImGuiHoveredFlags_ForTooltip)
   7779         flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse);
   7780     if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverWindowUnlockedStationaryId != ref_window->ID)
   7781         return false;
   7782 
   7783     return true;
   7784 }
   7785 
   7786 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
   7787 {
   7788     ImGuiContext& g = *GImGui;
   7789     ImGuiWindow* ref_window = g.NavWindow;
   7790     ImGuiWindow* cur_window = g.CurrentWindow;
   7791 
   7792     if (ref_window == NULL)
   7793         return false;
   7794     if (flags & ImGuiFocusedFlags_AnyWindow)
   7795         return true;
   7796 
   7797     IM_ASSERT(cur_window); // Not inside a Begin()/End()
   7798     const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0;
   7799     if (flags & ImGuiHoveredFlags_RootWindow)
   7800         cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy);
   7801 
   7802     if (flags & ImGuiHoveredFlags_ChildWindows)
   7803         return IsWindowChildOf(ref_window, cur_window, popup_hierarchy);
   7804     else
   7805         return (ref_window == cur_window);
   7806 }
   7807 
   7808 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
   7809 // Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmatically.
   7810 // If you want a window to never be focused, you may use the e.g. NoInputs flag.
   7811 bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
   7812 {
   7813     return window->WasActive && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
   7814 }
   7815 
   7816 float ImGui::GetWindowWidth()
   7817 {
   7818     ImGuiWindow* window = GImGui->CurrentWindow;
   7819     return window->Size.x;
   7820 }
   7821 
   7822 float ImGui::GetWindowHeight()
   7823 {
   7824     ImGuiWindow* window = GImGui->CurrentWindow;
   7825     return window->Size.y;
   7826 }
   7827 
   7828 ImVec2 ImGui::GetWindowPos()
   7829 {
   7830     ImGuiContext& g = *GImGui;
   7831     ImGuiWindow* window = g.CurrentWindow;
   7832     return window->Pos;
   7833 }
   7834 
   7835 void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
   7836 {
   7837     // Test condition (NB: bit 0 is always true) and clear flags for next time
   7838     if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
   7839         return;
   7840 
   7841     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
   7842     window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
   7843     window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
   7844 
   7845     // Set
   7846     const ImVec2 old_pos = window->Pos;
   7847     window->Pos = ImTrunc(pos);
   7848     ImVec2 offset = window->Pos - old_pos;
   7849     if (offset.x == 0.0f && offset.y == 0.0f)
   7850         return;
   7851     MarkIniSettingsDirty(window);
   7852     window->DC.CursorPos += offset;         // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor
   7853     window->DC.CursorMaxPos += offset;      // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.
   7854     window->DC.IdealMaxPos += offset;
   7855     window->DC.CursorStartPos += offset;
   7856 }
   7857 
   7858 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
   7859 {
   7860     ImGuiWindow* window = GetCurrentWindowRead();
   7861     SetWindowPos(window, pos, cond);
   7862 }
   7863 
   7864 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
   7865 {
   7866     if (ImGuiWindow* window = FindWindowByName(name))
   7867         SetWindowPos(window, pos, cond);
   7868 }
   7869 
   7870 ImVec2 ImGui::GetWindowSize()
   7871 {
   7872     ImGuiWindow* window = GetCurrentWindowRead();
   7873     return window->Size;
   7874 }
   7875 
   7876 void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
   7877 {
   7878     // Test condition (NB: bit 0 is always true) and clear flags for next time
   7879     if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
   7880         return;
   7881 
   7882     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
   7883     window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
   7884 
   7885     // Enable auto-fit (not done in BeginChild() path unless appearing or combined with ImGuiChildFlags_AlwaysAutoResize)
   7886     if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0)
   7887         window->AutoFitFramesX = (size.x <= 0.0f) ? 2 : 0;
   7888     if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0)
   7889         window->AutoFitFramesY = (size.y <= 0.0f) ? 2 : 0;
   7890 
   7891     // Set
   7892     ImVec2 old_size = window->SizeFull;
   7893     if (size.x <= 0.0f)
   7894         window->AutoFitOnlyGrows = false;
   7895     else
   7896         window->SizeFull.x = IM_TRUNC(size.x);
   7897     if (size.y <= 0.0f)
   7898         window->AutoFitOnlyGrows = false;
   7899     else
   7900         window->SizeFull.y = IM_TRUNC(size.y);
   7901     if (old_size.x != window->SizeFull.x || old_size.y != window->SizeFull.y)
   7902         MarkIniSettingsDirty(window);
   7903 }
   7904 
   7905 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
   7906 {
   7907     SetWindowSize(GImGui->CurrentWindow, size, cond);
   7908 }
   7909 
   7910 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
   7911 {
   7912     if (ImGuiWindow* window = FindWindowByName(name))
   7913         SetWindowSize(window, size, cond);
   7914 }
   7915 
   7916 void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
   7917 {
   7918     // Test condition (NB: bit 0 is always true) and clear flags for next time
   7919     if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
   7920         return;
   7921     window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
   7922 
   7923     // Set
   7924     window->Collapsed = collapsed;
   7925 }
   7926 
   7927 void ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size)
   7928 {
   7929     IM_ASSERT(window->HitTestHoleSize.x == 0);     // We don't support multiple holes/hit test filters
   7930     window->HitTestHoleSize = ImVec2ih(size);
   7931     window->HitTestHoleOffset = ImVec2ih(pos - window->Pos);
   7932 }
   7933 
   7934 void ImGui::SetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window)
   7935 {
   7936     window->Hidden = window->SkipItems = true;
   7937     window->HiddenFramesCanSkipItems = 1;
   7938 }
   7939 
   7940 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
   7941 {
   7942     SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
   7943 }
   7944 
   7945 bool ImGui::IsWindowCollapsed()
   7946 {
   7947     ImGuiWindow* window = GetCurrentWindowRead();
   7948     return window->Collapsed;
   7949 }
   7950 
   7951 bool ImGui::IsWindowAppearing()
   7952 {
   7953     ImGuiWindow* window = GetCurrentWindowRead();
   7954     return window->Appearing;
   7955 }
   7956 
   7957 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
   7958 {
   7959     if (ImGuiWindow* window = FindWindowByName(name))
   7960         SetWindowCollapsed(window, collapsed, cond);
   7961 }
   7962 
   7963 void ImGui::SetWindowFocus()
   7964 {
   7965     FocusWindow(GImGui->CurrentWindow);
   7966 }
   7967 
   7968 void ImGui::SetWindowFocus(const char* name)
   7969 {
   7970     if (name)
   7971     {
   7972         if (ImGuiWindow* window = FindWindowByName(name))
   7973             FocusWindow(window);
   7974     }
   7975     else
   7976     {
   7977         FocusWindow(NULL);
   7978     }
   7979 }
   7980 
   7981 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
   7982 {
   7983     ImGuiContext& g = *GImGui;
   7984     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
   7985     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos;
   7986     g.NextWindowData.PosVal = pos;
   7987     g.NextWindowData.PosPivotVal = pivot;
   7988     g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
   7989 }
   7990 
   7991 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
   7992 {
   7993     ImGuiContext& g = *GImGui;
   7994     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
   7995     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize;
   7996     g.NextWindowData.SizeVal = size;
   7997     g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
   7998 }
   7999 
   8000 // For each axis:
   8001 // - Use 0.0f as min or FLT_MAX as max if you don't want limits, e.g. size_min = (500.0f, 0.0f), size_max = (FLT_MAX, FLT_MAX) sets a minimum width.
   8002 // - Use -1 for both min and max of same axis to preserve current size which itself is a constraint.
   8003 // - See "Demo->Examples->Constrained-resizing window" for examples.
   8004 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
   8005 {
   8006     ImGuiContext& g = *GImGui;
   8007     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint;
   8008     g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
   8009     g.NextWindowData.SizeCallback = custom_callback;
   8010     g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
   8011 }
   8012 
   8013 // Content size = inner scrollable rectangle, padded with WindowPadding.
   8014 // SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
   8015 void ImGui::SetNextWindowContentSize(const ImVec2& size)
   8016 {
   8017     ImGuiContext& g = *GImGui;
   8018     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize;
   8019     g.NextWindowData.ContentSizeVal = ImTrunc(size);
   8020 }
   8021 
   8022 void ImGui::SetNextWindowScroll(const ImVec2& scroll)
   8023 {
   8024     ImGuiContext& g = *GImGui;
   8025     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasScroll;
   8026     g.NextWindowData.ScrollVal = scroll;
   8027 }
   8028 
   8029 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
   8030 {
   8031     ImGuiContext& g = *GImGui;
   8032     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
   8033     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed;
   8034     g.NextWindowData.CollapsedVal = collapsed;
   8035     g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
   8036 }
   8037 
   8038 void ImGui::SetNextWindowFocus()
   8039 {
   8040     ImGuiContext& g = *GImGui;
   8041     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus;
   8042 }
   8043 
   8044 void ImGui::SetNextWindowBgAlpha(float alpha)
   8045 {
   8046     ImGuiContext& g = *GImGui;
   8047     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha;
   8048     g.NextWindowData.BgAlphaVal = alpha;
   8049 }
   8050 
   8051 // This is experimental and meant to be a toy for exploring a future/wider range of features.
   8052 void ImGui::SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags)
   8053 {
   8054     ImGuiContext& g = *GImGui;
   8055     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasRefreshPolicy;
   8056     g.NextWindowData.RefreshFlagsVal = flags;
   8057 }
   8058 
   8059 ImDrawList* ImGui::GetWindowDrawList()
   8060 {
   8061     ImGuiWindow* window = GetCurrentWindow();
   8062     return window->DrawList;
   8063 }
   8064 
   8065 ImFont* ImGui::GetFont()
   8066 {
   8067     return GImGui->Font;
   8068 }
   8069 
   8070 float ImGui::GetFontSize()
   8071 {
   8072     return GImGui->FontSize;
   8073 }
   8074 
   8075 ImVec2 ImGui::GetFontTexUvWhitePixel()
   8076 {
   8077     return GImGui->DrawListSharedData.TexUvWhitePixel;
   8078 }
   8079 
   8080 void ImGui::SetWindowFontScale(float scale)
   8081 {
   8082     IM_ASSERT(scale > 0.0f);
   8083     ImGuiContext& g = *GImGui;
   8084     ImGuiWindow* window = GetCurrentWindow();
   8085     window->FontWindowScale = scale;
   8086     g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
   8087     g.FontScale = g.DrawListSharedData.FontScale = g.FontSize / g.Font->FontSize;
   8088 }
   8089 
   8090 void ImGui::PushFocusScope(ImGuiID id)
   8091 {
   8092     ImGuiContext& g = *GImGui;
   8093     ImGuiFocusScopeData data;
   8094     data.ID = id;
   8095     data.WindowID = g.CurrentWindow->ID;
   8096     g.FocusScopeStack.push_back(data);
   8097     g.CurrentFocusScopeId = id;
   8098 }
   8099 
   8100 void ImGui::PopFocusScope()
   8101 {
   8102     ImGuiContext& g = *GImGui;
   8103     if (g.FocusScopeStack.Size == 0)
   8104     {
   8105         IM_ASSERT_USER_ERROR(g.FocusScopeStack.Size > 0, "Calling PopFocusScope() too many times!");
   8106         return;
   8107     }
   8108     g.FocusScopeStack.pop_back();
   8109     g.CurrentFocusScopeId = g.FocusScopeStack.Size ? g.FocusScopeStack.back().ID : 0;
   8110 }
   8111 
   8112 void ImGui::SetNavFocusScope(ImGuiID focus_scope_id)
   8113 {
   8114     ImGuiContext& g = *GImGui;
   8115     g.NavFocusScopeId = focus_scope_id;
   8116     g.NavFocusRoute.resize(0); // Invalidate
   8117     if (focus_scope_id == 0)
   8118         return;
   8119     IM_ASSERT(g.NavWindow != NULL);
   8120 
   8121     // Store current path (in reverse order)
   8122     if (focus_scope_id == g.CurrentFocusScopeId)
   8123     {
   8124         // Top of focus stack contains local focus scopes inside current window
   8125         for (int n = g.FocusScopeStack.Size - 1; n >= 0 && g.FocusScopeStack.Data[n].WindowID == g.CurrentWindow->ID; n--)
   8126             g.NavFocusRoute.push_back(g.FocusScopeStack.Data[n]);
   8127     }
   8128     else if (focus_scope_id == g.NavWindow->NavRootFocusScopeId)
   8129         g.NavFocusRoute.push_back({ focus_scope_id, g.NavWindow->ID });
   8130     else
   8131         return;
   8132 
   8133     // Then follow on manually set ParentWindowForFocusRoute field (#6798)
   8134     for (ImGuiWindow* window = g.NavWindow->ParentWindowForFocusRoute; window != NULL; window = window->ParentWindowForFocusRoute)
   8135         g.NavFocusRoute.push_back({ window->NavRootFocusScopeId, window->ID });
   8136     IM_ASSERT(g.NavFocusRoute.Size < 100); // Maximum depth is technically 251 as per CalcRoutingScore(): 254 - 3
   8137 }
   8138 
   8139 // Focus = move navigation cursor, set scrolling, set focus window.
   8140 void ImGui::FocusItem()
   8141 {
   8142     ImGuiContext& g = *GImGui;
   8143     ImGuiWindow* window = g.CurrentWindow;
   8144     IMGUI_DEBUG_LOG_FOCUS("FocusItem(0x%08x) in window \"%s\"\n", g.LastItemData.ID, window->Name);
   8145     if (g.DragDropActive || g.MovingWindow != NULL) // FIXME: Opt-in flags for this?
   8146     {
   8147         IMGUI_DEBUG_LOG_FOCUS("FocusItem() ignored while DragDropActive!\n");
   8148         return;
   8149     }
   8150 
   8151     ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavHighlight | ImGuiNavMoveFlags_NoSelect;
   8152     ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
   8153     SetNavWindow(window);
   8154     NavMoveRequestSubmit(ImGuiDir_None, ImGuiDir_Up, move_flags, scroll_flags);
   8155     NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal);
   8156 }
   8157 
   8158 void ImGui::ActivateItemByID(ImGuiID id)
   8159 {
   8160     ImGuiContext& g = *GImGui;
   8161     g.NavNextActivateId = id;
   8162     g.NavNextActivateFlags = ImGuiActivateFlags_None;
   8163 }
   8164 
   8165 // Note: this will likely be called ActivateItem() once we rework our Focus/Activation system!
   8166 // But ActivateItem() should function without altering scroll/focus?
   8167 void ImGui::SetKeyboardFocusHere(int offset)
   8168 {
   8169     ImGuiContext& g = *GImGui;
   8170     ImGuiWindow* window = g.CurrentWindow;
   8171     IM_ASSERT(offset >= -1);    // -1 is allowed but not below
   8172     IMGUI_DEBUG_LOG_FOCUS("SetKeyboardFocusHere(%d) in window \"%s\"\n", offset, window->Name);
   8173 
   8174     // It makes sense in the vast majority of cases to never interrupt a drag and drop.
   8175     // When we refactor this function into ActivateItem() we may want to make this an option.
   8176     // MovingWindow is protected from most user inputs using SetActiveIdUsingNavAndKeys(), but
   8177     // is also automatically dropped in the event g.ActiveId is stolen.
   8178     if (g.DragDropActive || g.MovingWindow != NULL)
   8179     {
   8180         IMGUI_DEBUG_LOG_FOCUS("SetKeyboardFocusHere() ignored while DragDropActive!\n");
   8181         return;
   8182     }
   8183 
   8184     SetNavWindow(window);
   8185 
   8186     ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavHighlight;
   8187     ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
   8188     NavMoveRequestSubmit(ImGuiDir_None, offset < 0 ? ImGuiDir_Up : ImGuiDir_Down, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.
   8189     if (offset == -1)
   8190     {
   8191         NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal);
   8192     }
   8193     else
   8194     {
   8195         g.NavTabbingDir = 1;
   8196         g.NavTabbingCounter = offset + 1;
   8197     }
   8198 }
   8199 
   8200 void ImGui::SetItemDefaultFocus()
   8201 {
   8202     ImGuiContext& g = *GImGui;
   8203     ImGuiWindow* window = g.CurrentWindow;
   8204     if (!window->Appearing)
   8205         return;
   8206     if (g.NavWindow != window->RootWindowForNav || (!g.NavInitRequest && g.NavInitResult.ID == 0) || g.NavLayer != window->DC.NavLayerCurrent)
   8207         return;
   8208 
   8209     g.NavInitRequest = false;
   8210     NavApplyItemToResult(&g.NavInitResult);
   8211     NavUpdateAnyRequestFlag();
   8212 
   8213     // Scroll could be done in NavInitRequestApplyResult() via an opt-in flag (we however don't want regular init requests to scroll)
   8214     if (!window->ClipRect.Contains(g.LastItemData.Rect))
   8215         ScrollToRectEx(window, g.LastItemData.Rect, ImGuiScrollFlags_None);
   8216 }
   8217 
   8218 void ImGui::SetStateStorage(ImGuiStorage* tree)
   8219 {
   8220     ImGuiWindow* window = GImGui->CurrentWindow;
   8221     window->DC.StateStorage = tree ? tree : &window->StateStorage;
   8222 }
   8223 
   8224 ImGuiStorage* ImGui::GetStateStorage()
   8225 {
   8226     ImGuiWindow* window = GImGui->CurrentWindow;
   8227     return window->DC.StateStorage;
   8228 }
   8229 
   8230 bool ImGui::IsRectVisible(const ImVec2& size)
   8231 {
   8232     ImGuiWindow* window = GImGui->CurrentWindow;
   8233     return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
   8234 }
   8235 
   8236 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
   8237 {
   8238     ImGuiWindow* window = GImGui->CurrentWindow;
   8239     return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
   8240 }
   8241 
   8242 //-----------------------------------------------------------------------------
   8243 // [SECTION] ID STACK
   8244 //-----------------------------------------------------------------------------
   8245 
   8246 // This is one of the very rare legacy case where we use ImGuiWindow methods,
   8247 // it should ideally be flattened at some point but it's been used a lots by widgets.
   8248 IM_MSVC_RUNTIME_CHECKS_OFF
   8249 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
   8250 {
   8251     ImGuiID seed = IDStack.back();
   8252     ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
   8253 #ifndef IMGUI_DISABLE_DEBUG_TOOLS
   8254     ImGuiContext& g = *Ctx;
   8255     if (g.DebugHookIdInfo == id)
   8256         ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
   8257 #endif
   8258     return id;
   8259 }
   8260 
   8261 ImGuiID ImGuiWindow::GetID(const void* ptr)
   8262 {
   8263     ImGuiID seed = IDStack.back();
   8264     ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
   8265 #ifndef IMGUI_DISABLE_DEBUG_TOOLS
   8266     ImGuiContext& g = *Ctx;
   8267     if (g.DebugHookIdInfo == id)
   8268         ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL);
   8269 #endif
   8270     return id;
   8271 }
   8272 
   8273 ImGuiID ImGuiWindow::GetID(int n)
   8274 {
   8275     ImGuiID seed = IDStack.back();
   8276     ImGuiID id = ImHashData(&n, sizeof(n), seed);
   8277 #ifndef IMGUI_DISABLE_DEBUG_TOOLS
   8278     ImGuiContext& g = *Ctx;
   8279     if (g.DebugHookIdInfo == id)
   8280         ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
   8281 #endif
   8282     return id;
   8283 }
   8284 
   8285 // This is only used in rare/specific situations to manufacture an ID out of nowhere.
   8286 ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
   8287 {
   8288     ImGuiID seed = IDStack.back();
   8289     ImRect r_rel = ImGui::WindowRectAbsToRel(this, r_abs);
   8290     ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
   8291     return id;
   8292 }
   8293 
   8294 void ImGui::PushID(const char* str_id)
   8295 {
   8296     ImGuiContext& g = *GImGui;
   8297     ImGuiWindow* window = g.CurrentWindow;
   8298     ImGuiID id = window->GetID(str_id);
   8299     window->IDStack.push_back(id);
   8300 }
   8301 
   8302 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
   8303 {
   8304     ImGuiContext& g = *GImGui;
   8305     ImGuiWindow* window = g.CurrentWindow;
   8306     ImGuiID id = window->GetID(str_id_begin, str_id_end);
   8307     window->IDStack.push_back(id);
   8308 }
   8309 
   8310 void ImGui::PushID(const void* ptr_id)
   8311 {
   8312     ImGuiContext& g = *GImGui;
   8313     ImGuiWindow* window = g.CurrentWindow;
   8314     ImGuiID id = window->GetID(ptr_id);
   8315     window->IDStack.push_back(id);
   8316 }
   8317 
   8318 void ImGui::PushID(int int_id)
   8319 {
   8320     ImGuiContext& g = *GImGui;
   8321     ImGuiWindow* window = g.CurrentWindow;
   8322     ImGuiID id = window->GetID(int_id);
   8323     window->IDStack.push_back(id);
   8324 }
   8325 
   8326 // Push a given id value ignoring the ID stack as a seed.
   8327 void ImGui::PushOverrideID(ImGuiID id)
   8328 {
   8329     ImGuiContext& g = *GImGui;
   8330     ImGuiWindow* window = g.CurrentWindow;
   8331 #ifndef IMGUI_DISABLE_DEBUG_TOOLS
   8332     if (g.DebugHookIdInfo == id)
   8333         DebugHookIdInfo(id, ImGuiDataType_ID, NULL, NULL);
   8334 #endif
   8335     window->IDStack.push_back(id);
   8336 }
   8337 
   8338 // Helper to avoid a common series of PushOverrideID -> GetID() -> PopID() call
   8339 // (note that when using this pattern, ID Stack Tool will tend to not display the intermediate stack level.
   8340 //  for that to work we would need to do PushOverrideID() -> ItemAdd() -> PopID() which would alter widget code a little more)
   8341 ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed)
   8342 {
   8343     ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
   8344 #ifndef IMGUI_DISABLE_DEBUG_TOOLS
   8345     ImGuiContext& g = *GImGui;
   8346     if (g.DebugHookIdInfo == id)
   8347         DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
   8348 #endif
   8349     return id;
   8350 }
   8351 
   8352 ImGuiID ImGui::GetIDWithSeed(int n, ImGuiID seed)
   8353 {
   8354     ImGuiID id = ImHashData(&n, sizeof(n), seed);
   8355 #ifndef IMGUI_DISABLE_DEBUG_TOOLS
   8356     ImGuiContext& g = *GImGui;
   8357     if (g.DebugHookIdInfo == id)
   8358         DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
   8359 #endif
   8360     return id;
   8361 }
   8362 
   8363 void ImGui::PopID()
   8364 {
   8365     ImGuiWindow* window = GImGui->CurrentWindow;
   8366     IM_ASSERT(window->IDStack.Size > 1); // Too many PopID(), or could be popping in a wrong/different window?
   8367     window->IDStack.pop_back();
   8368 }
   8369 
   8370 ImGuiID ImGui::GetID(const char* str_id)
   8371 {
   8372     ImGuiWindow* window = GImGui->CurrentWindow;
   8373     return window->GetID(str_id);
   8374 }
   8375 
   8376 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
   8377 {
   8378     ImGuiWindow* window = GImGui->CurrentWindow;
   8379     return window->GetID(str_id_begin, str_id_end);
   8380 }
   8381 
   8382 ImGuiID ImGui::GetID(const void* ptr_id)
   8383 {
   8384     ImGuiWindow* window = GImGui->CurrentWindow;
   8385     return window->GetID(ptr_id);
   8386 }
   8387 
   8388 ImGuiID ImGui::GetID(int int_id)
   8389 {
   8390     ImGuiWindow* window = GImGui->CurrentWindow;
   8391     return window->GetID(int_id);
   8392 }
   8393 IM_MSVC_RUNTIME_CHECKS_RESTORE
   8394 
   8395 //-----------------------------------------------------------------------------
   8396 // [SECTION] INPUTS
   8397 //-----------------------------------------------------------------------------
   8398 // - GetModForModKey() [Internal]
   8399 // - FixupKeyChord() [Internal]
   8400 // - GetKeyData() [Internal]
   8401 // - GetKeyIndex() [Internal]
   8402 // - GetKeyName()
   8403 // - GetKeyChordName() [Internal]
   8404 // - CalcTypematicRepeatAmount() [Internal]
   8405 // - GetTypematicRepeatRate() [Internal]
   8406 // - GetKeyPressedAmount() [Internal]
   8407 // - GetKeyMagnitude2d() [Internal]
   8408 //-----------------------------------------------------------------------------
   8409 // - UpdateKeyRoutingTable() [Internal]
   8410 // - GetRoutingIdFromOwnerId() [Internal]
   8411 // - GetShortcutRoutingData() [Internal]
   8412 // - CalcRoutingScore() [Internal]
   8413 // - SetShortcutRouting() [Internal]
   8414 // - TestShortcutRouting() [Internal]
   8415 //-----------------------------------------------------------------------------
   8416 // - IsKeyDown()
   8417 // - IsKeyPressed()
   8418 // - IsKeyReleased()
   8419 //-----------------------------------------------------------------------------
   8420 // - IsMouseDown()
   8421 // - IsMouseClicked()
   8422 // - IsMouseReleased()
   8423 // - IsMouseDoubleClicked()
   8424 // - GetMouseClickedCount()
   8425 // - IsMouseHoveringRect() [Internal]
   8426 // - IsMouseDragPastThreshold() [Internal]
   8427 // - IsMouseDragging()
   8428 // - GetMousePos()
   8429 // - SetMousePos() [Internal]
   8430 // - GetMousePosOnOpeningCurrentPopup()
   8431 // - IsMousePosValid()
   8432 // - IsAnyMouseDown()
   8433 // - GetMouseDragDelta()
   8434 // - ResetMouseDragDelta()
   8435 // - GetMouseCursor()
   8436 // - SetMouseCursor()
   8437 //-----------------------------------------------------------------------------
   8438 // - UpdateAliasKey()
   8439 // - GetMergedModsFromKeys()
   8440 // - UpdateKeyboardInputs()
   8441 // - UpdateMouseInputs()
   8442 //-----------------------------------------------------------------------------
   8443 // - LockWheelingWindow [Internal]
   8444 // - FindBestWheelingWindow [Internal]
   8445 // - UpdateMouseWheel() [Internal]
   8446 //-----------------------------------------------------------------------------
   8447 // - SetNextFrameWantCaptureKeyboard()
   8448 // - SetNextFrameWantCaptureMouse()
   8449 //-----------------------------------------------------------------------------
   8450 // - GetInputSourceName() [Internal]
   8451 // - DebugPrintInputEvent() [Internal]
   8452 // - UpdateInputEvents() [Internal]
   8453 //-----------------------------------------------------------------------------
   8454 // - GetKeyOwner() [Internal]
   8455 // - TestKeyOwner() [Internal]
   8456 // - SetKeyOwner() [Internal]
   8457 // - SetItemKeyOwner() [Internal]
   8458 // - Shortcut() [Internal]
   8459 //-----------------------------------------------------------------------------
   8460 
   8461 static ImGuiKeyChord GetModForModKey(ImGuiKey key)
   8462 {
   8463     if (key == ImGuiKey_LeftCtrl || key == ImGuiKey_RightCtrl)
   8464         return ImGuiMod_Ctrl;
   8465     if (key == ImGuiKey_LeftShift || key == ImGuiKey_RightShift)
   8466         return ImGuiMod_Shift;
   8467     if (key == ImGuiKey_LeftAlt || key == ImGuiKey_RightAlt)
   8468         return ImGuiMod_Alt;
   8469     if (key == ImGuiKey_LeftSuper || key == ImGuiKey_RightSuper)
   8470         return ImGuiMod_Super;
   8471     return ImGuiMod_None;
   8472 }
   8473 
   8474 ImGuiKeyChord ImGui::FixupKeyChord(ImGuiKeyChord key_chord)
   8475 {
   8476     // Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified.
   8477     ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
   8478     if (IsModKey(key))
   8479         key_chord |= GetModForModKey(key);
   8480     return key_chord;
   8481 }
   8482 
   8483 ImGuiKeyData* ImGui::GetKeyData(ImGuiContext* ctx, ImGuiKey key)
   8484 {
   8485     ImGuiContext& g = *ctx;
   8486 
   8487     // Special storage location for mods
   8488     if (key & ImGuiMod_Mask_)
   8489         key = ConvertSingleModFlagToKey(key);
   8490 
   8491 #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
   8492     IM_ASSERT(key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_NamedKey_END);
   8493     if (IsLegacyKey(key) && g.IO.KeyMap[key] != -1)
   8494         key = (ImGuiKey)g.IO.KeyMap[key];  // Remap native->imgui or imgui->native
   8495 #else
   8496     IM_ASSERT(IsNamedKey(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code.");
   8497 #endif
   8498     return &g.IO.KeysData[key - ImGuiKey_KeysData_OFFSET];
   8499 }
   8500 
   8501 #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
   8502 // Formally moved to obsolete section in 1.90.5 in spite of documented as obsolete since 1.87
   8503 ImGuiKey ImGui::GetKeyIndex(ImGuiKey key)
   8504 {
   8505     ImGuiContext& g = *GImGui;
   8506     IM_ASSERT(IsNamedKey(key));
   8507     const ImGuiKeyData* key_data = GetKeyData(key);
   8508     return (ImGuiKey)(key_data - g.IO.KeysData);
   8509 }
   8510 #endif
   8511 
   8512 // Those names a provided for debugging purpose and are not meant to be saved persistently not compared.
   8513 static const char* const GKeyNames[] =
   8514 {
   8515     "Tab", "LeftArrow", "RightArrow", "UpArrow", "DownArrow", "PageUp", "PageDown",
   8516     "Home", "End", "Insert", "Delete", "Backspace", "Space", "Enter", "Escape",
   8517     "LeftCtrl", "LeftShift", "LeftAlt", "LeftSuper", "RightCtrl", "RightShift", "RightAlt", "RightSuper", "Menu",
   8518     "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H",
   8519     "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
   8520     "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
   8521     "F13", "F14", "F15", "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23", "F24",
   8522     "Apostrophe", "Comma", "Minus", "Period", "Slash", "Semicolon", "Equal", "LeftBracket",
   8523     "Backslash", "RightBracket", "GraveAccent", "CapsLock", "ScrollLock", "NumLock", "PrintScreen",
   8524     "Pause", "Keypad0", "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6",
   8525     "Keypad7", "Keypad8", "Keypad9", "KeypadDecimal", "KeypadDivide", "KeypadMultiply",
   8526     "KeypadSubtract", "KeypadAdd", "KeypadEnter", "KeypadEqual",
   8527     "AppBack", "AppForward",
   8528     "GamepadStart", "GamepadBack",
   8529     "GamepadFaceLeft", "GamepadFaceRight", "GamepadFaceUp", "GamepadFaceDown",
   8530     "GamepadDpadLeft", "GamepadDpadRight", "GamepadDpadUp", "GamepadDpadDown",
   8531     "GamepadL1", "GamepadR1", "GamepadL2", "GamepadR2", "GamepadL3", "GamepadR3",
   8532     "GamepadLStickLeft", "GamepadLStickRight", "GamepadLStickUp", "GamepadLStickDown",
   8533     "GamepadRStickLeft", "GamepadRStickRight", "GamepadRStickUp", "GamepadRStickDown",
   8534     "MouseLeft", "MouseRight", "MouseMiddle", "MouseX1", "MouseX2", "MouseWheelX", "MouseWheelY",
   8535     "ModCtrl", "ModShift", "ModAlt", "ModSuper", // ReservedForModXXX are showing the ModXXX names.
   8536 };
   8537 IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_ARRAYSIZE(GKeyNames));
   8538 
   8539 const char* ImGui::GetKeyName(ImGuiKey key)
   8540 {
   8541     if (key == ImGuiKey_None)
   8542         return "None";
   8543 #ifdef IMGUI_DISABLE_OBSOLETE_KEYIO
   8544     IM_ASSERT(IsNamedKeyOrMod(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code.");
   8545 #else
   8546     ImGuiContext& g = *GImGui;
   8547     if (IsLegacyKey(key))
   8548     {
   8549         if (g.IO.KeyMap[key] == -1)
   8550             return "N/A";
   8551         IM_ASSERT(IsNamedKey((ImGuiKey)g.IO.KeyMap[key]));
   8552         key = (ImGuiKey)g.IO.KeyMap[key];
   8553     }
   8554 #endif
   8555     if (key & ImGuiMod_Mask_)
   8556         key = ConvertSingleModFlagToKey(key);
   8557     if (!IsNamedKey(key))
   8558         return "Unknown";
   8559 
   8560     return GKeyNames[key - ImGuiKey_NamedKey_BEGIN];
   8561 }
   8562 
   8563 // Return untranslated names: on macOS, Cmd key will show as Ctrl, Ctrl key will show as super.
   8564 // Lifetime of return value: valid until next call to same function.
   8565 const char* ImGui::GetKeyChordName(ImGuiKeyChord key_chord)
   8566 {
   8567     ImGuiContext& g = *GImGui;
   8568 
   8569     const ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
   8570     if (IsModKey(key))
   8571         key_chord &= ~GetModForModKey(key); // Return "Ctrl+LeftShift" instead of "Ctrl+Shift+LeftShift"
   8572     ImFormatString(g.TempKeychordName, IM_ARRAYSIZE(g.TempKeychordName), "%s%s%s%s%s",
   8573         (key_chord & ImGuiMod_Ctrl) ? "Ctrl+" : "",
   8574         (key_chord & ImGuiMod_Shift) ? "Shift+" : "",
   8575         (key_chord & ImGuiMod_Alt) ? "Alt+" : "",
   8576         (key_chord & ImGuiMod_Super) ? "Super+" : "",
   8577         (key != ImGuiKey_None || key_chord == ImGuiKey_None) ? GetKeyName(key) : "");
   8578     size_t len;
   8579     if (key == ImGuiKey_None && key_chord != 0)
   8580         if ((len = strlen(g.TempKeychordName)) != 0) // Remove trailing '+'
   8581             g.TempKeychordName[len - 1] = 0;
   8582     return g.TempKeychordName;
   8583 }
   8584 
   8585 // t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
   8586 // t1 = current time (e.g.: g.Time)
   8587 // An event is triggered at:
   8588 //  t = 0.0f     t = repeat_delay,    t = repeat_delay + repeat_rate*N
   8589 int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)
   8590 {
   8591     if (t1 == 0.0f)
   8592         return 1;
   8593     if (t0 >= t1)
   8594         return 0;
   8595     if (repeat_rate <= 0.0f)
   8596         return (t0 < repeat_delay) && (t1 >= repeat_delay);
   8597     const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);
   8598     const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);
   8599     const int count = count_t1 - count_t0;
   8600     return count;
   8601 }
   8602 
   8603 void ImGui::GetTypematicRepeatRate(ImGuiInputFlags flags, float* repeat_delay, float* repeat_rate)
   8604 {
   8605     ImGuiContext& g = *GImGui;
   8606     switch (flags & ImGuiInputFlags_RepeatRateMask_)
   8607     {
   8608     case ImGuiInputFlags_RepeatRateNavMove:             *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.80f; return;
   8609     case ImGuiInputFlags_RepeatRateNavTweak:            *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.30f; return;
   8610     case ImGuiInputFlags_RepeatRateDefault: default:    *repeat_delay = g.IO.KeyRepeatDelay * 1.00f; *repeat_rate = g.IO.KeyRepeatRate * 1.00f; return;
   8611     }
   8612 }
   8613 
   8614 // Return value representing the number of presses in the last time period, for the given repeat rate
   8615 // (most often returns 0 or 1. The result is generally only >1 when RepeatRate is smaller than DeltaTime, aka large DeltaTime or fast RepeatRate)
   8616 int ImGui::GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float repeat_rate)
   8617 {
   8618     ImGuiContext& g = *GImGui;
   8619     const ImGuiKeyData* key_data = GetKeyData(key);
   8620     if (!key_data->Down) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership)
   8621         return 0;
   8622     const float t = key_data->DownDuration;
   8623     return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
   8624 }
   8625 
   8626 // Return 2D vector representing the combination of four cardinal direction, with analog value support (for e.g. ImGuiKey_GamepadLStick* values).
   8627 ImVec2 ImGui::GetKeyMagnitude2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down)
   8628 {
   8629     return ImVec2(
   8630         GetKeyData(key_right)->AnalogValue - GetKeyData(key_left)->AnalogValue,
   8631         GetKeyData(key_down)->AnalogValue - GetKeyData(key_up)->AnalogValue);
   8632 }
   8633 
   8634 // Rewrite routing data buffers to strip old entries + sort by key to make queries not touch scattered data.
   8635 //   Entries   D,A,B,B,A,C,B     --> A,A,B,B,B,C,D
   8636 //   Index     A:1 B:2 C:5 D:0   --> A:0 B:2 C:5 D:6
   8637 // See 'Metrics->Key Owners & Shortcut Routing' to visualize the result of that operation.
   8638 static void ImGui::UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt)
   8639 {
   8640     ImGuiContext& g = *GImGui;
   8641     rt->EntriesNext.resize(0);
   8642     for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
   8643     {
   8644         const int new_routing_start_idx = rt->EntriesNext.Size;
   8645         ImGuiKeyRoutingData* routing_entry;
   8646         for (int old_routing_idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; old_routing_idx != -1; old_routing_idx = routing_entry->NextEntryIndex)
   8647         {
   8648             routing_entry = &rt->Entries[old_routing_idx];
   8649             routing_entry->RoutingCurrScore = routing_entry->RoutingNextScore;
   8650             routing_entry->RoutingCurr = routing_entry->RoutingNext; // Update entry
   8651             routing_entry->RoutingNext = ImGuiKeyOwner_NoOwner;
   8652             routing_entry->RoutingNextScore = 255;
   8653             if (routing_entry->RoutingCurr == ImGuiKeyOwner_NoOwner)
   8654                 continue;
   8655             rt->EntriesNext.push_back(*routing_entry); // Write alive ones into new buffer
   8656 
   8657             // Apply routing to owner if there's no owner already (RoutingCurr == None at this point)
   8658             // This is the result of previous frame's SetShortcutRouting() call.
   8659             if (routing_entry->Mods == g.IO.KeyMods)
   8660             {
   8661                 ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
   8662                 if (owner_data->OwnerCurr == ImGuiKeyOwner_NoOwner)
   8663                 {
   8664                     owner_data->OwnerCurr = routing_entry->RoutingCurr;
   8665                     //IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X) via Routing\n", GetKeyName(key), routing_entry->RoutingCurr);
   8666                 }
   8667             }
   8668         }
   8669 
   8670         // Rewrite linked-list
   8671         rt->Index[key - ImGuiKey_NamedKey_BEGIN] = (ImGuiKeyRoutingIndex)(new_routing_start_idx < rt->EntriesNext.Size ? new_routing_start_idx : -1);
   8672         for (int n = new_routing_start_idx; n < rt->EntriesNext.Size; n++)
   8673             rt->EntriesNext[n].NextEntryIndex = (ImGuiKeyRoutingIndex)((n + 1 < rt->EntriesNext.Size) ? n + 1 : -1);
   8674     }
   8675     rt->Entries.swap(rt->EntriesNext); // Swap new and old indexes
   8676 }
   8677 
   8678 // owner_id may be None/Any, but routing_id needs to be always be set, so we default to GetCurrentFocusScope().
   8679 static inline ImGuiID GetRoutingIdFromOwnerId(ImGuiID owner_id)
   8680 {
   8681     ImGuiContext& g = *GImGui;
   8682     return (owner_id != ImGuiKeyOwner_NoOwner && owner_id != ImGuiKeyOwner_Any) ? owner_id : g.CurrentFocusScopeId;
   8683 }
   8684 
   8685 ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord)
   8686 {
   8687     // Majority of shortcuts will be Key + any number of Mods
   8688     // We accept _Single_ mod with ImGuiKey_None.
   8689     //  - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl);                    // Legal
   8690     //  - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl | ImGuiMod_Shift);   // Legal
   8691     //  - Shortcut(ImGuiMod_Ctrl);                                 // Legal
   8692     //  - Shortcut(ImGuiMod_Ctrl | ImGuiMod_Shift);                // Not legal
   8693     ImGuiContext& g = *GImGui;
   8694     ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable;
   8695     ImGuiKeyRoutingData* routing_data;
   8696     ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
   8697     ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
   8698     if (key == ImGuiKey_None)
   8699         key = ConvertSingleModFlagToKey(mods);
   8700     IM_ASSERT(IsNamedKey(key));
   8701 
   8702     // Get (in the majority of case, the linked list will have one element so this should be 2 reads.
   8703     // Subsequent elements will be contiguous in memory as list is sorted/rebuilt in NewFrame).
   8704     for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; idx = routing_data->NextEntryIndex)
   8705     {
   8706         routing_data = &rt->Entries[idx];
   8707         if (routing_data->Mods == mods)
   8708             return routing_data;
   8709     }
   8710 
   8711     // Add to linked-list
   8712     ImGuiKeyRoutingIndex routing_data_idx = (ImGuiKeyRoutingIndex)rt->Entries.Size;
   8713     rt->Entries.push_back(ImGuiKeyRoutingData());
   8714     routing_data = &rt->Entries[routing_data_idx];
   8715     routing_data->Mods = (ImU16)mods;
   8716     routing_data->NextEntryIndex = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; // Setup linked list
   8717     rt->Index[key - ImGuiKey_NamedKey_BEGIN] = routing_data_idx;
   8718     return routing_data;
   8719 }
   8720 
   8721 // Current score encoding (lower is highest priority):
   8722 //  -   0: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverActive
   8723 //  -   1: ImGuiInputFlags_ActiveItem or ImGuiInputFlags_RouteFocused (if item active)
   8724 //  -   2: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused
   8725 //  -  3+: ImGuiInputFlags_RouteFocused (if window in focus-stack)
   8726 //  - 254: ImGuiInputFlags_RouteGlobal
   8727 //  - 255: never route
   8728 // 'flags' should include an explicit routing policy
   8729 static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInputFlags flags)
   8730 {
   8731     ImGuiContext& g = *GImGui;
   8732     if (flags & ImGuiInputFlags_RouteFocused)
   8733     {
   8734         // ActiveID gets top priority
   8735         // (we don't check g.ActiveIdUsingAllKeys here. Routing is applied but if input ownership is tested later it may discard it)
   8736         if (owner_id != 0 && g.ActiveId == owner_id)
   8737             return 1;
   8738 
   8739         // Score based on distance to focused window (lower is better)
   8740         // Assuming both windows are submitting a routing request,
   8741         // - When Window....... is focused -> Window scores 3 (best), Window/ChildB scores 255 (no match)
   8742         // - When Window/ChildB is focused -> Window scores 4,        Window/ChildB scores 3 (best)
   8743         // Assuming only WindowA is submitting a routing request,
   8744         // - When Window/ChildB is focused -> Window scores 4 (best), Window/ChildB doesn't have a score.
   8745         // This essentially follow the window->ParentWindowForFocusRoute chain.
   8746         if (focus_scope_id == 0)
   8747             return 255;
   8748         for (int index_in_focus_path = 0; index_in_focus_path < g.NavFocusRoute.Size; index_in_focus_path++)
   8749             if (g.NavFocusRoute.Data[index_in_focus_path].ID == focus_scope_id)
   8750                 return 3 + index_in_focus_path;
   8751         return 255;
   8752     }
   8753     else if (flags & ImGuiInputFlags_RouteActive)
   8754     {
   8755         if (owner_id != 0 && g.ActiveId == owner_id)
   8756             return 1;
   8757         return 255;
   8758     }
   8759     else if (flags & ImGuiInputFlags_RouteGlobal)
   8760     {
   8761         if (flags & ImGuiInputFlags_RouteOverActive)
   8762             return 0;
   8763         if (flags & ImGuiInputFlags_RouteOverFocused)
   8764             return 2;
   8765         return 254;
   8766     }
   8767     IM_ASSERT(0);
   8768     return 0;
   8769 }
   8770 
   8771 // We need this to filter some Shortcut() routes when an item e.g. an InputText() is active
   8772 // e.g. ImGuiKey_G won't be considered a shortcut when item is active, but ImGuiMod|ImGuiKey_G can be.
   8773 static bool IsKeyChordPotentiallyCharInput(ImGuiKeyChord key_chord)
   8774 {
   8775     // Mimic 'ignore_char_inputs' logic in InputText()
   8776     ImGuiContext& g = *GImGui;
   8777 
   8778     // When the right mods are pressed it cannot be a char input so we won't filter the shortcut out.
   8779     ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
   8780     const bool ignore_char_inputs = ((mods & ImGuiMod_Ctrl) && !(mods & ImGuiMod_Alt)) || (g.IO.ConfigMacOSXBehaviors && (mods & ImGuiMod_Ctrl));
   8781     if (ignore_char_inputs)
   8782         return false;
   8783 
   8784     // Return true for A-Z, 0-9 and other keys associated to char inputs. Other keys such as F1-F12 won't be filtered.
   8785     ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
   8786     return g.KeysMayBeCharInput.TestBit(key);
   8787 }
   8788 
   8789 // Request a desired route for an input chord (key + mods).
   8790 // Return true if the route is available this frame.
   8791 // - Routes and key ownership are attributed at the beginning of next frame based on best score and mod state.
   8792 //   (Conceptually this does a "Submit for next frame" + "Test for current frame".
   8793 //   As such, it could be called TrySetXXX or SubmitXXX, or the Submit and Test operations should be separate.)
   8794 bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
   8795 {
   8796     ImGuiContext& g = *GImGui;
   8797     if ((flags & ImGuiInputFlags_RouteTypeMask_) == 0)
   8798         flags |= ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive; // IMPORTANT: This is the default for SetShortcutRouting() but NOT Shortcut()
   8799     else
   8800         IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteTypeMask_)); // Check that only 1 routing flag is used
   8801     IM_ASSERT(owner_id != ImGuiKeyOwner_Any && owner_id != ImGuiKeyOwner_NoOwner);
   8802     if (flags & (ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused))
   8803         IM_ASSERT(flags & ImGuiInputFlags_RouteGlobal);
   8804 
   8805     // Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified.
   8806     key_chord = FixupKeyChord(key_chord);
   8807 
   8808     // [DEBUG] Debug break requested by user
   8809     if (g.DebugBreakInShortcutRouting == key_chord)
   8810         IM_DEBUG_BREAK();
   8811 
   8812     if (flags & ImGuiInputFlags_RouteUnlessBgFocused)
   8813         if (g.NavWindow == NULL)
   8814             return false;
   8815 
   8816     // Note how ImGuiInputFlags_RouteAlways won't set routing and thus won't set owner. May want to rework this?
   8817     if (flags & ImGuiInputFlags_RouteAlways)
   8818     {
   8819         IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> always, no register\n", GetKeyChordName(key_chord), flags, owner_id);
   8820         return true;
   8821     }
   8822 
   8823     // Specific culling when there's an active item.
   8824     if (g.ActiveId != 0 && g.ActiveId != owner_id)
   8825     {
   8826         if (flags & ImGuiInputFlags_RouteActive)
   8827             return false;
   8828 
   8829         // Cull shortcuts with no modifiers when it could generate a character.
   8830         // e.g. Shortcut(ImGuiKey_G) also generates 'g' character, should not trigger when InputText() is active.
   8831         // but  Shortcut(Ctrl+G) should generally trigger when InputText() is active.
   8832         // TL;DR: lettered shortcut with no mods or with only Alt mod will not trigger while an item reading text input is active.
   8833         // (We cannot filter based on io.InputQueueCharacters[] contents because of trickling and key<>chars submission order are undefined)
   8834         if (g.IO.WantTextInput && IsKeyChordPotentiallyCharInput(key_chord))
   8835         {
   8836             IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> filtered as potential char input\n", GetKeyChordName(key_chord), flags, owner_id);
   8837             return false;
   8838         }
   8839 
   8840         // ActiveIdUsingAllKeyboardKeys trumps all for ActiveId
   8841         if ((flags & ImGuiInputFlags_RouteOverActive) == 0 && g.ActiveIdUsingAllKeyboardKeys)
   8842         {
   8843             ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
   8844             if (key == ImGuiKey_None)
   8845                 key = ConvertSingleModFlagToKey((ImGuiKey)(key_chord & ImGuiMod_Mask_));
   8846             if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
   8847                 return false;
   8848         }
   8849     }
   8850 
   8851     // Where do we evaluate route for?
   8852     ImGuiID focus_scope_id = g.CurrentFocusScopeId;
   8853     if (flags & ImGuiInputFlags_RouteFromRootWindow)
   8854         focus_scope_id = g.CurrentWindow->RootWindow->ID; // See PushFocusScope() call in Begin()
   8855 
   8856     const int score = CalcRoutingScore(focus_scope_id, owner_id, flags);
   8857     IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> score %d\n", GetKeyChordName(key_chord), flags, owner_id, score);
   8858     if (score == 255)
   8859         return false;
   8860 
   8861     // Submit routing for NEXT frame (assuming score is sufficient)
   8862     // FIXME: Could expose a way to use a "serve last" policy for same score resolution (using <= instead of <).
   8863     ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord);
   8864     //const bool set_route = (flags & ImGuiInputFlags_ServeLast) ? (score <= routing_data->RoutingNextScore) : (score < routing_data->RoutingNextScore);
   8865     if (score < routing_data->RoutingNextScore)
   8866     {
   8867         routing_data->RoutingNext = owner_id;
   8868         routing_data->RoutingNextScore = (ImU8)score;
   8869     }
   8870 
   8871     // Return routing state for CURRENT frame
   8872     if (routing_data->RoutingCurr == owner_id)
   8873         IMGUI_DEBUG_LOG_INPUTROUTING("--> granting current route\n");
   8874     return routing_data->RoutingCurr == owner_id;
   8875 }
   8876 
   8877 // Currently unused by core (but used by tests)
   8878 // Note: this cannot be turned into GetShortcutRouting() because we do the owner_id->routing_id translation, name would be more misleading.
   8879 bool ImGui::TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id)
   8880 {
   8881     const ImGuiID routing_id = GetRoutingIdFromOwnerId(owner_id);
   8882     key_chord = FixupKeyChord(key_chord);
   8883     ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord); // FIXME: Could avoid creating entry.
   8884     return routing_data->RoutingCurr == routing_id;
   8885 }
   8886 
   8887 // Note that Dear ImGui doesn't know the meaning/semantic of ImGuiKey from 0..511: they are legacy native keycodes.
   8888 // Consider transitioning from 'IsKeyDown(MY_ENGINE_KEY_A)' (<1.87) to IsKeyDown(ImGuiKey_A) (>= 1.87)
   8889 bool ImGui::IsKeyDown(ImGuiKey key)
   8890 {
   8891     return IsKeyDown(key, ImGuiKeyOwner_Any);
   8892 }
   8893 
   8894 bool ImGui::IsKeyDown(ImGuiKey key, ImGuiID owner_id)
   8895 {
   8896     const ImGuiKeyData* key_data = GetKeyData(key);
   8897     if (!key_data->Down)
   8898         return false;
   8899     if (!TestKeyOwner(key, owner_id))
   8900         return false;
   8901     return true;
   8902 }
   8903 
   8904 bool ImGui::IsKeyPressed(ImGuiKey key, bool repeat)
   8905 {
   8906     return IsKeyPressed(key, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None, ImGuiKeyOwner_Any);
   8907 }
   8908 
   8909 // Important: unless legacy IsKeyPressed(ImGuiKey, bool repeat=true) which DEFAULT to repeat, this requires EXPLICIT repeat.
   8910 bool ImGui::IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id)
   8911 {
   8912     const ImGuiKeyData* key_data = GetKeyData(key);
   8913     if (!key_data->Down) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership)
   8914         return false;
   8915     const float t = key_data->DownDuration;
   8916     if (t < 0.0f)
   8917         return false;
   8918     IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsKeyPressed) == 0); // Passing flags not supported by this function!
   8919     if (flags & (ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RepeatUntilMask_)) // Setting any _RepeatXXX option enables _Repeat
   8920         flags |= ImGuiInputFlags_Repeat;
   8921 
   8922     bool pressed = (t == 0.0f);
   8923     if (!pressed && (flags & ImGuiInputFlags_Repeat) != 0)
   8924     {
   8925         float repeat_delay, repeat_rate;
   8926         GetTypematicRepeatRate(flags, &repeat_delay, &repeat_rate);
   8927         pressed = (t > repeat_delay) && GetKeyPressedAmount(key, repeat_delay, repeat_rate) > 0;
   8928         if (pressed && (flags & ImGuiInputFlags_RepeatUntilMask_))
   8929         {
   8930             // Slightly bias 'key_pressed_time' as DownDuration is an accumulation of DeltaTime which we compare to an absolute time value.
   8931             // Ideally we'd replace DownDuration with KeyPressedTime but it would break user's code.
   8932             ImGuiContext& g = *GImGui;
   8933             double key_pressed_time = g.Time - t + 0.00001f;
   8934             if ((flags & ImGuiInputFlags_RepeatUntilKeyModsChange) && (g.LastKeyModsChangeTime > key_pressed_time))
   8935                 pressed = false;
   8936             if ((flags & ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone) && (g.LastKeyModsChangeFromNoneTime > key_pressed_time))
   8937                 pressed = false;
   8938             if ((flags & ImGuiInputFlags_RepeatUntilOtherKeyPress) && (g.LastKeyboardKeyPressTime > key_pressed_time))
   8939                 pressed = false;
   8940         }
   8941     }
   8942     if (!pressed)
   8943         return false;
   8944     if (!TestKeyOwner(key, owner_id))
   8945         return false;
   8946     return true;
   8947 }
   8948 
   8949 bool ImGui::IsKeyReleased(ImGuiKey key)
   8950 {
   8951     return IsKeyReleased(key, ImGuiKeyOwner_Any);
   8952 }
   8953 
   8954 bool ImGui::IsKeyReleased(ImGuiKey key, ImGuiID owner_id)
   8955 {
   8956     const ImGuiKeyData* key_data = GetKeyData(key);
   8957     if (key_data->DownDurationPrev < 0.0f || key_data->Down)
   8958         return false;
   8959     if (!TestKeyOwner(key, owner_id))
   8960         return false;
   8961     return true;
   8962 }
   8963 
   8964 bool ImGui::IsMouseDown(ImGuiMouseButton button)
   8965 {
   8966     ImGuiContext& g = *GImGui;
   8967     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
   8968     return g.IO.MouseDown[button] && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); // should be same as IsKeyDown(MouseButtonToKey(button), ImGuiKeyOwner_Any), but this allows legacy code hijacking the io.Mousedown[] array.
   8969 }
   8970 
   8971 bool ImGui::IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id)
   8972 {
   8973     ImGuiContext& g = *GImGui;
   8974     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
   8975     return g.IO.MouseDown[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyDown(MouseButtonToKey(button), owner_id), but this allows legacy code hijacking the io.Mousedown[] array.
   8976 }
   8977 
   8978 bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)
   8979 {
   8980     return IsMouseClicked(button, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None, ImGuiKeyOwner_Any);
   8981 }
   8982 
   8983 bool ImGui::IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id)
   8984 {
   8985     ImGuiContext& g = *GImGui;
   8986     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
   8987     if (!g.IO.MouseDown[button]) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership)
   8988         return false;
   8989     const float t = g.IO.MouseDownDuration[button];
   8990     if (t < 0.0f)
   8991         return false;
   8992     IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsMouseClicked) == 0); // Passing flags not supported by this function! // FIXME: Could support RepeatRate and RepeatUntil flags here.
   8993 
   8994     const bool repeat = (flags & ImGuiInputFlags_Repeat) != 0;
   8995     const bool pressed = (t == 0.0f) || (repeat && t > g.IO.KeyRepeatDelay && CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0);
   8996     if (!pressed)
   8997         return false;
   8998 
   8999     if (!TestKeyOwner(MouseButtonToKey(button), owner_id))
   9000         return false;
   9001 
   9002     return true;
   9003 }
   9004 
   9005 bool ImGui::IsMouseReleased(ImGuiMouseButton button)
   9006 {
   9007     ImGuiContext& g = *GImGui;
   9008     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
   9009     return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); // Should be same as IsKeyReleased(MouseButtonToKey(button), ImGuiKeyOwner_Any)
   9010 }
   9011 
   9012 bool ImGui::IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id)
   9013 {
   9014     ImGuiContext& g = *GImGui;
   9015     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
   9016     return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyReleased(MouseButtonToKey(button), owner_id)
   9017 }
   9018 
   9019 bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
   9020 {
   9021     ImGuiContext& g = *GImGui;
   9022     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
   9023     return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any);
   9024 }
   9025 
   9026 bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button, ImGuiID owner_id)
   9027 {
   9028     ImGuiContext& g = *GImGui;
   9029     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
   9030     return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), owner_id);
   9031 }
   9032 
   9033 int ImGui::GetMouseClickedCount(ImGuiMouseButton button)
   9034 {
   9035     ImGuiContext& g = *GImGui;
   9036     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
   9037     return g.IO.MouseClickedCount[button];
   9038 }
   9039 
   9040 // Test if mouse cursor is hovering given rectangle
   9041 // NB- Rectangle is clipped by our current clip setting
   9042 // NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
   9043 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
   9044 {
   9045     ImGuiContext& g = *GImGui;
   9046 
   9047     // Clip
   9048     ImRect rect_clipped(r_min, r_max);
   9049     if (clip)
   9050         rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
   9051 
   9052     // Hit testing, expanded for touch input
   9053     if (!rect_clipped.ContainsWithPad(g.IO.MousePos, g.Style.TouchExtraPadding))
   9054         return false;
   9055     return true;
   9056 }
   9057 
   9058 // Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame.
   9059 // [Internal] This doesn't test if the button is pressed
   9060 bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold)
   9061 {
   9062     ImGuiContext& g = *GImGui;
   9063     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
   9064     if (lock_threshold < 0.0f)
   9065         lock_threshold = g.IO.MouseDragThreshold;
   9066     return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
   9067 }
   9068 
   9069 bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold)
   9070 {
   9071     ImGuiContext& g = *GImGui;
   9072     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
   9073     if (!g.IO.MouseDown[button])
   9074         return false;
   9075     return IsMouseDragPastThreshold(button, lock_threshold);
   9076 }
   9077 
   9078 ImVec2 ImGui::GetMousePos()
   9079 {
   9080     ImGuiContext& g = *GImGui;
   9081     return g.IO.MousePos;
   9082 }
   9083 
   9084 // This is called TeleportMousePos() and not SetMousePos() to emphasis that setting MousePosPrev will effectively clear mouse delta as well.
   9085 // It is expected you only call this if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) is set and supported by backend.
   9086 void ImGui::TeleportMousePos(const ImVec2& pos)
   9087 {
   9088     ImGuiContext& g = *GImGui;
   9089     g.IO.MousePos = g.IO.MousePosPrev = pos;
   9090     g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
   9091     g.IO.WantSetMousePos = true;
   9092     //IMGUI_DEBUG_LOG_IO("TeleportMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y);
   9093 }
   9094 
   9095 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
   9096 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
   9097 {
   9098     ImGuiContext& g = *GImGui;
   9099     if (g.BeginPopupStack.Size > 0)
   9100         return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos;
   9101     return g.IO.MousePos;
   9102 }
   9103 
   9104 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
   9105 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
   9106 {
   9107     // The assert is only to silence a false-positive in XCode Static Analysis.
   9108     // Because GImGui is not dereferenced in every code path, the static analyzer assume that it may be NULL (which it doesn't for other functions).
   9109     IM_ASSERT(GImGui != NULL);
   9110     const float MOUSE_INVALID = -256000.0f;
   9111     ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
   9112     return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
   9113 }
   9114 
   9115 // [WILL OBSOLETE] This was designed for backends, but prefer having backend maintain a mask of held mouse buttons, because upcoming input queue system will make this invalid.
   9116 bool ImGui::IsAnyMouseDown()
   9117 {
   9118     ImGuiContext& g = *GImGui;
   9119     for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
   9120         if (g.IO.MouseDown[n])
   9121             return true;
   9122     return false;
   9123 }
   9124 
   9125 // Return the delta from the initial clicking position while the mouse button is clicked or was just released.
   9126 // This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
   9127 // NB: This is only valid if IsMousePosValid(). backends in theory should always keep mouse position valid when dragging even outside the client window.
   9128 ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold)
   9129 {
   9130     ImGuiContext& g = *GImGui;
   9131     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
   9132     if (lock_threshold < 0.0f)
   9133         lock_threshold = g.IO.MouseDragThreshold;
   9134     if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])
   9135         if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
   9136             if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))
   9137                 return g.IO.MousePos - g.IO.MouseClickedPos[button];
   9138     return ImVec2(0.0f, 0.0f);
   9139 }
   9140 
   9141 void ImGui::ResetMouseDragDelta(ImGuiMouseButton button)
   9142 {
   9143     ImGuiContext& g = *GImGui;
   9144     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
   9145     // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
   9146     g.IO.MouseClickedPos[button] = g.IO.MousePos;
   9147 }
   9148 
   9149 // Get desired mouse cursor shape.
   9150 // Important: this is meant to be used by a platform backend, it is reset in ImGui::NewFrame(),
   9151 // updated during the frame, and locked in EndFrame()/Render().
   9152 // If you use software rendering by setting io.MouseDrawCursor then Dear ImGui will render those for you
   9153 ImGuiMouseCursor ImGui::GetMouseCursor()
   9154 {
   9155     ImGuiContext& g = *GImGui;
   9156     return g.MouseCursor;
   9157 }
   9158 
   9159 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
   9160 {
   9161     ImGuiContext& g = *GImGui;
   9162     g.MouseCursor = cursor_type;
   9163 }
   9164 
   9165 static void UpdateAliasKey(ImGuiKey key, bool v, float analog_value)
   9166 {
   9167     IM_ASSERT(ImGui::IsAliasKey(key));
   9168     ImGuiKeyData* key_data = ImGui::GetKeyData(key);
   9169     key_data->Down = v;
   9170     key_data->AnalogValue = analog_value;
   9171 }
   9172 
   9173 // [Internal] Do not use directly
   9174 static ImGuiKeyChord GetMergedModsFromKeys()
   9175 {
   9176     ImGuiKeyChord mods = 0;
   9177     if (ImGui::IsKeyDown(ImGuiMod_Ctrl))     { mods |= ImGuiMod_Ctrl; }
   9178     if (ImGui::IsKeyDown(ImGuiMod_Shift))    { mods |= ImGuiMod_Shift; }
   9179     if (ImGui::IsKeyDown(ImGuiMod_Alt))      { mods |= ImGuiMod_Alt; }
   9180     if (ImGui::IsKeyDown(ImGuiMod_Super))    { mods |= ImGuiMod_Super; }
   9181     return mods;
   9182 }
   9183 
   9184 static void ImGui::UpdateKeyboardInputs()
   9185 {
   9186     ImGuiContext& g = *GImGui;
   9187     ImGuiIO& io = g.IO;
   9188 
   9189     if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)
   9190         io.ClearInputKeys();
   9191 
   9192     // Import legacy keys or verify they are not used
   9193 #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
   9194     if (io.BackendUsingLegacyKeyArrays == 0)
   9195     {
   9196         // Backend used new io.AddKeyEvent() API: Good! Verify that old arrays are never written to externally.
   9197         for (int n = 0; n < ImGuiKey_LegacyNativeKey_END; n++)
   9198             IM_ASSERT((io.KeysDown[n] == false || IsKeyDown((ImGuiKey)n)) && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!");
   9199     }
   9200     else
   9201     {
   9202         if (g.FrameCount == 0)
   9203             for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++)
   9204                 IM_ASSERT(g.IO.KeyMap[n] == -1 && "Backend is not allowed to write to io.KeyMap[0..511]!");
   9205 
   9206         // Build reverse KeyMap (Named -> Legacy)
   9207         for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++)
   9208             if (io.KeyMap[n] != -1)
   9209             {
   9210                 IM_ASSERT(IsLegacyKey((ImGuiKey)io.KeyMap[n]));
   9211                 io.KeyMap[io.KeyMap[n]] = n;
   9212             }
   9213 
   9214         // Import legacy keys into new ones
   9215         for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++)
   9216             if (io.KeysDown[n] || io.BackendUsingLegacyKeyArrays == 1)
   9217             {
   9218                 const ImGuiKey key = (ImGuiKey)(io.KeyMap[n] != -1 ? io.KeyMap[n] : n);
   9219                 IM_ASSERT(io.KeyMap[n] == -1 || IsNamedKey(key));
   9220                 io.KeysData[key].Down = io.KeysDown[n];
   9221                 if (key != n)
   9222                     io.KeysDown[key] = io.KeysDown[n]; // Allow legacy code using io.KeysDown[GetKeyIndex()] with old backends
   9223                 io.BackendUsingLegacyKeyArrays = 1;
   9224             }
   9225         if (io.BackendUsingLegacyKeyArrays == 1)
   9226         {
   9227             GetKeyData(ImGuiMod_Ctrl)->Down = io.KeyCtrl;
   9228             GetKeyData(ImGuiMod_Shift)->Down = io.KeyShift;
   9229             GetKeyData(ImGuiMod_Alt)->Down = io.KeyAlt;
   9230             GetKeyData(ImGuiMod_Super)->Down = io.KeySuper;
   9231         }
   9232     }
   9233 #endif
   9234 
   9235     // Import legacy ImGuiNavInput_ io inputs and convert to gamepad keys
   9236 #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
   9237     const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
   9238     if (io.BackendUsingLegacyNavInputArray && nav_gamepad_active)
   9239     {
   9240         #define MAP_LEGACY_NAV_INPUT_TO_KEY1(_KEY, _NAV1)           do { io.KeysData[_KEY].Down = (io.NavInputs[_NAV1] > 0.0f); io.KeysData[_KEY].AnalogValue = io.NavInputs[_NAV1]; } while (0)
   9241         #define MAP_LEGACY_NAV_INPUT_TO_KEY2(_KEY, _NAV1, _NAV2)    do { io.KeysData[_KEY].Down = (io.NavInputs[_NAV1] > 0.0f) || (io.NavInputs[_NAV2] > 0.0f); io.KeysData[_KEY].AnalogValue = ImMax(io.NavInputs[_NAV1], io.NavInputs[_NAV2]); } while (0)
   9242         MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceDown, ImGuiNavInput_Activate);
   9243         MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceRight, ImGuiNavInput_Cancel);
   9244         MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceLeft, ImGuiNavInput_Menu);
   9245         MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceUp, ImGuiNavInput_Input);
   9246         MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadLeft, ImGuiNavInput_DpadLeft);
   9247         MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadRight, ImGuiNavInput_DpadRight);
   9248         MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadUp, ImGuiNavInput_DpadUp);
   9249         MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadDown, ImGuiNavInput_DpadDown);
   9250         MAP_LEGACY_NAV_INPUT_TO_KEY2(ImGuiKey_GamepadL1, ImGuiNavInput_FocusPrev, ImGuiNavInput_TweakSlow);
   9251         MAP_LEGACY_NAV_INPUT_TO_KEY2(ImGuiKey_GamepadR1, ImGuiNavInput_FocusNext, ImGuiNavInput_TweakFast);
   9252         MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickLeft, ImGuiNavInput_LStickLeft);
   9253         MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickRight, ImGuiNavInput_LStickRight);
   9254         MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickUp, ImGuiNavInput_LStickUp);
   9255         MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickDown, ImGuiNavInput_LStickDown);
   9256         #undef NAV_MAP_KEY
   9257     }
   9258 #endif
   9259 
   9260     // Update aliases
   9261     for (int n = 0; n < ImGuiMouseButton_COUNT; n++)
   9262         UpdateAliasKey(MouseButtonToKey(n), io.MouseDown[n], io.MouseDown[n] ? 1.0f : 0.0f);
   9263     UpdateAliasKey(ImGuiKey_MouseWheelX, io.MouseWheelH != 0.0f, io.MouseWheelH);
   9264     UpdateAliasKey(ImGuiKey_MouseWheelY, io.MouseWheel != 0.0f, io.MouseWheel);
   9265 
   9266     // Synchronize io.KeyMods and io.KeyCtrl/io.KeyShift/etc. values.
   9267     // - New backends (1.87+): send io.AddKeyEvent(ImGuiMod_XXX) ->                                      -> (here) deriving io.KeyMods + io.KeyXXX from key array.
   9268     // - Legacy backends:      set io.KeyXXX bools               -> (above) set key array from io.KeyXXX -> (here) deriving io.KeyMods + io.KeyXXX from key array.
   9269     // So with legacy backends the 4 values will do a unnecessary back-and-forth but it makes the code simpler and future facing.
   9270     const ImGuiKeyChord prev_key_mods = io.KeyMods;
   9271     io.KeyMods = GetMergedModsFromKeys();
   9272     io.KeyCtrl = (io.KeyMods & ImGuiMod_Ctrl) != 0;
   9273     io.KeyShift = (io.KeyMods & ImGuiMod_Shift) != 0;
   9274     io.KeyAlt = (io.KeyMods & ImGuiMod_Alt) != 0;
   9275     io.KeySuper = (io.KeyMods & ImGuiMod_Super) != 0;
   9276     if (prev_key_mods != io.KeyMods)
   9277         g.LastKeyModsChangeTime = g.Time;
   9278     if (prev_key_mods != io.KeyMods && prev_key_mods == 0)
   9279         g.LastKeyModsChangeFromNoneTime = g.Time;
   9280 
   9281     // Clear gamepad data if disabled
   9282     if ((io.BackendFlags & ImGuiBackendFlags_HasGamepad) == 0)
   9283         for (int i = ImGuiKey_Gamepad_BEGIN; i < ImGuiKey_Gamepad_END; i++)
   9284         {
   9285             io.KeysData[i - ImGuiKey_KeysData_OFFSET].Down = false;
   9286             io.KeysData[i - ImGuiKey_KeysData_OFFSET].AnalogValue = 0.0f;
   9287         }
   9288 
   9289     // Update keys
   9290     for (int i = 0; i < ImGuiKey_KeysData_SIZE; i++)
   9291     {
   9292         ImGuiKeyData* key_data = &io.KeysData[i];
   9293         key_data->DownDurationPrev = key_data->DownDuration;
   9294         key_data->DownDuration = key_data->Down ? (key_data->DownDuration < 0.0f ? 0.0f : key_data->DownDuration + io.DeltaTime) : -1.0f;
   9295         if (key_data->DownDuration == 0.0f)
   9296         {
   9297             ImGuiKey key = (ImGuiKey)(ImGuiKey_KeysData_OFFSET + i);
   9298             if (IsKeyboardKey(key))
   9299                 g.LastKeyboardKeyPressTime = g.Time;
   9300             else if (key == ImGuiKey_ReservedForModCtrl || key == ImGuiKey_ReservedForModShift || key == ImGuiKey_ReservedForModAlt || key == ImGuiKey_ReservedForModSuper)
   9301                 g.LastKeyboardKeyPressTime = g.Time;
   9302         }
   9303     }
   9304 
   9305     // Update keys/input owner (named keys only): one entry per key
   9306     for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
   9307     {
   9308         ImGuiKeyData* key_data = &io.KeysData[key - ImGuiKey_KeysData_OFFSET];
   9309         ImGuiKeyOwnerData* owner_data = &g.KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN];
   9310         owner_data->OwnerCurr = owner_data->OwnerNext;
   9311         if (!key_data->Down) // Important: ownership is released on the frame after a release. Ensure a 'MouseDown -> CloseWindow -> MouseUp' chain doesn't lead to someone else seeing the MouseUp.
   9312             owner_data->OwnerNext = ImGuiKeyOwner_NoOwner;
   9313         owner_data->LockThisFrame = owner_data->LockUntilRelease = owner_data->LockUntilRelease && key_data->Down;  // Clear LockUntilRelease when key is not Down anymore
   9314     }
   9315 
   9316     // Update key routing (for e.g. shortcuts)
   9317     UpdateKeyRoutingTable(&g.KeysRoutingTable);
   9318 }
   9319 
   9320 static void ImGui::UpdateMouseInputs()
   9321 {
   9322     ImGuiContext& g = *GImGui;
   9323     ImGuiIO& io = g.IO;
   9324 
   9325     // Mouse Wheel swapping flag
   9326     // As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead
   9327     // - We avoid doing it on OSX as it the OS input layer handles this already.
   9328     // - FIXME: However this means when running on OSX over Emscripten, Shift+WheelY will incur two swapping (1 in OS, 1 here), canceling the feature.
   9329     // - FIXME: When we can distinguish e.g. touchpad scroll events from mouse ones, we'll set this accordingly based on input source.
   9330     io.MouseWheelRequestAxisSwap = io.KeyShift && !io.ConfigMacOSXBehaviors;
   9331 
   9332     // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
   9333     if (IsMousePosValid(&io.MousePos))
   9334         io.MousePos = g.MouseLastValidPos = ImFloor(io.MousePos);
   9335 
   9336     // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
   9337     if (IsMousePosValid(&io.MousePos) && IsMousePosValid(&io.MousePosPrev))
   9338         io.MouseDelta = io.MousePos - io.MousePosPrev;
   9339     else
   9340         io.MouseDelta = ImVec2(0.0f, 0.0f);
   9341 
   9342     // Update stationary timer.
   9343     // FIXME: May need to rework again to have some tolerance for occasional small movement, while being functional on high-framerates.
   9344     const float mouse_stationary_threshold = (io.MouseSource == ImGuiMouseSource_Mouse) ? 2.0f : 3.0f; // Slightly higher threshold for ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen, may need rework.
   9345     const bool mouse_stationary = (ImLengthSqr(io.MouseDelta) <= mouse_stationary_threshold * mouse_stationary_threshold);
   9346     g.MouseStationaryTimer = mouse_stationary ? (g.MouseStationaryTimer + io.DeltaTime) : 0.0f;
   9347     //IMGUI_DEBUG_LOG("%.4f\n", g.MouseStationaryTimer);
   9348 
   9349     // If mouse moved we re-enable mouse hovering in case it was disabled by gamepad/keyboard. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true.
   9350     if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)
   9351         g.NavDisableMouseHover = false;
   9352 
   9353     for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
   9354     {
   9355         io.MouseClicked[i] = io.MouseDown[i] && io.MouseDownDuration[i] < 0.0f;
   9356         io.MouseClickedCount[i] = 0; // Will be filled below
   9357         io.MouseReleased[i] = !io.MouseDown[i] && io.MouseDownDuration[i] >= 0.0f;
   9358         io.MouseDownDurationPrev[i] = io.MouseDownDuration[i];
   9359         io.MouseDownDuration[i] = io.MouseDown[i] ? (io.MouseDownDuration[i] < 0.0f ? 0.0f : io.MouseDownDuration[i] + io.DeltaTime) : -1.0f;
   9360         if (io.MouseClicked[i])
   9361         {
   9362             bool is_repeated_click = false;
   9363             if ((float)(g.Time - io.MouseClickedTime[i]) < io.MouseDoubleClickTime)
   9364             {
   9365                 ImVec2 delta_from_click_pos = IsMousePosValid(&io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
   9366                 if (ImLengthSqr(delta_from_click_pos) < io.MouseDoubleClickMaxDist * io.MouseDoubleClickMaxDist)
   9367                     is_repeated_click = true;
   9368             }
   9369             if (is_repeated_click)
   9370                 io.MouseClickedLastCount[i]++;
   9371             else
   9372                 io.MouseClickedLastCount[i] = 1;
   9373             io.MouseClickedTime[i] = g.Time;
   9374             io.MouseClickedPos[i] = io.MousePos;
   9375             io.MouseClickedCount[i] = io.MouseClickedLastCount[i];
   9376             io.MouseDragMaxDistanceSqr[i] = 0.0f;
   9377         }
   9378         else if (io.MouseDown[i])
   9379         {
   9380             // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
   9381             float delta_sqr_click_pos = IsMousePosValid(&io.MousePos) ? ImLengthSqr(io.MousePos - io.MouseClickedPos[i]) : 0.0f;
   9382             io.MouseDragMaxDistanceSqr[i] = ImMax(io.MouseDragMaxDistanceSqr[i], delta_sqr_click_pos);
   9383         }
   9384 
   9385         // We provide io.MouseDoubleClicked[] as a legacy service
   9386         io.MouseDoubleClicked[i] = (io.MouseClickedCount[i] == 2);
   9387 
   9388         // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
   9389         if (io.MouseClicked[i])
   9390             g.NavDisableMouseHover = false;
   9391     }
   9392 }
   9393 
   9394 static void LockWheelingWindow(ImGuiWindow* window, float wheel_amount)
   9395 {
   9396     ImGuiContext& g = *GImGui;
   9397     if (window)
   9398         g.WheelingWindowReleaseTimer = ImMin(g.WheelingWindowReleaseTimer + ImAbs(wheel_amount) * WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER, WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER);
   9399     else
   9400         g.WheelingWindowReleaseTimer = 0.0f;
   9401     if (g.WheelingWindow == window)
   9402         return;
   9403     IMGUI_DEBUG_LOG_IO("[io] LockWheelingWindow() \"%s\"\n", window ? window->Name : "NULL");
   9404     g.WheelingWindow = window;
   9405     g.WheelingWindowRefMousePos = g.IO.MousePos;
   9406     if (window == NULL)
   9407     {
   9408         g.WheelingWindowStartFrame = -1;
   9409         g.WheelingAxisAvg = ImVec2(0.0f, 0.0f);
   9410     }
   9411 }
   9412 
   9413 static ImGuiWindow* FindBestWheelingWindow(const ImVec2& wheel)
   9414 {
   9415     // For each axis, find window in the hierarchy that may want to use scrolling
   9416     ImGuiContext& g = *GImGui;
   9417     ImGuiWindow* windows[2] = { NULL, NULL };
   9418     for (int axis = 0; axis < 2; axis++)
   9419         if (wheel[axis] != 0.0f)
   9420             for (ImGuiWindow* window = windows[axis] = g.HoveredWindow; window->Flags & ImGuiWindowFlags_ChildWindow; window = windows[axis] = window->ParentWindow)
   9421             {
   9422                 // Bubble up into parent window if:
   9423                 // - a child window doesn't allow any scrolling.
   9424                 // - a child window has the ImGuiWindowFlags_NoScrollWithMouse flag.
   9425                 //// - a child window doesn't need scrolling because it is already at the edge for the direction we are going in (FIXME-WIP)
   9426                 const bool has_scrolling = (window->ScrollMax[axis] != 0.0f);
   9427                 const bool inputs_disabled = (window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs);
   9428                 //const bool scrolling_past_limits = (wheel_v < 0.0f) ? (window->Scroll[axis] <= 0.0f) : (window->Scroll[axis] >= window->ScrollMax[axis]);
   9429                 if (has_scrolling && !inputs_disabled) // && !scrolling_past_limits)
   9430                     break; // select this window
   9431             }
   9432     if (windows[0] == NULL && windows[1] == NULL)
   9433         return NULL;
   9434 
   9435     // If there's only one window or only one axis then there's no ambiguity
   9436     if (windows[0] == windows[1] || windows[0] == NULL || windows[1] == NULL)
   9437         return windows[1] ? windows[1] : windows[0];
   9438 
   9439     // If candidate are different windows we need to decide which one to prioritize
   9440     // - First frame: only find a winner if one axis is zero.
   9441     // - Subsequent frames: only find a winner when one is more than the other.
   9442     if (g.WheelingWindowStartFrame == -1)
   9443         g.WheelingWindowStartFrame = g.FrameCount;
   9444     if ((g.WheelingWindowStartFrame == g.FrameCount && wheel.x != 0.0f && wheel.y != 0.0f) || (g.WheelingAxisAvg.x == g.WheelingAxisAvg.y))
   9445     {
   9446         g.WheelingWindowWheelRemainder = wheel;
   9447         return NULL;
   9448     }
   9449     return (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? windows[0] : windows[1];
   9450 }
   9451 
   9452 // Called by NewFrame()
   9453 void ImGui::UpdateMouseWheel()
   9454 {
   9455     // Reset the locked window if we move the mouse or after the timer elapses.
   9456     // FIXME: Ideally we could refactor to have one timer for "changing window w/ same axis" and a shorter timer for "changing window or axis w/ other axis" (#3795)
   9457     ImGuiContext& g = *GImGui;
   9458     if (g.WheelingWindow != NULL)
   9459     {
   9460         g.WheelingWindowReleaseTimer -= g.IO.DeltaTime;
   9461         if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
   9462             g.WheelingWindowReleaseTimer = 0.0f;
   9463         if (g.WheelingWindowReleaseTimer <= 0.0f)
   9464             LockWheelingWindow(NULL, 0.0f);
   9465     }
   9466 
   9467     ImVec2 wheel;
   9468     wheel.x = TestKeyOwner(ImGuiKey_MouseWheelX, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheelH : 0.0f;
   9469     wheel.y = TestKeyOwner(ImGuiKey_MouseWheelY, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheel : 0.0f;
   9470 
   9471     //IMGUI_DEBUG_LOG("MouseWheel X:%.3f Y:%.3f\n", wheel_x, wheel_y);
   9472     ImGuiWindow* mouse_window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
   9473     if (!mouse_window || mouse_window->Collapsed)
   9474         return;
   9475 
   9476     // Zoom / Scale window
   9477     // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
   9478     if (wheel.y != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
   9479     {
   9480         LockWheelingWindow(mouse_window, wheel.y);
   9481         ImGuiWindow* window = mouse_window;
   9482         const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
   9483         const float scale = new_font_scale / window->FontWindowScale;
   9484         window->FontWindowScale = new_font_scale;
   9485         if (window == window->RootWindow)
   9486         {
   9487             const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
   9488             SetWindowPos(window, window->Pos + offset, 0);
   9489             window->Size = ImTrunc(window->Size * scale);
   9490             window->SizeFull = ImTrunc(window->SizeFull * scale);
   9491         }
   9492         return;
   9493     }
   9494     if (g.IO.KeyCtrl)
   9495         return;
   9496 
   9497     // Mouse wheel scrolling
   9498     // Read about io.MouseWheelRequestAxisSwap and its issue on Mac+Emscripten in UpdateMouseInputs()
   9499     if (g.IO.MouseWheelRequestAxisSwap)
   9500         wheel = ImVec2(wheel.y, 0.0f);
   9501 
   9502     // Maintain a rough average of moving magnitude on both axises
   9503     // FIXME: should by based on wall clock time rather than frame-counter
   9504     g.WheelingAxisAvg.x = ImExponentialMovingAverage(g.WheelingAxisAvg.x, ImAbs(wheel.x), 30);
   9505     g.WheelingAxisAvg.y = ImExponentialMovingAverage(g.WheelingAxisAvg.y, ImAbs(wheel.y), 30);
   9506 
   9507     // In the rare situation where FindBestWheelingWindow() had to defer first frame of wheeling due to ambiguous main axis, reinject it now.
   9508     wheel += g.WheelingWindowWheelRemainder;
   9509     g.WheelingWindowWheelRemainder = ImVec2(0.0f, 0.0f);
   9510     if (wheel.x == 0.0f && wheel.y == 0.0f)
   9511         return;
   9512 
   9513     // Mouse wheel scrolling: find target and apply
   9514     // - don't renew lock if axis doesn't apply on the window.
   9515     // - select a main axis when both axises are being moved.
   9516     if (ImGuiWindow* window = (g.WheelingWindow ? g.WheelingWindow : FindBestWheelingWindow(wheel)))
   9517         if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
   9518         {
   9519             bool do_scroll[2] = { wheel.x != 0.0f && window->ScrollMax.x != 0.0f, wheel.y != 0.0f && window->ScrollMax.y != 0.0f };
   9520             if (do_scroll[ImGuiAxis_X] && do_scroll[ImGuiAxis_Y])
   9521                 do_scroll[(g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? ImGuiAxis_Y : ImGuiAxis_X] = false;
   9522             if (do_scroll[ImGuiAxis_X])
   9523             {
   9524                 LockWheelingWindow(window, wheel.x);
   9525                 float max_step = window->InnerRect.GetWidth() * 0.67f;
   9526                 float scroll_step = ImTrunc(ImMin(2 * window->CalcFontSize(), max_step));
   9527                 SetScrollX(window, window->Scroll.x - wheel.x * scroll_step);
   9528                 g.WheelingWindowScrolledFrame = g.FrameCount;
   9529             }
   9530             if (do_scroll[ImGuiAxis_Y])
   9531             {
   9532                 LockWheelingWindow(window, wheel.y);
   9533                 float max_step = window->InnerRect.GetHeight() * 0.67f;
   9534                 float scroll_step = ImTrunc(ImMin(5 * window->CalcFontSize(), max_step));
   9535                 SetScrollY(window, window->Scroll.y - wheel.y * scroll_step);
   9536                 g.WheelingWindowScrolledFrame = g.FrameCount;
   9537             }
   9538         }
   9539 }
   9540 
   9541 void ImGui::SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard)
   9542 {
   9543     ImGuiContext& g = *GImGui;
   9544     g.WantCaptureKeyboardNextFrame = want_capture_keyboard ? 1 : 0;
   9545 }
   9546 
   9547 void ImGui::SetNextFrameWantCaptureMouse(bool want_capture_mouse)
   9548 {
   9549     ImGuiContext& g = *GImGui;
   9550     g.WantCaptureMouseNextFrame = want_capture_mouse ? 1 : 0;
   9551 }
   9552 
   9553 #ifndef IMGUI_DISABLE_DEBUG_TOOLS
   9554 static const char* GetInputSourceName(ImGuiInputSource source)
   9555 {
   9556     const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad" };
   9557     IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT && source >= 0 && source < ImGuiInputSource_COUNT);
   9558     return input_source_names[source];
   9559 }
   9560 static const char* GetMouseSourceName(ImGuiMouseSource source)
   9561 {
   9562     const char* mouse_source_names[] = { "Mouse", "TouchScreen", "Pen" };
   9563     IM_ASSERT(IM_ARRAYSIZE(mouse_source_names) == ImGuiMouseSource_COUNT && source >= 0 && source < ImGuiMouseSource_COUNT);
   9564     return mouse_source_names[source];
   9565 }
   9566 static void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e)
   9567 {
   9568     ImGuiContext& g = *GImGui;
   9569     if (e->Type == ImGuiInputEventType_MousePos)    { if (e->MousePos.PosX == -FLT_MAX && e->MousePos.PosY == -FLT_MAX) IMGUI_DEBUG_LOG_IO("[io] %s: MousePos (-FLT_MAX, -FLT_MAX)\n", prefix); else IMGUI_DEBUG_LOG_IO("[io] %s: MousePos (%.1f, %.1f) (%s)\n", prefix, e->MousePos.PosX, e->MousePos.PosY, GetMouseSourceName(e->MousePos.MouseSource)); return; }
   9570     if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_LOG_IO("[io] %s: MouseButton %d %s (%s)\n", prefix, e->MouseButton.Button, e->MouseButton.Down ? "Down" : "Up", GetMouseSourceName(e->MouseButton.MouseSource)); return; }
   9571     if (e->Type == ImGuiInputEventType_MouseWheel)  { IMGUI_DEBUG_LOG_IO("[io] %s: MouseWheel (%.3f, %.3f) (%s)\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY, GetMouseSourceName(e->MouseWheel.MouseSource)); return; }
   9572     if (e->Type == ImGuiInputEventType_Key)         { IMGUI_DEBUG_LOG_IO("[io] %s: Key \"%s\" %s\n", prefix, ImGui::GetKeyName(e->Key.Key), e->Key.Down ? "Down" : "Up"); return; }
   9573     if (e->Type == ImGuiInputEventType_Text)        { IMGUI_DEBUG_LOG_IO("[io] %s: Text: %c (U+%08X)\n", prefix, e->Text.Char, e->Text.Char); return; }
   9574     if (e->Type == ImGuiInputEventType_Focus)       { IMGUI_DEBUG_LOG_IO("[io] %s: AppFocused %d\n", prefix, e->AppFocused.Focused); return; }
   9575 }
   9576 #endif
   9577 
   9578 // Process input queue
   9579 // We always call this with the value of 'bool g.IO.ConfigInputTrickleEventQueue'.
   9580 // - trickle_fast_inputs = false : process all events, turn into flattened input state (e.g. successive down/up/down/up will be lost)
   9581 // - trickle_fast_inputs = true  : process as many events as possible (successive down/up/down/up will be trickled over several frames so nothing is lost) (new feature in 1.87)
   9582 void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
   9583 {
   9584     ImGuiContext& g = *GImGui;
   9585     ImGuiIO& io = g.IO;
   9586 
   9587     // Only trickle chars<>key when working with InputText()
   9588     // FIXME: InputText() could parse event trail?
   9589     // FIXME: Could specialize chars<>keys trickling rules for control keys (those not typically associated to characters)
   9590     const bool trickle_interleaved_keys_and_text = (trickle_fast_inputs && g.WantTextInputNextFrame == 1);
   9591 
   9592     bool mouse_moved = false, mouse_wheeled = false, key_changed = false, text_inputted = false;
   9593     int  mouse_button_changed = 0x00;
   9594     ImBitArray<ImGuiKey_KeysData_SIZE> key_changed_mask;
   9595 
   9596     int event_n = 0;
   9597     for (; event_n < g.InputEventsQueue.Size; event_n++)
   9598     {
   9599         ImGuiInputEvent* e = &g.InputEventsQueue[event_n];
   9600         if (e->Type == ImGuiInputEventType_MousePos)
   9601         {
   9602             if (g.IO.WantSetMousePos)
   9603                 continue;
   9604             // Trickling Rule: Stop processing queued events if we already handled a mouse button change
   9605             ImVec2 event_pos(e->MousePos.PosX, e->MousePos.PosY);
   9606             if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || text_inputted))
   9607                 break;
   9608             io.MousePos = event_pos;
   9609             io.MouseSource = e->MousePos.MouseSource;
   9610             mouse_moved = true;
   9611         }
   9612         else if (e->Type == ImGuiInputEventType_MouseButton)
   9613         {
   9614             // Trickling Rule: Stop processing queued events if we got multiple action on the same button
   9615             const ImGuiMouseButton button = e->MouseButton.Button;
   9616             IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT);
   9617             if (trickle_fast_inputs && ((mouse_button_changed & (1 << button)) || mouse_wheeled))
   9618                 break;
   9619             if (trickle_fast_inputs && e->MouseButton.MouseSource == ImGuiMouseSource_TouchScreen && mouse_moved) // #2702: TouchScreen have no initial hover.
   9620                 break;
   9621             io.MouseDown[button] = e->MouseButton.Down;
   9622             io.MouseSource = e->MouseButton.MouseSource;
   9623             mouse_button_changed |= (1 << button);
   9624         }
   9625         else if (e->Type == ImGuiInputEventType_MouseWheel)
   9626         {
   9627             // Trickling Rule: Stop processing queued events if we got multiple action on the event
   9628             if (trickle_fast_inputs && (mouse_moved || mouse_button_changed != 0))
   9629                 break;
   9630             io.MouseWheelH += e->MouseWheel.WheelX;
   9631             io.MouseWheel += e->MouseWheel.WheelY;
   9632             io.MouseSource = e->MouseWheel.MouseSource;
   9633             mouse_wheeled = true;
   9634         }
   9635         else if (e->Type == ImGuiInputEventType_Key)
   9636         {
   9637             // Trickling Rule: Stop processing queued events if we got multiple action on the same button
   9638             if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)
   9639                 continue;
   9640             ImGuiKey key = e->Key.Key;
   9641             IM_ASSERT(key != ImGuiKey_None);
   9642             ImGuiKeyData* key_data = GetKeyData(key);
   9643             const int key_data_index = (int)(key_data - g.IO.KeysData);
   9644             if (trickle_fast_inputs && key_data->Down != e->Key.Down && (key_changed_mask.TestBit(key_data_index) || text_inputted || mouse_button_changed != 0))
   9645                 break;
   9646             key_data->Down = e->Key.Down;
   9647             key_data->AnalogValue = e->Key.AnalogValue;
   9648             key_changed = true;
   9649             key_changed_mask.SetBit(key_data_index);
   9650 
   9651             // Allow legacy code using io.KeysDown[GetKeyIndex()] with new backends
   9652 #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
   9653             io.KeysDown[key_data_index] = key_data->Down;
   9654             if (io.KeyMap[key_data_index] != -1)
   9655                 io.KeysDown[io.KeyMap[key_data_index]] = key_data->Down;
   9656 #endif
   9657         }
   9658         else if (e->Type == ImGuiInputEventType_Text)
   9659         {
   9660             if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)
   9661                 continue;
   9662             // Trickling Rule: Stop processing queued events if keys/mouse have been interacted with
   9663             if (trickle_fast_inputs && ((key_changed && trickle_interleaved_keys_and_text) || mouse_button_changed != 0 || mouse_moved || mouse_wheeled))
   9664                 break;
   9665             unsigned int c = e->Text.Char;
   9666             io.InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID);
   9667             if (trickle_interleaved_keys_and_text)
   9668                 text_inputted = true;
   9669         }
   9670         else if (e->Type == ImGuiInputEventType_Focus)
   9671         {
   9672             // We intentionally overwrite this and process in NewFrame(), in order to give a chance
   9673             // to multi-viewports backends to queue AddFocusEvent(false) + AddFocusEvent(true) in same frame.
   9674             const bool focus_lost = !e->AppFocused.Focused;
   9675             io.AppFocusLost = focus_lost;
   9676         }
   9677         else
   9678         {
   9679             IM_ASSERT(0 && "Unknown event!");
   9680         }
   9681     }
   9682 
   9683     // Record trail (for domain-specific applications wanting to access a precise trail)
   9684     //if (event_n != 0) IMGUI_DEBUG_LOG_IO("Processed: %d / Remaining: %d\n", event_n, g.InputEventsQueue.Size - event_n);
   9685     for (int n = 0; n < event_n; n++)
   9686         g.InputEventsTrail.push_back(g.InputEventsQueue[n]);
   9687 
   9688     // [DEBUG]
   9689 #ifndef IMGUI_DISABLE_DEBUG_TOOLS
   9690     if (event_n != 0 && (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO))
   9691         for (int n = 0; n < g.InputEventsQueue.Size; n++)
   9692             DebugPrintInputEvent(n < event_n ? "Processed" : "Remaining", &g.InputEventsQueue[n]);
   9693 #endif
   9694 
   9695     // Remaining events will be processed on the next frame
   9696     if (event_n == g.InputEventsQueue.Size)
   9697         g.InputEventsQueue.resize(0);
   9698     else
   9699         g.InputEventsQueue.erase(g.InputEventsQueue.Data, g.InputEventsQueue.Data + event_n);
   9700 
   9701     // Clear buttons state when focus is lost
   9702     // - this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle.
   9703     // - we clear in EndFrame() and not now in order allow application/user code polling this flag
   9704     //   (e.g. custom backend may want to clear additional data, custom widgets may want to react with a "canceling" event).
   9705     if (g.IO.AppFocusLost)
   9706     {
   9707         g.IO.ClearInputKeys();
   9708         g.IO.ClearInputMouse();
   9709     }
   9710 }
   9711 
   9712 ImGuiID ImGui::GetKeyOwner(ImGuiKey key)
   9713 {
   9714     if (!IsNamedKeyOrMod(key))
   9715         return ImGuiKeyOwner_NoOwner;
   9716 
   9717     ImGuiContext& g = *GImGui;
   9718     ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
   9719     ImGuiID owner_id = owner_data->OwnerCurr;
   9720 
   9721     if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any)
   9722         if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
   9723             return ImGuiKeyOwner_NoOwner;
   9724 
   9725     return owner_id;
   9726 }
   9727 
   9728 // TestKeyOwner(..., ID)   : (owner == None || owner == ID)
   9729 // TestKeyOwner(..., None) : (owner == None)
   9730 // TestKeyOwner(..., Any)  : no owner test
   9731 // All paths are also testing for key not being locked, for the rare cases that key have been locked with using ImGuiInputFlags_LockXXX flags.
   9732 bool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id)
   9733 {
   9734     if (!IsNamedKeyOrMod(key))
   9735         return true;
   9736 
   9737     ImGuiContext& g = *GImGui;
   9738     if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any)
   9739         if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
   9740             return false;
   9741 
   9742     ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
   9743     if (owner_id == ImGuiKeyOwner_Any)
   9744         return (owner_data->LockThisFrame == false);
   9745 
   9746     // Note: SetKeyOwner() sets OwnerCurr. It is not strictly required for most mouse routing overlap (because of ActiveId/HoveredId
   9747     // are acting as filter before this has a chance to filter), but sane as soon as user tries to look into things.
   9748     // Setting OwnerCurr in SetKeyOwner() is more consistent than testing OwnerNext here: would be inconsistent with getter and other functions.
   9749     if (owner_data->OwnerCurr != owner_id)
   9750     {
   9751         if (owner_data->LockThisFrame)
   9752             return false;
   9753         if (owner_data->OwnerCurr != ImGuiKeyOwner_NoOwner)
   9754             return false;
   9755     }
   9756 
   9757     return true;
   9758 }
   9759 
   9760 // _LockXXX flags are useful to lock keys away from code which is not input-owner aware.
   9761 // When using _LockXXX flags, you can use ImGuiKeyOwner_Any to lock keys from everyone.
   9762 // - SetKeyOwner(..., None)              : clears owner
   9763 // - SetKeyOwner(..., Any, !Lock)        : illegal (assert)
   9764 // - SetKeyOwner(..., Any or None, Lock) : set lock
   9765 void ImGui::SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags)
   9766 {
   9767     ImGuiContext& g = *GImGui;
   9768     IM_ASSERT(IsNamedKeyOrMod(key) && (owner_id != ImGuiKeyOwner_Any || (flags & (ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease)))); // Can only use _Any with _LockXXX flags (to eat a key away without an ID to retrieve it)
   9769     IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetKeyOwner) == 0); // Passing flags not supported by this function!
   9770     //IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X, flags=%08X)\n", GetKeyName(key), owner_id, flags);
   9771 
   9772     ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
   9773     owner_data->OwnerCurr = owner_data->OwnerNext = owner_id;
   9774 
   9775     // We cannot lock by default as it would likely break lots of legacy code.
   9776     // In the case of using LockUntilRelease while key is not down we still lock during the frame (no key_data->Down test)
   9777     owner_data->LockUntilRelease = (flags & ImGuiInputFlags_LockUntilRelease) != 0;
   9778     owner_data->LockThisFrame = (flags & ImGuiInputFlags_LockThisFrame) != 0 || (owner_data->LockUntilRelease);
   9779 }
   9780 
   9781 // Rarely used helper
   9782 void ImGui::SetKeyOwnersForKeyChord(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags)
   9783 {
   9784     if (key_chord & ImGuiMod_Ctrl)      { SetKeyOwner(ImGuiMod_Ctrl, owner_id, flags); }
   9785     if (key_chord & ImGuiMod_Shift)     { SetKeyOwner(ImGuiMod_Shift, owner_id, flags); }
   9786     if (key_chord & ImGuiMod_Alt)       { SetKeyOwner(ImGuiMod_Alt, owner_id, flags); }
   9787     if (key_chord & ImGuiMod_Super)     { SetKeyOwner(ImGuiMod_Super, owner_id, flags); }
   9788     if (key_chord & ~ImGuiMod_Mask_)    { SetKeyOwner((ImGuiKey)(key_chord & ~ImGuiMod_Mask_), owner_id, flags); }
   9789 }
   9790 
   9791 // This is more or less equivalent to:
   9792 //   if (IsItemHovered() || IsItemActive())
   9793 //       SetKeyOwner(key, GetItemID());
   9794 // Extensive uses of that (e.g. many calls for a single item) may want to manually perform the tests once and then call SetKeyOwner() multiple times.
   9795 // More advanced usage scenarios may want to call SetKeyOwner() manually based on different condition.
   9796 // Worth noting is that only one item can be hovered and only one item can be active, therefore this usage pattern doesn't need to bother with routing and priority.
   9797 void ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags)
   9798 {
   9799     ImGuiContext& g = *GImGui;
   9800     ImGuiID id = g.LastItemData.ID;
   9801     if (id == 0 || (g.HoveredId != id && g.ActiveId != id))
   9802         return;
   9803     if ((flags & ImGuiInputFlags_CondMask_) == 0)
   9804         flags |= ImGuiInputFlags_CondDefault_;
   9805     if ((g.HoveredId == id && (flags & ImGuiInputFlags_CondHovered)) || (g.ActiveId == id && (flags & ImGuiInputFlags_CondActive)))
   9806     {
   9807         IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetItemKeyOwner) == 0); // Passing flags not supported by this function!
   9808         SetKeyOwner(key, id, flags & ~ImGuiInputFlags_CondMask_);
   9809     }
   9810 }
   9811 
   9812 void ImGui::SetItemKeyOwner(ImGuiKey key)
   9813 {
   9814     SetItemKeyOwner(key, ImGuiInputFlags_None);
   9815 }
   9816 
   9817 // This is the only public API until we expose owner_id versions of the API as replacements.
   9818 bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord)
   9819 {
   9820     return IsKeyChordPressed(key_chord, ImGuiInputFlags_None, ImGuiKeyOwner_Any);
   9821 }
   9822 
   9823 // This is equivalent to comparing KeyMods + doing a IsKeyPressed()
   9824 bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
   9825 {
   9826     ImGuiContext& g = *GImGui;
   9827     key_chord = FixupKeyChord(key_chord);
   9828     ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
   9829     if (g.IO.KeyMods != mods)
   9830         return false;
   9831 
   9832     // Special storage location for mods
   9833     ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
   9834     if (key == ImGuiKey_None)
   9835         key = ConvertSingleModFlagToKey(mods);
   9836     if (!IsKeyPressed(key, (flags & ImGuiInputFlags_RepeatMask_), owner_id))
   9837         return false;
   9838     return true;
   9839 }
   9840 
   9841 void ImGui::SetNextItemShortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags)
   9842 {
   9843     ImGuiContext& g = *GImGui;
   9844     g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasShortcut;
   9845     g.NextItemData.Shortcut = key_chord;
   9846     g.NextItemData.ShortcutFlags = flags;
   9847 }
   9848 
   9849 // Called from within ItemAdd: at this point we can read from NextItemData and write to LastItemData
   9850 void ImGui::ItemHandleShortcut(ImGuiID id)
   9851 {
   9852     ImGuiContext& g = *GImGui;
   9853     ImGuiInputFlags flags = g.NextItemData.ShortcutFlags;
   9854     IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetNextItemShortcut) == 0); // Passing flags not supported by SetNextItemShortcut()!
   9855 
   9856     if (g.LastItemData.InFlags & ImGuiItemFlags_Disabled)
   9857         return;
   9858     if (flags & ImGuiInputFlags_Tooltip)
   9859     {
   9860         g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasShortcut;
   9861         g.LastItemData.Shortcut = g.NextItemData.Shortcut;
   9862     }
   9863     if (!Shortcut(g.NextItemData.Shortcut, flags & ImGuiInputFlags_SupportedByShortcut, id) || g.NavActivateId != 0)
   9864         return;
   9865 
   9866     // FIXME: Generalize Activation queue?
   9867     g.NavActivateId = id; // Will effectively disable clipping.
   9868     g.NavActivateFlags = ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_FromShortcut;
   9869     //if (g.ActiveId == 0 || g.ActiveId == id)
   9870     g.NavActivateDownId = g.NavActivatePressedId = id;
   9871     NavHighlightActivated(id);
   9872 }
   9873 
   9874 bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags)
   9875 {
   9876     return Shortcut(key_chord, flags, ImGuiKeyOwner_Any);
   9877 }
   9878 
   9879 bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
   9880 {
   9881     ImGuiContext& g = *GImGui;
   9882     //IMGUI_DEBUG_LOG("Shortcut(%s, flags=%X, owner_id=0x%08X)\n", GetKeyChordName(key_chord, g.TempBuffer.Data, g.TempBuffer.Size), flags, owner_id);
   9883 
   9884     // When using (owner_id == 0/Any): SetShortcutRouting() will use CurrentFocusScopeId and filter with this, so IsKeyPressed() is fine with he 0/Any.
   9885     if ((flags & ImGuiInputFlags_RouteTypeMask_) == 0)
   9886         flags |= ImGuiInputFlags_RouteFocused;
   9887 
   9888     // Using 'owner_id == ImGuiKeyOwner_Any/0': auto-assign an owner based on current focus scope (each window has its focus scope by default)
   9889     // Effectively makes Shortcut() always input-owner aware.
   9890     if (owner_id == ImGuiKeyOwner_Any || owner_id == ImGuiKeyOwner_NoOwner)
   9891         owner_id = GetRoutingIdFromOwnerId(owner_id);
   9892 
   9893     if (g.CurrentItemFlags & ImGuiItemFlags_Disabled)
   9894         return false;
   9895 
   9896     // Submit route
   9897     if (!SetShortcutRouting(key_chord, flags, owner_id))
   9898         return false;
   9899 
   9900     // Default repeat behavior for Shortcut()
   9901     // So e.g. pressing Ctrl+W and releasing Ctrl while holding W will not trigger the W shortcut.
   9902     if ((flags & ImGuiInputFlags_Repeat) != 0 && (flags & ImGuiInputFlags_RepeatUntilMask_) == 0)
   9903         flags |= ImGuiInputFlags_RepeatUntilKeyModsChange;
   9904 
   9905     if (!IsKeyChordPressed(key_chord, flags, owner_id))
   9906         return false;
   9907 
   9908     // Claim mods during the press
   9909     SetKeyOwnersForKeyChord(key_chord & ImGuiMod_Mask_, owner_id);
   9910 
   9911     IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByShortcut) == 0); // Passing flags not supported by this function!
   9912     return true;
   9913 }
   9914 
   9915 
   9916 //-----------------------------------------------------------------------------
   9917 // [SECTION] ERROR CHECKING
   9918 //-----------------------------------------------------------------------------
   9919 
   9920 // Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues.
   9921 // Called by IMGUI_CHECKVERSION().
   9922 // Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
   9923 // If this triggers you have mismatched headers and compiled code versions.
   9924 // - It could be because of a build issue (using new headers with old compiled code)
   9925 // - It could be because of mismatched configuration #define, compilation settings, packing pragma etc.
   9926 //   THE CONFIGURATION SETTINGS MENTIONED IN imconfig.h MUST BE SET FOR ALL COMPILATION UNITS INVOLVED WITH DEAR IMGUI.
   9927 //   Which is why it is required you put them in your imconfig file (and NOT only before including imgui.h).
   9928 //   Otherwise it is possible that different compilation units would see different structure layout.
   9929 //   If you don't want to modify imconfig.h you can use the IMGUI_USER_CONFIG define to change filename.
   9930 bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert, size_t sz_idx)
   9931 {
   9932     bool error = false;
   9933     if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); }
   9934     if (sz_io    != sizeof(ImGuiIO))    { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
   9935     if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
   9936     if (sz_vec2  != sizeof(ImVec2))     { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
   9937     if (sz_vec4  != sizeof(ImVec4))     { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
   9938     if (sz_vert  != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
   9939     if (sz_idx   != sizeof(ImDrawIdx))  { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); }
   9940     return !error;
   9941 }
   9942 
   9943 // Until 1.89 (IMGUI_VERSION_NUM < 18814) it was legal to use SetCursorPos() to extend the boundary of a parent (e.g. window or table cell)
   9944 // This is causing issues and ambiguity and we need to retire that.
   9945 // See https://github.com/ocornut/imgui/issues/5548 for more details.
   9946 // [Scenario 1]
   9947 //  Previously this would make the window content size ~200x200:
   9948 //    Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End();  // NOT OK
   9949 //  Instead, please submit an item:
   9950 //    Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End(); // OK
   9951 //  Alternative:
   9952 //    Begin(...) + Dummy(ImVec2(200,200)) + End(); // OK
   9953 // [Scenario 2]
   9954 //  For reference this is one of the issue what we aim to fix with this change:
   9955 //    BeginGroup() + SomeItem("foobar") + SetCursorScreenPos(GetCursorScreenPos()) + EndGroup()
   9956 //  The previous logic made SetCursorScreenPos(GetCursorScreenPos()) have a side-effect! It would erroneously incorporate ItemSpacing.y after the item into content size, making the group taller!
   9957 //  While this code is a little twisted, no-one would expect SetXXX(GetXXX()) to have a side-effect. Using vertical alignment patterns could trigger this issue.
   9958 void ImGui::ErrorCheckUsingSetCursorPosToExtendParentBoundaries()
   9959 {
   9960     ImGuiContext& g = *GImGui;
   9961     ImGuiWindow* window = g.CurrentWindow;
   9962     IM_ASSERT(window->DC.IsSetPos);
   9963     window->DC.IsSetPos = false;
   9964 #ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
   9965     if (window->DC.CursorPos.x <= window->DC.CursorMaxPos.x && window->DC.CursorPos.y <= window->DC.CursorMaxPos.y)
   9966         return;
   9967     if (window->SkipItems)
   9968         return;
   9969     IM_ASSERT(0 && "Code uses SetCursorPos()/SetCursorScreenPos() to extend window/parent boundaries. Please submit an item e.g. Dummy() to validate extent.");
   9970 #else
   9971     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
   9972 #endif
   9973 }
   9974 
   9975 static void ImGui::ErrorCheckNewFrameSanityChecks()
   9976 {
   9977     ImGuiContext& g = *GImGui;
   9978 
   9979     // Check user IM_ASSERT macro
   9980     // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means your assert macro is incorrectly defined!
   9981     //  If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block.
   9982     //  This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.)
   9983     // #define IM_ASSERT(EXPR)   if (SomeCode(EXPR)) SomeMoreCode();                    // Wrong!
   9984     // #define IM_ASSERT(EXPR)   do { if (SomeCode(EXPR)) SomeMoreCode(); } while (0)   // Correct!
   9985     if (true) IM_ASSERT(1); else IM_ASSERT(0);
   9986 
   9987     // Emscripten backends are often imprecise in their submission of DeltaTime. (#6114, #3644)
   9988     // Ideally the Emscripten app/backend should aim to fix or smooth this value and avoid feeding zero, but we tolerate it.
   9989 #ifdef __EMSCRIPTEN__
   9990     if (g.IO.DeltaTime <= 0.0f && g.FrameCount > 0)
   9991         g.IO.DeltaTime = 0.00001f;
   9992 #endif
   9993 
   9994     // Check user data
   9995     // (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)
   9996     IM_ASSERT(g.Initialized);
   9997     IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0)              && "Need a positive DeltaTime!");
   9998     IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount)  && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
   9999     IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f  && "Invalid DisplaySize value!");
  10000     IM_ASSERT(g.IO.Fonts->IsBuilt()                                     && "Font Atlas not built! Make sure you called ImGui_ImplXXXX_NewFrame() function for renderer backend, which should call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()");
  10001     IM_ASSERT(g.Style.CurveTessellationTol > 0.0f                       && "Invalid style setting!");
  10002     IM_ASSERT(g.Style.CircleTessellationMaxError > 0.0f                 && "Invalid style setting!");
  10003     IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f            && "Invalid style setting!"); // Allows us to avoid a few clamps in color computations
  10004     IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting.");
  10005     IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
  10006     IM_ASSERT(g.Style.ColorButtonPosition == ImGuiDir_Left || g.Style.ColorButtonPosition == ImGuiDir_Right);
  10007 #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
  10008     for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_COUNT; n++)
  10009         IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < ImGuiKey_LegacyNativeKey_END && "io.KeyMap[] contains an out of bound value (need to be 0..511, or -1 for unmapped key)");
  10010 
  10011     // Check: required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only added in 1.60 WIP)
  10012     if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && g.IO.BackendUsingLegacyKeyArrays == 1)
  10013         IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
  10014 #endif
  10015 }
  10016 
  10017 static void ImGui::ErrorCheckEndFrameSanityChecks()
  10018 {
  10019     ImGuiContext& g = *GImGui;
  10020 
  10021     // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame()
  10022     // One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame().
  10023     // It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will
  10024     // send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs.
  10025     // We silently accommodate for this case by ignoring the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0),
  10026     // while still correctly asserting on mid-frame key press events.
  10027     const ImGuiKeyChord key_mods = GetMergedModsFromKeys();
  10028     IM_ASSERT((key_mods == 0 || g.IO.KeyMods == key_mods) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
  10029     IM_UNUSED(key_mods);
  10030 
  10031     // [EXPERIMENTAL] Recover from errors: You may call this yourself before EndFrame().
  10032     //ErrorCheckEndFrameRecover();
  10033 
  10034     // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you
  10035     // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API).
  10036     if (g.CurrentWindowStack.Size != 1)
  10037     {
  10038         if (g.CurrentWindowStack.Size > 1)
  10039         {
  10040             ImGuiWindow* window = g.CurrentWindowStack.back().Window; // <-- This window was not Ended!
  10041             IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?");
  10042             IM_UNUSED(window);
  10043             while (g.CurrentWindowStack.Size > 1)
  10044                 End();
  10045         }
  10046         else
  10047         {
  10048             IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?");
  10049         }
  10050     }
  10051 
  10052     IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, "Missing EndGroup call!");
  10053 }
  10054 
  10055 // Experimental recovery from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.
  10056 // Must be called during or before EndFrame().
  10057 // This is generally flawed as we are not necessarily End/Popping things in the right order.
  10058 // FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.
  10059 // FIXME: Can't recover from interleaved BeginTabBar/Begin
  10060 void    ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data)
  10061 {
  10062     // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations"
  10063     ImGuiContext& g = *GImGui;
  10064     while (g.CurrentWindowStack.Size > 0) //-V1044
  10065     {
  10066         ErrorCheckEndWindowRecover(log_callback, user_data);
  10067         ImGuiWindow* window = g.CurrentWindow;
  10068         if (g.CurrentWindowStack.Size == 1)
  10069         {
  10070             IM_ASSERT(window->IsFallbackWindow);
  10071             break;
  10072         }
  10073         if (window->Flags & ImGuiWindowFlags_ChildWindow)
  10074         {
  10075             if (log_callback) log_callback(user_data, "Recovered from missing EndChild() for '%s'", window->Name);
  10076             EndChild();
  10077         }
  10078         else
  10079         {
  10080             if (log_callback) log_callback(user_data, "Recovered from missing End() for '%s'", window->Name);
  10081             End();
  10082         }
  10083     }
  10084 }
  10085 
  10086 // Must be called before End()/EndChild()
  10087 void    ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data)
  10088 {
  10089     ImGuiContext& g = *GImGui;
  10090     while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow))
  10091     {
  10092         if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name);
  10093         EndTable();
  10094     }
  10095 
  10096     ImGuiWindow* window = g.CurrentWindow;
  10097     ImGuiStackSizes* stack_sizes = &g.CurrentWindowStack.back().StackSizesOnBegin;
  10098     IM_ASSERT(window != NULL);
  10099     while (g.CurrentTabBar != NULL) //-V1044
  10100     {
  10101         if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name);
  10102         EndTabBar();
  10103     }
  10104     while (g.CurrentMultiSelect != NULL && g.CurrentMultiSelect->Storage->Window == window)
  10105     {
  10106         if (log_callback) log_callback(user_data, "Recovered from missing EndMultiSelect() in '%s'", window->Name);
  10107         EndMultiSelect();
  10108     }
  10109     while (window->DC.TreeDepth > 0)
  10110     {
  10111         if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name);
  10112         TreePop();
  10113     }
  10114     while (g.GroupStack.Size > stack_sizes->SizeOfGroupStack) //-V1044
  10115     {
  10116         if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name);
  10117         EndGroup();
  10118     }
  10119     while (window->IDStack.Size > 1)
  10120     {
  10121         if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name);
  10122         PopID();
  10123     }
  10124     while (g.DisabledStackSize > stack_sizes->SizeOfDisabledStack) //-V1044
  10125     {
  10126         if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'", window->Name);
  10127         if (g.CurrentItemFlags & ImGuiItemFlags_Disabled)
  10128             EndDisabled();
  10129         else
  10130         {
  10131             EndDisabledOverrideReenable();
  10132             g.CurrentWindowStack.back().DisabledOverrideReenable = false;
  10133         }
  10134     }
  10135     while (g.ColorStack.Size > stack_sizes->SizeOfColorStack)
  10136     {
  10137         if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col));
  10138         PopStyleColor();
  10139     }
  10140     while (g.ItemFlagsStack.Size > stack_sizes->SizeOfItemFlagsStack) //-V1044
  10141     {
  10142         if (log_callback) log_callback(user_data, "Recovered from missing PopItemFlag() in '%s'", window->Name);
  10143         PopItemFlag();
  10144     }
  10145     while (g.StyleVarStack.Size > stack_sizes->SizeOfStyleVarStack) //-V1044
  10146     {
  10147         if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name);
  10148         PopStyleVar();
  10149     }
  10150     while (g.FontStack.Size > stack_sizes->SizeOfFontStack) //-V1044
  10151     {
  10152         if (log_callback) log_callback(user_data, "Recovered from missing PopFont() in '%s'", window->Name);
  10153         PopFont();
  10154     }
  10155     while (g.FocusScopeStack.Size > stack_sizes->SizeOfFocusScopeStack + 1) //-V1044
  10156     {
  10157         if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name);
  10158         PopFocusScope();
  10159     }
  10160 }
  10161 
  10162 // Save current stack sizes for later compare
  10163 void ImGuiStackSizes::SetToContextState(ImGuiContext* ctx)
  10164 {
  10165     ImGuiContext& g = *ctx;
  10166     ImGuiWindow* window = g.CurrentWindow;
  10167     SizeOfIDStack = (short)window->IDStack.Size;
  10168     SizeOfColorStack = (short)g.ColorStack.Size;
  10169     SizeOfStyleVarStack = (short)g.StyleVarStack.Size;
  10170     SizeOfFontStack = (short)g.FontStack.Size;
  10171     SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size;
  10172     SizeOfGroupStack = (short)g.GroupStack.Size;
  10173     SizeOfItemFlagsStack = (short)g.ItemFlagsStack.Size;
  10174     SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size;
  10175     SizeOfDisabledStack = (short)g.DisabledStackSize;
  10176 }
  10177 
  10178 // Compare to detect usage errors
  10179 void ImGuiStackSizes::CompareWithContextState(ImGuiContext* ctx)
  10180 {
  10181     ImGuiContext& g = *ctx;
  10182     ImGuiWindow* window = g.CurrentWindow;
  10183     IM_UNUSED(window);
  10184 
  10185     // Window stacks
  10186     // NOT checking: DC.ItemWidth, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin)
  10187     IM_ASSERT(SizeOfIDStack         == window->IDStack.Size     && "PushID/PopID or TreeNode/TreePop Mismatch!");
  10188 
  10189     // Global stacks
  10190     // 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.
  10191     IM_ASSERT(SizeOfGroupStack      == g.GroupStack.Size        && "BeginGroup/EndGroup Mismatch!");
  10192     IM_ASSERT(SizeOfBeginPopupStack == g.BeginPopupStack.Size   && "BeginPopup/EndPopup or BeginMenu/EndMenu Mismatch!");
  10193     IM_ASSERT(SizeOfDisabledStack   == g.DisabledStackSize      && "BeginDisabled/EndDisabled Mismatch!");
  10194     IM_ASSERT(SizeOfItemFlagsStack  >= g.ItemFlagsStack.Size    && "PushItemFlag/PopItemFlag Mismatch!");
  10195     IM_ASSERT(SizeOfColorStack      >= g.ColorStack.Size        && "PushStyleColor/PopStyleColor Mismatch!");
  10196     IM_ASSERT(SizeOfStyleVarStack   >= g.StyleVarStack.Size     && "PushStyleVar/PopStyleVar Mismatch!");
  10197     IM_ASSERT(SizeOfFontStack       >= g.FontStack.Size         && "PushFont/PopFont Mismatch!");
  10198     IM_ASSERT(SizeOfFocusScopeStack == g.FocusScopeStack.Size   && "PushFocusScope/PopFocusScope Mismatch!");
  10199 }
  10200 
  10201 //-----------------------------------------------------------------------------
  10202 // [SECTION] ITEM SUBMISSION
  10203 //-----------------------------------------------------------------------------
  10204 // - KeepAliveID()
  10205 // - ItemAdd()
  10206 //-----------------------------------------------------------------------------
  10207 
  10208 // Code not using ItemAdd() may need to call this manually otherwise ActiveId will be cleared. In IMGUI_VERSION_NUM < 18717 this was called by GetID().
  10209 void ImGui::KeepAliveID(ImGuiID id)
  10210 {
  10211     ImGuiContext& g = *GImGui;
  10212     if (g.ActiveId == id)
  10213         g.ActiveIdIsAlive = id;
  10214     if (g.ActiveIdPreviousFrame == id)
  10215         g.ActiveIdPreviousFrameIsAlive = true;
  10216 }
  10217 
  10218 // Declare item bounding box for clipping and interaction.
  10219 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
  10220 // declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction.
  10221 // THIS IS IN THE PERFORMANCE CRITICAL PATH (UNTIL THE CLIPPING TEST AND EARLY-RETURN)
  10222 IM_MSVC_RUNTIME_CHECKS_OFF
  10223 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemFlags extra_flags)
  10224 {
  10225     ImGuiContext& g = *GImGui;
  10226     ImGuiWindow* window = g.CurrentWindow;
  10227 
  10228     // Set item data
  10229     // (DisplayRect is left untouched, made valid when ImGuiItemStatusFlags_HasDisplayRect is set)
  10230     g.LastItemData.ID = id;
  10231     g.LastItemData.Rect = bb;
  10232     g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb;
  10233     g.LastItemData.InFlags = g.CurrentItemFlags | g.NextItemData.ItemFlags | extra_flags;
  10234     g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None;
  10235     // Note: we don't copy 'g.NextItemData.SelectionUserData' to an hypothetical g.LastItemData.SelectionUserData: since the former is not cleared.
  10236 
  10237     if (id != 0)
  10238     {
  10239         KeepAliveID(id);
  10240 
  10241         // Directional navigation processing
  10242         // Runs prior to clipping early-out
  10243         //  (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
  10244         //  (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
  10245         //      unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
  10246         //      thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
  10247         //      We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
  10248         //      to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
  10249         // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
  10250         // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
  10251         if (!(g.LastItemData.InFlags & ImGuiItemFlags_NoNav))
  10252         {
  10253             // FIMXE-NAV: investigate changing the window tests into a simple 'if (g.NavFocusScopeId == g.CurrentFocusScopeId)' test.
  10254             window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent);
  10255             if (g.NavId == id || g.NavAnyRequest)
  10256                 if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
  10257                     if (window == g.NavWindow || ((window->ChildFlags | g.NavWindow->ChildFlags) & ImGuiChildFlags_NavFlattened))
  10258                         NavProcessItem();
  10259         }
  10260 
  10261         if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasShortcut)
  10262             ItemHandleShortcut(id);
  10263     }
  10264 
  10265     // Lightweight clear of SetNextItemXXX data.
  10266     g.NextItemData.Flags = ImGuiNextItemDataFlags_None;
  10267     g.NextItemData.ItemFlags = ImGuiItemFlags_None;
  10268 
  10269 #ifdef IMGUI_ENABLE_TEST_ENGINE
  10270     if (id != 0)
  10271         IMGUI_TEST_ENGINE_ITEM_ADD(id, g.LastItemData.NavRect, &g.LastItemData);
  10272 #endif
  10273 
  10274     // Clipping test
  10275     // (this is an inline copy of IsClippedEx() so we can reuse the is_rect_visible value, otherwise we'd do 'if (IsClippedEx(bb, id)) return false')
  10276     // g.NavActivateId is not necessarily == g.NavId, in the case of remote activation (e.g. shortcuts)
  10277     const bool is_rect_visible = bb.Overlaps(window->ClipRect);
  10278     if (!is_rect_visible)
  10279         if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId))
  10280             if (!g.ItemUnclipByLog)
  10281                 return false;
  10282 
  10283     // [DEBUG]
  10284 #ifndef IMGUI_DISABLE_DEBUG_TOOLS
  10285     if (id != 0)
  10286     {
  10287         if (id == g.DebugLocateId)
  10288             DebugLocateItemResolveWithLastItem();
  10289 
  10290         // [DEBUG] People keep stumbling on this problem and using "" as identifier in the root of a window instead of "##something".
  10291         // Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use "##something".
  10292         // READ THE FAQ: https://dearimgui.com/faq
  10293         IM_ASSERT(id != window->ID && "Cannot have an empty ID at the root of a window. If you need an empty label, use ## and read the FAQ about how the ID Stack works!");
  10294     }
  10295     //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
  10296     //if ((g.LastItemData.InFlags & ImGuiItemFlags_NoNav) == 0)
  10297     //    window->DrawList->AddRect(g.LastItemData.NavRect.Min, g.LastItemData.NavRect.Max, IM_COL32(255,255,0,255)); // [DEBUG]
  10298 #endif
  10299 
  10300     // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
  10301     if (is_rect_visible)
  10302         g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Visible;
  10303     if (IsMouseHoveringRect(bb.Min, bb.Max))
  10304         g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect;
  10305     return true;
  10306 }
  10307 IM_MSVC_RUNTIME_CHECKS_RESTORE
  10308 
  10309 //-----------------------------------------------------------------------------
  10310 // [SECTION] LAYOUT
  10311 //-----------------------------------------------------------------------------
  10312 // - ItemSize()
  10313 // - SameLine()
  10314 // - GetCursorScreenPos()
  10315 // - SetCursorScreenPos()
  10316 // - GetCursorPos(), GetCursorPosX(), GetCursorPosY()
  10317 // - SetCursorPos(), SetCursorPosX(), SetCursorPosY()
  10318 // - GetCursorStartPos()
  10319 // - Indent()
  10320 // - Unindent()
  10321 // - SetNextItemWidth()
  10322 // - PushItemWidth()
  10323 // - PushMultiItemsWidths()
  10324 // - PopItemWidth()
  10325 // - CalcItemWidth()
  10326 // - CalcItemSize()
  10327 // - GetTextLineHeight()
  10328 // - GetTextLineHeightWithSpacing()
  10329 // - GetFrameHeight()
  10330 // - GetFrameHeightWithSpacing()
  10331 // - GetContentRegionMax()
  10332 // - GetContentRegionAvail(),
  10333 // - BeginGroup()
  10334 // - EndGroup()
  10335 // Also see in imgui_widgets: tab bars, and in imgui_tables: tables, columns.
  10336 //-----------------------------------------------------------------------------
  10337 
  10338 // Advance cursor given item size for layout.
  10339 // Register minimum needed size so it can extend the bounding box used for auto-fit calculation.
  10340 // See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different.
  10341 // THIS IS IN THE PERFORMANCE CRITICAL PATH.
  10342 IM_MSVC_RUNTIME_CHECKS_OFF
  10343 void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
  10344 {
  10345     ImGuiContext& g = *GImGui;
  10346     ImGuiWindow* window = g.CurrentWindow;
  10347     if (window->SkipItems)
  10348         return;
  10349 
  10350     // We increase the height in this function to accommodate for baseline offset.
  10351     // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
  10352     // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
  10353     const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
  10354 
  10355     const float line_y1 = window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y;
  10356     const float line_height = ImMax(window->DC.CurrLineSize.y, /*ImMax(*/window->DC.CursorPos.y - line_y1/*, 0.0f)*/ + size.y + offset_to_match_baseline_y);
  10357 
  10358     // Always align ourselves on pixel boundaries
  10359     //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]
  10360     window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
  10361     window->DC.CursorPosPrevLine.y = line_y1;
  10362     window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);    // Next line
  10363     window->DC.CursorPos.y = IM_TRUNC(line_y1 + line_height + g.Style.ItemSpacing.y);                       // Next line
  10364     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
  10365     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
  10366     //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
  10367 
  10368     window->DC.PrevLineSize.y = line_height;
  10369     window->DC.CurrLineSize.y = 0.0f;
  10370     window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
  10371     window->DC.CurrLineTextBaseOffset = 0.0f;
  10372     window->DC.IsSameLine = window->DC.IsSetPos = false;
  10373 
  10374     // Horizontal layout mode
  10375     if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
  10376         SameLine();
  10377 }
  10378 IM_MSVC_RUNTIME_CHECKS_RESTORE
  10379 
  10380 // Gets back to previous line and continue with horizontal layout
  10381 //      offset_from_start_x == 0 : follow right after previous item
  10382 //      offset_from_start_x != 0 : align to specified x position (relative to window/group left)
  10383 //      spacing_w < 0            : use default spacing if offset_from_start_x == 0, no spacing if offset_from_start_x != 0
  10384 //      spacing_w >= 0           : enforce spacing amount
  10385 void ImGui::SameLine(float offset_from_start_x, float spacing_w)
  10386 {
  10387     ImGuiContext& g = *GImGui;
  10388     ImGuiWindow* window = g.CurrentWindow;
  10389     if (window->SkipItems)
  10390         return;
  10391 
  10392     if (offset_from_start_x != 0.0f)
  10393     {
  10394         if (spacing_w < 0.0f)
  10395             spacing_w = 0.0f;
  10396         window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
  10397         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
  10398     }
  10399     else
  10400     {
  10401         if (spacing_w < 0.0f)
  10402             spacing_w = g.Style.ItemSpacing.x;
  10403         window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
  10404         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
  10405     }
  10406     window->DC.CurrLineSize = window->DC.PrevLineSize;
  10407     window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
  10408     window->DC.IsSameLine = true;
  10409 }
  10410 
  10411 ImVec2 ImGui::GetCursorScreenPos()
  10412 {
  10413     ImGuiWindow* window = GetCurrentWindowRead();
  10414     return window->DC.CursorPos;
  10415 }
  10416 
  10417 void ImGui::SetCursorScreenPos(const ImVec2& pos)
  10418 {
  10419     ImGuiWindow* window = GetCurrentWindow();
  10420     window->DC.CursorPos = pos;
  10421     //window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
  10422     window->DC.IsSetPos = true;
  10423 }
  10424 
  10425 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
  10426 // 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'.
  10427 ImVec2 ImGui::GetCursorPos()
  10428 {
  10429     ImGuiWindow* window = GetCurrentWindowRead();
  10430     return window->DC.CursorPos - window->Pos + window->Scroll;
  10431 }
  10432 
  10433 float ImGui::GetCursorPosX()
  10434 {
  10435     ImGuiWindow* window = GetCurrentWindowRead();
  10436     return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
  10437 }
  10438 
  10439 float ImGui::GetCursorPosY()
  10440 {
  10441     ImGuiWindow* window = GetCurrentWindowRead();
  10442     return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
  10443 }
  10444 
  10445 void ImGui::SetCursorPos(const ImVec2& local_pos)
  10446 {
  10447     ImGuiWindow* window = GetCurrentWindow();
  10448     window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
  10449     //window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
  10450     window->DC.IsSetPos = true;
  10451 }
  10452 
  10453 void ImGui::SetCursorPosX(float x)
  10454 {
  10455     ImGuiWindow* window = GetCurrentWindow();
  10456     window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
  10457     //window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
  10458     window->DC.IsSetPos = true;
  10459 }
  10460 
  10461 void ImGui::SetCursorPosY(float y)
  10462 {
  10463     ImGuiWindow* window = GetCurrentWindow();
  10464     window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
  10465     //window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
  10466     window->DC.IsSetPos = true;
  10467 }
  10468 
  10469 ImVec2 ImGui::GetCursorStartPos()
  10470 {
  10471     ImGuiWindow* window = GetCurrentWindowRead();
  10472     return window->DC.CursorStartPos - window->Pos;
  10473 }
  10474 
  10475 void ImGui::Indent(float indent_w)
  10476 {
  10477     ImGuiContext& g = *GImGui;
  10478     ImGuiWindow* window = GetCurrentWindow();
  10479     window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
  10480     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
  10481 }
  10482 
  10483 void ImGui::Unindent(float indent_w)
  10484 {
  10485     ImGuiContext& g = *GImGui;
  10486     ImGuiWindow* window = GetCurrentWindow();
  10487     window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
  10488     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
  10489 }
  10490 
  10491 // Affect large frame+labels widgets only.
  10492 void ImGui::SetNextItemWidth(float item_width)
  10493 {
  10494     ImGuiContext& g = *GImGui;
  10495     g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasWidth;
  10496     g.NextItemData.Width = item_width;
  10497 }
  10498 
  10499 // FIXME: Remove the == 0.0f behavior?
  10500 void ImGui::PushItemWidth(float item_width)
  10501 {
  10502     ImGuiContext& g = *GImGui;
  10503     ImGuiWindow* window = g.CurrentWindow;
  10504     window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width
  10505     window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
  10506     g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
  10507 }
  10508 
  10509 void ImGui::PushMultiItemsWidths(int components, float w_full)
  10510 {
  10511     ImGuiContext& g = *GImGui;
  10512     ImGuiWindow* window = g.CurrentWindow;
  10513     IM_ASSERT(components > 0);
  10514     const ImGuiStyle& style = g.Style;
  10515     window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width
  10516     float w_items = w_full - style.ItemInnerSpacing.x * (components - 1);
  10517     float prev_split = w_items;
  10518     for (int i = components - 1; i > 0; i--)
  10519     {
  10520         float next_split = IM_TRUNC(w_items * i / components);
  10521         window->DC.ItemWidthStack.push_back(ImMax(prev_split - next_split, 1.0f));
  10522         prev_split = next_split;
  10523     }
  10524     window->DC.ItemWidth = ImMax(prev_split, 1.0f);
  10525     g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
  10526 }
  10527 
  10528 void ImGui::PopItemWidth()
  10529 {
  10530     ImGuiWindow* window = GetCurrentWindow();
  10531     window->DC.ItemWidth = window->DC.ItemWidthStack.back();
  10532     window->DC.ItemWidthStack.pop_back();
  10533 }
  10534 
  10535 // Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().
  10536 // The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()
  10537 float ImGui::CalcItemWidth()
  10538 {
  10539     ImGuiContext& g = *GImGui;
  10540     ImGuiWindow* window = g.CurrentWindow;
  10541     float w;
  10542     if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth)
  10543         w = g.NextItemData.Width;
  10544     else
  10545         w = window->DC.ItemWidth;
  10546     if (w < 0.0f)
  10547     {
  10548         float region_avail_x = GetContentRegionAvail().x;
  10549         w = ImMax(1.0f, region_avail_x + w);
  10550     }
  10551     w = IM_TRUNC(w);
  10552     return w;
  10553 }
  10554 
  10555 // [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().
  10556 // Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.
  10557 // Note that only CalcItemWidth() is publicly exposed.
  10558 // The 4.0f here may be changed to match CalcItemWidth() and/or BeginChild() (right now we have a mismatch which is harmless but undesirable)
  10559 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)
  10560 {
  10561     ImVec2 avail;
  10562     if (size.x < 0.0f || size.y < 0.0f)
  10563         avail = GetContentRegionAvail();
  10564 
  10565     if (size.x == 0.0f)
  10566         size.x = default_w;
  10567     else if (size.x < 0.0f)
  10568         size.x = ImMax(4.0f, avail.x + size.x); // <-- size.x is negative here so we are subtracting
  10569 
  10570     if (size.y == 0.0f)
  10571         size.y = default_h;
  10572     else if (size.y < 0.0f)
  10573         size.y = ImMax(4.0f, avail.y + size.y); // <-- size.y is negative here so we are subtracting
  10574 
  10575     return size;
  10576 }
  10577 
  10578 float ImGui::GetTextLineHeight()
  10579 {
  10580     ImGuiContext& g = *GImGui;
  10581     return g.FontSize;
  10582 }
  10583 
  10584 float ImGui::GetTextLineHeightWithSpacing()
  10585 {
  10586     ImGuiContext& g = *GImGui;
  10587     return g.FontSize + g.Style.ItemSpacing.y;
  10588 }
  10589 
  10590 float ImGui::GetFrameHeight()
  10591 {
  10592     ImGuiContext& g = *GImGui;
  10593     return g.FontSize + g.Style.FramePadding.y * 2.0f;
  10594 }
  10595 
  10596 float ImGui::GetFrameHeightWithSpacing()
  10597 {
  10598     ImGuiContext& g = *GImGui;
  10599     return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
  10600 }
  10601 
  10602 ImVec2 ImGui::GetContentRegionAvail()
  10603 {
  10604     ImGuiContext& g = *GImGui;
  10605     ImGuiWindow* window = g.CurrentWindow;
  10606     ImVec2 mx = (window->DC.CurrentColumns || g.CurrentTable) ? window->WorkRect.Max : window->ContentRegionRect.Max;
  10607     return mx - window->DC.CursorPos;
  10608 }
  10609 
  10610 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
  10611 
  10612 // You should never need those functions. Always use GetCursorScreenPos() and GetContentRegionAvail()!
  10613 // They are bizarre local-coordinates which don't play well with scrolling.
  10614 ImVec2 ImGui::GetContentRegionMax()
  10615 {
  10616     return GetContentRegionAvail() + GetCursorScreenPos() - GetWindowPos();
  10617 }
  10618 
  10619 ImVec2 ImGui::GetWindowContentRegionMin()
  10620 {
  10621     ImGuiWindow* window = GImGui->CurrentWindow;
  10622     return window->ContentRegionRect.Min - window->Pos;
  10623 }
  10624 
  10625 ImVec2 ImGui::GetWindowContentRegionMax()
  10626 {
  10627     ImGuiWindow* window = GImGui->CurrentWindow;
  10628     return window->ContentRegionRect.Max - window->Pos;
  10629 }
  10630 #endif
  10631 
  10632 // 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.)
  10633 // Groups are currently a mishmash of functionalities which should perhaps be clarified and separated.
  10634 // FIXME-OPT: Could we safely early out on ->SkipItems?
  10635 void ImGui::BeginGroup()
  10636 {
  10637     ImGuiContext& g = *GImGui;
  10638     ImGuiWindow* window = g.CurrentWindow;
  10639 
  10640     g.GroupStack.resize(g.GroupStack.Size + 1);
  10641     ImGuiGroupData& group_data = g.GroupStack.back();
  10642     group_data.WindowID = window->ID;
  10643     group_data.BackupCursorPos = window->DC.CursorPos;
  10644     group_data.BackupCursorPosPrevLine = window->DC.CursorPosPrevLine;
  10645     group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
  10646     group_data.BackupIndent = window->DC.Indent;
  10647     group_data.BackupGroupOffset = window->DC.GroupOffset;
  10648     group_data.BackupCurrLineSize = window->DC.CurrLineSize;
  10649     group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;
  10650     group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
  10651     group_data.BackupHoveredIdIsAlive = g.HoveredId != 0;
  10652     group_data.BackupIsSameLine = window->DC.IsSameLine;
  10653     group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
  10654     group_data.EmitItem = true;
  10655 
  10656     window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
  10657     window->DC.Indent = window->DC.GroupOffset;
  10658     window->DC.CursorMaxPos = window->DC.CursorPos;
  10659     window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
  10660     if (g.LogEnabled)
  10661         g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
  10662 }
  10663 
  10664 void ImGui::EndGroup()
  10665 {
  10666     ImGuiContext& g = *GImGui;
  10667     ImGuiWindow* window = g.CurrentWindow;
  10668     IM_ASSERT(g.GroupStack.Size > 0); // Mismatched BeginGroup()/EndGroup() calls
  10669 
  10670     ImGuiGroupData& group_data = g.GroupStack.back();
  10671     IM_ASSERT(group_data.WindowID == window->ID); // EndGroup() in wrong window?
  10672 
  10673     if (window->DC.IsSetPos)
  10674         ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
  10675 
  10676     // Include LastItemData.Rect.Max as a workaround for e.g. EndTable() undershooting with CursorMaxPos report. (#7543)
  10677     ImRect group_bb(group_data.BackupCursorPos, ImMax(ImMax(window->DC.CursorMaxPos, g.LastItemData.Rect.Max), group_data.BackupCursorPos));
  10678     window->DC.CursorPos = group_data.BackupCursorPos;
  10679     window->DC.CursorPosPrevLine = group_data.BackupCursorPosPrevLine;
  10680     window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, group_bb.Max);
  10681     window->DC.Indent = group_data.BackupIndent;
  10682     window->DC.GroupOffset = group_data.BackupGroupOffset;
  10683     window->DC.CurrLineSize = group_data.BackupCurrLineSize;
  10684     window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;
  10685     window->DC.IsSameLine = group_data.BackupIsSameLine;
  10686     if (g.LogEnabled)
  10687         g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
  10688 
  10689     if (!group_data.EmitItem)
  10690     {
  10691         g.GroupStack.pop_back();
  10692         return;
  10693     }
  10694 
  10695     window->DC.CurrLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now.
  10696     ItemSize(group_bb.GetSize());
  10697     ItemAdd(group_bb, 0, NULL, ImGuiItemFlags_NoTabStop);
  10698 
  10699     // 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.
  10700     // It would be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but would put a little more burden on individual widgets.
  10701     // Also if you grep for LastItemId you'll notice it is only used in that context.
  10702     // (The two tests not the same because ActiveIdIsAlive is an ID itself, in order to be able to handle ActiveId being overwritten during the frame.)
  10703     const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId;
  10704     const bool group_contains_prev_active_id = (group_data.BackupActiveIdPreviousFrameIsAlive == false) && (g.ActiveIdPreviousFrameIsAlive == true);
  10705     if (group_contains_curr_active_id)
  10706         g.LastItemData.ID = g.ActiveId;
  10707     else if (group_contains_prev_active_id)
  10708         g.LastItemData.ID = g.ActiveIdPreviousFrame;
  10709     g.LastItemData.Rect = group_bb;
  10710 
  10711     // Forward Hovered flag
  10712     const bool group_contains_curr_hovered_id = (group_data.BackupHoveredIdIsAlive == false) && g.HoveredId != 0;
  10713     if (group_contains_curr_hovered_id)
  10714         g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
  10715 
  10716     // Forward Edited flag
  10717     if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame)
  10718         g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;
  10719 
  10720     // Forward Deactivated flag
  10721     g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDeactivated;
  10722     if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame)
  10723         g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Deactivated;
  10724 
  10725     g.GroupStack.pop_back();
  10726     if (g.DebugShowGroupRects)
  10727         window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255));   // [Debug]
  10728 }
  10729 
  10730 
  10731 //-----------------------------------------------------------------------------
  10732 // [SECTION] SCROLLING
  10733 //-----------------------------------------------------------------------------
  10734 
  10735 // Helper to snap on edges when aiming at an item very close to the edge,
  10736 // So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling.
  10737 // When we refactor the scrolling API this may be configurable with a flag?
  10738 // Note that the effect for this won't be visible on X axis with default Style settings as WindowPadding.x == ItemSpacing.x by default.
  10739 static float CalcScrollEdgeSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio)
  10740 {
  10741     if (target <= snap_min + snap_threshold)
  10742         return ImLerp(snap_min, target, center_ratio);
  10743     if (target >= snap_max - snap_threshold)
  10744         return ImLerp(target, snap_max, center_ratio);
  10745     return target;
  10746 }
  10747 
  10748 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
  10749 {
  10750     ImVec2 scroll = window->Scroll;
  10751     ImVec2 decoration_size(window->DecoOuterSizeX1 + window->DecoInnerSizeX1 + window->DecoOuterSizeX2, window->DecoOuterSizeY1 + window->DecoInnerSizeY1 + window->DecoOuterSizeY2);
  10752     for (int axis = 0; axis < 2; axis++)
  10753     {
  10754         if (window->ScrollTarget[axis] < FLT_MAX)
  10755         {
  10756             float center_ratio = window->ScrollTargetCenterRatio[axis];
  10757             float scroll_target = window->ScrollTarget[axis];
  10758             if (window->ScrollTargetEdgeSnapDist[axis] > 0.0f)
  10759             {
  10760                 float snap_min = 0.0f;
  10761                 float snap_max = window->ScrollMax[axis] + window->SizeFull[axis] - decoration_size[axis];
  10762                 scroll_target = CalcScrollEdgeSnap(scroll_target, snap_min, snap_max, window->ScrollTargetEdgeSnapDist[axis], center_ratio);
  10763             }
  10764             scroll[axis] = scroll_target - center_ratio * (window->SizeFull[axis] - decoration_size[axis]);
  10765         }
  10766         scroll[axis] = IM_ROUND(ImMax(scroll[axis], 0.0f));
  10767         if (!window->Collapsed && !window->SkipItems)
  10768             scroll[axis] = ImMin(scroll[axis], window->ScrollMax[axis]);
  10769     }
  10770     return scroll;
  10771 }
  10772 
  10773 void ImGui::ScrollToItem(ImGuiScrollFlags flags)
  10774 {
  10775     ImGuiContext& g = *GImGui;
  10776     ImGuiWindow* window = g.CurrentWindow;
  10777     ScrollToRectEx(window, g.LastItemData.NavRect, flags);
  10778 }
  10779 
  10780 void ImGui::ScrollToRect(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags)
  10781 {
  10782     ScrollToRectEx(window, item_rect, flags);
  10783 }
  10784 
  10785 // Scroll to keep newly navigated item fully into view
  10786 ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags)
  10787 {
  10788     ImGuiContext& g = *GImGui;
  10789     ImRect scroll_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));
  10790     scroll_rect.Min.x = ImMin(scroll_rect.Min.x + window->DecoInnerSizeX1, scroll_rect.Max.x);
  10791     scroll_rect.Min.y = ImMin(scroll_rect.Min.y + window->DecoInnerSizeY1, scroll_rect.Max.y);
  10792     //GetForegroundDrawList(window)->AddRect(item_rect.Min, item_rect.Max, IM_COL32(255,0,0,255), 0.0f, 0, 5.0f); // [DEBUG]
  10793     //GetForegroundDrawList(window)->AddRect(scroll_rect.Min, scroll_rect.Max, IM_COL32_WHITE); // [DEBUG]
  10794 
  10795     // Check that only one behavior is selected per axis
  10796     IM_ASSERT((flags & ImGuiScrollFlags_MaskX_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskX_));
  10797     IM_ASSERT((flags & ImGuiScrollFlags_MaskY_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskY_));
  10798 
  10799     // Defaults
  10800     ImGuiScrollFlags in_flags = flags;
  10801     if ((flags & ImGuiScrollFlags_MaskX_) == 0 && window->ScrollbarX)
  10802         flags |= ImGuiScrollFlags_KeepVisibleEdgeX;
  10803     if ((flags & ImGuiScrollFlags_MaskY_) == 0)
  10804         flags |= window->Appearing ? ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeY;
  10805 
  10806     const bool fully_visible_x = item_rect.Min.x >= scroll_rect.Min.x && item_rect.Max.x <= scroll_rect.Max.x;
  10807     const bool fully_visible_y = item_rect.Min.y >= scroll_rect.Min.y && item_rect.Max.y <= scroll_rect.Max.y;
  10808     const bool can_be_fully_visible_x = (item_rect.GetWidth() + g.Style.ItemSpacing.x * 2.0f) <= scroll_rect.GetWidth() || (window->AutoFitFramesX > 0) || (window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0;
  10809     const bool can_be_fully_visible_y = (item_rect.GetHeight() + g.Style.ItemSpacing.y * 2.0f) <= scroll_rect.GetHeight() || (window->AutoFitFramesY > 0) || (window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0;
  10810 
  10811     if ((flags & ImGuiScrollFlags_KeepVisibleEdgeX) && !fully_visible_x)
  10812     {
  10813         if (item_rect.Min.x < scroll_rect.Min.x || !can_be_fully_visible_x)
  10814             SetScrollFromPosX(window, item_rect.Min.x - g.Style.ItemSpacing.x - window->Pos.x, 0.0f);
  10815         else if (item_rect.Max.x >= scroll_rect.Max.x)
  10816             SetScrollFromPosX(window, item_rect.Max.x + g.Style.ItemSpacing.x - window->Pos.x, 1.0f);
  10817     }
  10818     else if (((flags & ImGuiScrollFlags_KeepVisibleCenterX) && !fully_visible_x) || (flags & ImGuiScrollFlags_AlwaysCenterX))
  10819     {
  10820         if (can_be_fully_visible_x)
  10821             SetScrollFromPosX(window, ImTrunc((item_rect.Min.x + item_rect.Max.x) * 0.5f) - window->Pos.x, 0.5f);
  10822         else
  10823             SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x, 0.0f);
  10824     }
  10825 
  10826     if ((flags & ImGuiScrollFlags_KeepVisibleEdgeY) && !fully_visible_y)
  10827     {
  10828         if (item_rect.Min.y < scroll_rect.Min.y || !can_be_fully_visible_y)
  10829             SetScrollFromPosY(window, item_rect.Min.y - g.Style.ItemSpacing.y - window->Pos.y, 0.0f);
  10830         else if (item_rect.Max.y >= scroll_rect.Max.y)
  10831             SetScrollFromPosY(window, item_rect.Max.y + g.Style.ItemSpacing.y - window->Pos.y, 1.0f);
  10832     }
  10833     else if (((flags & ImGuiScrollFlags_KeepVisibleCenterY) && !fully_visible_y) || (flags & ImGuiScrollFlags_AlwaysCenterY))
  10834     {
  10835         if (can_be_fully_visible_y)
  10836             SetScrollFromPosY(window, ImTrunc((item_rect.Min.y + item_rect.Max.y) * 0.5f) - window->Pos.y, 0.5f);
  10837         else
  10838             SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y, 0.0f);
  10839     }
  10840 
  10841     ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
  10842     ImVec2 delta_scroll = next_scroll - window->Scroll;
  10843 
  10844     // Also scroll parent window to keep us into view if necessary
  10845     if (!(flags & ImGuiScrollFlags_NoScrollParent) && (window->Flags & ImGuiWindowFlags_ChildWindow))
  10846     {
  10847         // FIXME-SCROLL: May be an option?
  10848         if ((in_flags & (ImGuiScrollFlags_AlwaysCenterX | ImGuiScrollFlags_KeepVisibleCenterX)) != 0)
  10849             in_flags = (in_flags & ~ImGuiScrollFlags_MaskX_) | ImGuiScrollFlags_KeepVisibleEdgeX;
  10850         if ((in_flags & (ImGuiScrollFlags_AlwaysCenterY | ImGuiScrollFlags_KeepVisibleCenterY)) != 0)
  10851             in_flags = (in_flags & ~ImGuiScrollFlags_MaskY_) | ImGuiScrollFlags_KeepVisibleEdgeY;
  10852         delta_scroll += ScrollToRectEx(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll), in_flags);
  10853     }
  10854 
  10855     return delta_scroll;
  10856 }
  10857 
  10858 float ImGui::GetScrollX()
  10859 {
  10860     ImGuiWindow* window = GImGui->CurrentWindow;
  10861     return window->Scroll.x;
  10862 }
  10863 
  10864 float ImGui::GetScrollY()
  10865 {
  10866     ImGuiWindow* window = GImGui->CurrentWindow;
  10867     return window->Scroll.y;
  10868 }
  10869 
  10870 float ImGui::GetScrollMaxX()
  10871 {
  10872     ImGuiWindow* window = GImGui->CurrentWindow;
  10873     return window->ScrollMax.x;
  10874 }
  10875 
  10876 float ImGui::GetScrollMaxY()
  10877 {
  10878     ImGuiWindow* window = GImGui->CurrentWindow;
  10879     return window->ScrollMax.y;
  10880 }
  10881 
  10882 void ImGui::SetScrollX(ImGuiWindow* window, float scroll_x)
  10883 {
  10884     window->ScrollTarget.x = scroll_x;
  10885     window->ScrollTargetCenterRatio.x = 0.0f;
  10886     window->ScrollTargetEdgeSnapDist.x = 0.0f;
  10887 }
  10888 
  10889 void ImGui::SetScrollY(ImGuiWindow* window, float scroll_y)
  10890 {
  10891     window->ScrollTarget.y = scroll_y;
  10892     window->ScrollTargetCenterRatio.y = 0.0f;
  10893     window->ScrollTargetEdgeSnapDist.y = 0.0f;
  10894 }
  10895 
  10896 void ImGui::SetScrollX(float scroll_x)
  10897 {
  10898     ImGuiContext& g = *GImGui;
  10899     SetScrollX(g.CurrentWindow, scroll_x);
  10900 }
  10901 
  10902 void ImGui::SetScrollY(float scroll_y)
  10903 {
  10904     ImGuiContext& g = *GImGui;
  10905     SetScrollY(g.CurrentWindow, scroll_y);
  10906 }
  10907 
  10908 // Note that a local position will vary depending on initial scroll value,
  10909 // This is a little bit confusing so bear with us:
  10910 //  - local_pos = (absolution_pos - window->Pos)
  10911 //  - So local_x/local_y are 0.0f for a position at the upper-left corner of a window,
  10912 //    and generally local_x/local_y are >(padding+decoration) && <(size-padding-decoration) when in the visible area.
  10913 //  - They mostly exist because of legacy API.
  10914 // Following the rules above, when trying to work with scrolling code, consider that:
  10915 //  - SetScrollFromPosY(0.0f) == SetScrollY(0.0f + scroll.y) == has no effect!
  10916 //  - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0f) == reset scroll. Of course writing SetScrollY(0.0f) directly then makes more sense
  10917 // We store a target position so centering and clamping can occur on the next frame when we are guaranteed to have a known window size
  10918 void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
  10919 {
  10920     IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);
  10921     window->ScrollTarget.x = IM_TRUNC(local_x - window->DecoOuterSizeX1 - window->DecoInnerSizeX1 + window->Scroll.x); // Convert local position to scroll offset
  10922     window->ScrollTargetCenterRatio.x = center_x_ratio;
  10923     window->ScrollTargetEdgeSnapDist.x = 0.0f;
  10924 }
  10925 
  10926 void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
  10927 {
  10928     IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
  10929     window->ScrollTarget.y = IM_TRUNC(local_y - window->DecoOuterSizeY1 - window->DecoInnerSizeY1 + window->Scroll.y); // Convert local position to scroll offset
  10930     window->ScrollTargetCenterRatio.y = center_y_ratio;
  10931     window->ScrollTargetEdgeSnapDist.y = 0.0f;
  10932 }
  10933 
  10934 void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)
  10935 {
  10936     ImGuiContext& g = *GImGui;
  10937     SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio);
  10938 }
  10939 
  10940 void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
  10941 {
  10942     ImGuiContext& g = *GImGui;
  10943     SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio);
  10944 }
  10945 
  10946 // center_x_ratio: 0.0f left of last item, 0.5f horizontal center of last item, 1.0f right of last item.
  10947 void ImGui::SetScrollHereX(float center_x_ratio)
  10948 {
  10949     ImGuiContext& g = *GImGui;
  10950     ImGuiWindow* window = g.CurrentWindow;
  10951     float spacing_x = ImMax(window->WindowPadding.x, g.Style.ItemSpacing.x);
  10952     float target_pos_x = ImLerp(g.LastItemData.Rect.Min.x - spacing_x, g.LastItemData.Rect.Max.x + spacing_x, center_x_ratio);
  10953     SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos
  10954 
  10955     // Tweak: snap on edges when aiming at an item very close to the edge
  10956     window->ScrollTargetEdgeSnapDist.x = ImMax(0.0f, window->WindowPadding.x - spacing_x);
  10957 }
  10958 
  10959 // center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
  10960 void ImGui::SetScrollHereY(float center_y_ratio)
  10961 {
  10962     ImGuiContext& g = *GImGui;
  10963     ImGuiWindow* window = g.CurrentWindow;
  10964     float spacing_y = ImMax(window->WindowPadding.y, g.Style.ItemSpacing.y);
  10965     float target_pos_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio);
  10966     SetScrollFromPosY(window, target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos
  10967 
  10968     // Tweak: snap on edges when aiming at an item very close to the edge
  10969     window->ScrollTargetEdgeSnapDist.y = ImMax(0.0f, window->WindowPadding.y - spacing_y);
  10970 }
  10971 
  10972 //-----------------------------------------------------------------------------
  10973 // [SECTION] TOOLTIPS
  10974 //-----------------------------------------------------------------------------
  10975 
  10976 bool ImGui::BeginTooltip()
  10977 {
  10978     return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None);
  10979 }
  10980 
  10981 bool ImGui::BeginItemTooltip()
  10982 {
  10983     if (!IsItemHovered(ImGuiHoveredFlags_ForTooltip))
  10984         return false;
  10985     return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None);
  10986 }
  10987 
  10988 bool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags)
  10989 {
  10990     ImGuiContext& g = *GImGui;
  10991 
  10992     if (g.DragDropWithinSource || g.DragDropWithinTarget)
  10993     {
  10994         // Drag and Drop tooltips are positioning differently than other tooltips:
  10995         // - offset visibility to increase visibility around mouse.
  10996         // - never clamp within outer viewport boundary.
  10997         // We call SetNextWindowPos() to enforce position and disable clamping.
  10998         // See FindBestWindowPosForPopup() for positionning logic of other tooltips (not drag and drop ones).
  10999         //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
  11000         ImVec2 tooltip_pos = g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET * g.Style.MouseCursorScale;
  11001         SetNextWindowPos(tooltip_pos);
  11002         SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
  11003         //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
  11004         tooltip_flags |= ImGuiTooltipFlags_OverridePrevious;
  11005     }
  11006 
  11007     char window_name[16];
  11008     ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
  11009     if (tooltip_flags & ImGuiTooltipFlags_OverridePrevious)
  11010         if (ImGuiWindow* window = FindWindowByName(window_name))
  11011             if (window->Active)
  11012             {
  11013                 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
  11014                 SetWindowHiddenAndSkipItemsForCurrentFrame(window);
  11015                 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
  11016             }
  11017     ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize;
  11018     Begin(window_name, NULL, flags | extra_window_flags);
  11019     // 2023-03-09: Added bool return value to the API, but currently always returning true.
  11020     // If this ever returns false we need to update BeginDragDropSource() accordingly.
  11021     //if (!ret)
  11022     //    End();
  11023     //return ret;
  11024     return true;
  11025 }
  11026 
  11027 void ImGui::EndTooltip()
  11028 {
  11029     IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip);   // Mismatched BeginTooltip()/EndTooltip() calls
  11030     End();
  11031 }
  11032 
  11033 void ImGui::SetTooltip(const char* fmt, ...)
  11034 {
  11035     va_list args;
  11036     va_start(args, fmt);
  11037     SetTooltipV(fmt, args);
  11038     va_end(args);
  11039 }
  11040 
  11041 void ImGui::SetTooltipV(const char* fmt, va_list args)
  11042 {
  11043     if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None))
  11044         return;
  11045     TextV(fmt, args);
  11046     EndTooltip();
  11047 }
  11048 
  11049 // Shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav'.
  11050 // Defaults to == ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort when using the mouse.
  11051 void ImGui::SetItemTooltip(const char* fmt, ...)
  11052 {
  11053     va_list args;
  11054     va_start(args, fmt);
  11055     if (IsItemHovered(ImGuiHoveredFlags_ForTooltip))
  11056         SetTooltipV(fmt, args);
  11057     va_end(args);
  11058 }
  11059 
  11060 void ImGui::SetItemTooltipV(const char* fmt, va_list args)
  11061 {
  11062     if (IsItemHovered(ImGuiHoveredFlags_ForTooltip))
  11063         SetTooltipV(fmt, args);
  11064 }
  11065 
  11066 
  11067 //-----------------------------------------------------------------------------
  11068 // [SECTION] POPUPS
  11069 //-----------------------------------------------------------------------------
  11070 
  11071 // Supported flags: ImGuiPopupFlags_AnyPopupId, ImGuiPopupFlags_AnyPopupLevel
  11072 bool ImGui::IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags)
  11073 {
  11074     ImGuiContext& g = *GImGui;
  11075     if (popup_flags & ImGuiPopupFlags_AnyPopupId)
  11076     {
  11077         // Return true if any popup is open at the current BeginPopup() level of the popup stack
  11078         // This may be used to e.g. test for another popups already opened to handle popups priorities at the same level.
  11079         IM_ASSERT(id == 0);
  11080         if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
  11081             return g.OpenPopupStack.Size > 0;
  11082         else
  11083             return g.OpenPopupStack.Size > g.BeginPopupStack.Size;
  11084     }
  11085     else
  11086     {
  11087         if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
  11088         {
  11089             // Return true if the popup is open anywhere in the popup stack
  11090             for (int n = 0; n < g.OpenPopupStack.Size; n++)
  11091                 if (g.OpenPopupStack[n].PopupId == id)
  11092                     return true;
  11093             return false;
  11094         }
  11095         else
  11096         {
  11097             // Return true if the popup is open at the current BeginPopup() level of the popup stack (this is the most-common query)
  11098             return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
  11099         }
  11100     }
  11101 }
  11102 
  11103 bool ImGui::IsPopupOpen(const char* str_id, ImGuiPopupFlags popup_flags)
  11104 {
  11105     ImGuiContext& g = *GImGui;
  11106     ImGuiID id = (popup_flags & ImGuiPopupFlags_AnyPopupId) ? 0 : g.CurrentWindow->GetID(str_id);
  11107     if ((popup_flags & ImGuiPopupFlags_AnyPopupLevel) && id != 0)
  11108         IM_ASSERT(0 && "Cannot use IsPopupOpen() with a string id and ImGuiPopupFlags_AnyPopupLevel."); // But non-string version is legal and used internally
  11109     return IsPopupOpen(id, popup_flags);
  11110 }
  11111 
  11112 // Also see FindBlockingModal(NULL)
  11113 ImGuiWindow* ImGui::GetTopMostPopupModal()
  11114 {
  11115     ImGuiContext& g = *GImGui;
  11116     for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
  11117         if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
  11118             if (popup->Flags & ImGuiWindowFlags_Modal)
  11119                 return popup;
  11120     return NULL;
  11121 }
  11122 
  11123 // See Demo->Stacked Modal to confirm what this is for.
  11124 ImGuiWindow* ImGui::GetTopMostAndVisiblePopupModal()
  11125 {
  11126     ImGuiContext& g = *GImGui;
  11127     for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
  11128         if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
  11129             if ((popup->Flags & ImGuiWindowFlags_Modal) && IsWindowActiveAndVisible(popup))
  11130                 return popup;
  11131     return NULL;
  11132 }
  11133 
  11134 void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags)
  11135 {
  11136     ImGuiContext& g = *GImGui;
  11137     ImGuiID id = g.CurrentWindow->GetID(str_id);
  11138     IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopup(\"%s\" -> 0x%08X)\n", str_id, id);
  11139     OpenPopupEx(id, popup_flags);
  11140 }
  11141 
  11142 void ImGui::OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags)
  11143 {
  11144     OpenPopupEx(id, popup_flags);
  11145 }
  11146 
  11147 // Mark popup as open (toggle toward open state).
  11148 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
  11149 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
  11150 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
  11151 void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags)
  11152 {
  11153     ImGuiContext& g = *GImGui;
  11154     ImGuiWindow* parent_window = g.CurrentWindow;
  11155     const int current_stack_size = g.BeginPopupStack.Size;
  11156 
  11157     if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup)
  11158         if (IsPopupOpen((ImGuiID)0, ImGuiPopupFlags_AnyPopupId))
  11159             return;
  11160 
  11161     ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
  11162     popup_ref.PopupId = id;
  11163     popup_ref.Window = NULL;
  11164     popup_ref.RestoreNavWindow = g.NavWindow;           // When popup closes focus may be restored to NavWindow (depend on window type).
  11165     popup_ref.OpenFrameCount = g.FrameCount;
  11166     popup_ref.OpenParentId = parent_window->IDStack.back();
  11167     popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
  11168     popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
  11169 
  11170     IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopupEx(0x%08X)\n", id);
  11171     if (g.OpenPopupStack.Size < current_stack_size + 1)
  11172     {
  11173         g.OpenPopupStack.push_back(popup_ref);
  11174     }
  11175     else
  11176     {
  11177         // Gently handle the user mistakenly calling OpenPopup() every frames: it is likely a programming mistake!
  11178         // However, if we were to run the regular code path, the ui would become completely unusable because the popup will always be
  11179         // in hidden-while-calculating-size state _while_ claiming focus. Which is extremely confusing situation for the programmer.
  11180         // Instead, for successive frames calls to OpenPopup(), we silently avoid reopening even if ImGuiPopupFlags_NoReopen is not specified.
  11181         bool keep_existing = false;
  11182         if (g.OpenPopupStack[current_stack_size].PopupId == id)
  11183             if ((g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1) || (popup_flags & ImGuiPopupFlags_NoReopen))
  11184                 keep_existing = true;
  11185         if (keep_existing)
  11186         {
  11187             // No reopen
  11188             g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
  11189         }
  11190         else
  11191         {
  11192             // Reopen: close child popups if any, then flag popup for open/reopen (set position, focus, init navigation)
  11193             ClosePopupToLevel(current_stack_size, true);
  11194             g.OpenPopupStack.push_back(popup_ref);
  11195         }
  11196 
  11197         // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
  11198         // This is equivalent to what ClosePopupToLevel() does.
  11199         //if (g.OpenPopupStack[current_stack_size].PopupId == id)
  11200         //    FocusWindow(parent_window);
  11201     }
  11202 }
  11203 
  11204 // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
  11205 // This function closes any popups that are over 'ref_window'.
  11206 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
  11207 {
  11208     ImGuiContext& g = *GImGui;
  11209     if (g.OpenPopupStack.Size == 0)
  11210         return;
  11211 
  11212     // Don't close our own child popup windows.
  11213     //IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupsOverWindow(\"%s\") restore_under=%d\n", ref_window ? ref_window->Name : "<NULL>", restore_focus_to_window_under_popup);
  11214     int popup_count_to_keep = 0;
  11215     if (ref_window)
  11216     {
  11217         // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
  11218         for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
  11219         {
  11220             ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];
  11221             if (!popup.Window)
  11222                 continue;
  11223             IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
  11224 
  11225             // Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow)
  11226             // - Clicking/Focusing Window2 won't close Popup1:
  11227             //     Window -> Popup1 -> Window2(Ref)
  11228             // - Clicking/focusing Popup1 will close Popup2 and Popup3:
  11229             //     Window -> Popup1(Ref) -> Popup2 -> Popup3
  11230             // - Each popups may contain child windows, which is why we compare ->RootWindow!
  11231             //     Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child
  11232             // We step through every popup from bottom to top to validate their position relative to reference window.
  11233             bool ref_window_is_descendent_of_popup = false;
  11234             for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++)
  11235                 if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window)
  11236                     if (IsWindowWithinBeginStackOf(ref_window, popup_window))
  11237                     {
  11238                         ref_window_is_descendent_of_popup = true;
  11239                         break;
  11240                     }
  11241             if (!ref_window_is_descendent_of_popup)
  11242                 break;
  11243         }
  11244     }
  11245     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
  11246     {
  11247         IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupsOverWindow(\"%s\")\n", ref_window ? ref_window->Name : "<NULL>");
  11248         ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup);
  11249     }
  11250 }
  11251 
  11252 void ImGui::ClosePopupsExceptModals()
  11253 {
  11254     ImGuiContext& g = *GImGui;
  11255 
  11256     int popup_count_to_keep;
  11257     for (popup_count_to_keep = g.OpenPopupStack.Size; popup_count_to_keep > 0; popup_count_to_keep--)
  11258     {
  11259         ImGuiWindow* window = g.OpenPopupStack[popup_count_to_keep - 1].Window;
  11260         if (!window || (window->Flags & ImGuiWindowFlags_Modal))
  11261             break;
  11262     }
  11263     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
  11264         ClosePopupToLevel(popup_count_to_keep, true);
  11265 }
  11266 
  11267 void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
  11268 {
  11269     ImGuiContext& g = *GImGui;
  11270     IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupToLevel(%d), restore_under=%d\n", remaining, restore_focus_to_window_under_popup);
  11271     IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
  11272 
  11273     // Trim open popup stack
  11274     ImGuiPopupData prev_popup = g.OpenPopupStack[remaining];
  11275     g.OpenPopupStack.resize(remaining);
  11276 
  11277     // Restore focus (unless popup window was not yet submitted, and didn't have a chance to take focus anyhow. See #7325 for an edge case)
  11278     if (restore_focus_to_window_under_popup && prev_popup.Window)
  11279     {
  11280         ImGuiWindow* popup_window = prev_popup.Window;
  11281         ImGuiWindow* focus_window = (popup_window->Flags & ImGuiWindowFlags_ChildMenu) ? popup_window->ParentWindow : prev_popup.RestoreNavWindow;
  11282         if (focus_window && !focus_window->WasActive)
  11283             FocusTopMostWindowUnderOne(popup_window, NULL, NULL, ImGuiFocusRequestFlags_RestoreFocusedChild); // Fallback
  11284         else
  11285             FocusWindow(focus_window, (g.NavLayer == ImGuiNavLayer_Main) ? ImGuiFocusRequestFlags_RestoreFocusedChild : ImGuiFocusRequestFlags_None);
  11286     }
  11287 }
  11288 
  11289 // Close the popup we have begin-ed into.
  11290 void ImGui::CloseCurrentPopup()
  11291 {
  11292     ImGuiContext& g = *GImGui;
  11293     int popup_idx = g.BeginPopupStack.Size - 1;
  11294     if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
  11295         return;
  11296 
  11297     // Closing a menu closes its top-most parent popup (unless a modal)
  11298     while (popup_idx > 0)
  11299     {
  11300         ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;
  11301         ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
  11302         bool close_parent = false;
  11303         if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
  11304             if (parent_popup_window && !(parent_popup_window->Flags & ImGuiWindowFlags_MenuBar))
  11305                 close_parent = true;
  11306         if (!close_parent)
  11307             break;
  11308         popup_idx--;
  11309     }
  11310     IMGUI_DEBUG_LOG_POPUP("[popup] CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx);
  11311     ClosePopupToLevel(popup_idx, true);
  11312 
  11313     // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
  11314     // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
  11315     // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
  11316     if (ImGuiWindow* window = g.NavWindow)
  11317         window->DC.NavHideHighlightOneFrame = true;
  11318 }
  11319 
  11320 // Attention! BeginPopup() adds default flags when calling BeginPopupEx()!
  11321 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_window_flags)
  11322 {
  11323     ImGuiContext& g = *GImGui;
  11324     if (!IsPopupOpen(id, ImGuiPopupFlags_None))
  11325     {
  11326         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
  11327         return false;
  11328     }
  11329 
  11330     char name[20];
  11331     if (extra_window_flags & ImGuiWindowFlags_ChildMenu)
  11332         ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginMenuDepth); // Recycle windows based on depth
  11333     else
  11334         ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
  11335 
  11336     bool is_open = Begin(name, NULL, extra_window_flags | ImGuiWindowFlags_Popup);
  11337     if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
  11338         EndPopup();
  11339 
  11340     //g.CurrentWindow->FocusRouteParentWindow = g.CurrentWindow->ParentWindowInBeginStack;
  11341 
  11342     return is_open;
  11343 }
  11344 
  11345 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
  11346 {
  11347     ImGuiContext& g = *GImGui;
  11348     if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
  11349     {
  11350         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
  11351         return false;
  11352     }
  11353     flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
  11354     ImGuiID id = g.CurrentWindow->GetID(str_id);
  11355     return BeginPopupEx(id, flags);
  11356 }
  11357 
  11358 // If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
  11359 // Note that popup visibility status is owned by Dear ImGui (and manipulated with e.g. OpenPopup).
  11360 // - *p_open set back to false in BeginPopupModal() when popup is not open.
  11361 // - if you set *p_open to false before calling BeginPopupModal(), it will close the popup.
  11362 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
  11363 {
  11364     ImGuiContext& g = *GImGui;
  11365     ImGuiWindow* window = g.CurrentWindow;
  11366     const ImGuiID id = window->GetID(name);
  11367     if (!IsPopupOpen(id, ImGuiPopupFlags_None))
  11368     {
  11369         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
  11370         if (p_open && *p_open)
  11371             *p_open = false;
  11372         return false;
  11373     }
  11374 
  11375     // Center modal windows by default for increased visibility
  11376     // (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves)
  11377     // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
  11378     if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
  11379     {
  11380         const ImGuiViewport* viewport = GetMainViewport();
  11381         SetNextWindowPos(viewport->GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
  11382     }
  11383 
  11384     flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse;
  11385     const bool is_open = Begin(name, p_open, flags);
  11386     if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
  11387     {
  11388         EndPopup();
  11389         if (is_open)
  11390             ClosePopupToLevel(g.BeginPopupStack.Size, true);
  11391         return false;
  11392     }
  11393     return is_open;
  11394 }
  11395 
  11396 void ImGui::EndPopup()
  11397 {
  11398     ImGuiContext& g = *GImGui;
  11399     ImGuiWindow* window = g.CurrentWindow;
  11400     IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup);  // Mismatched BeginPopup()/EndPopup() calls
  11401     IM_ASSERT(g.BeginPopupStack.Size > 0);
  11402 
  11403     // Make all menus and popups wrap around for now, may need to expose that policy (e.g. focus scope could include wrap/loop policy flags used by new move requests)
  11404     if (g.NavWindow == window)
  11405         NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
  11406 
  11407     // Child-popups don't need to be laid out
  11408     IM_ASSERT(g.WithinEndChild == false);
  11409     if (window->Flags & ImGuiWindowFlags_ChildWindow)
  11410         g.WithinEndChild = true;
  11411     End();
  11412     g.WithinEndChild = false;
  11413 }
  11414 
  11415 // Helper to open a popup if mouse button is released over the item
  11416 // - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup()
  11417 void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags)
  11418 {
  11419     ImGuiContext& g = *GImGui;
  11420     ImGuiWindow* window = g.CurrentWindow;
  11421     int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
  11422     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
  11423     {
  11424         ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID;    // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
  11425         IM_ASSERT(id != 0);                                             // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
  11426         OpenPopupEx(id, popup_flags);
  11427     }
  11428 }
  11429 
  11430 // This is a helper to handle the simplest case of associating one named popup to one given widget.
  11431 // - To create a popup associated to the last item, you generally want to pass a NULL value to str_id.
  11432 // - To create a popup with a specific identifier, pass it in str_id.
  11433 //    - This is useful when using using BeginPopupContextItem() on an item which doesn't have an identifier, e.g. a Text() call.
  11434 //    - This is useful when multiple code locations may want to manipulate/open the same popup, given an explicit id.
  11435 // - You may want to handle the whole on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
  11436 //   This is essentially the same as:
  11437 //       id = str_id ? GetID(str_id) : GetItemID();
  11438 //       OpenPopupOnItemClick(str_id, ImGuiPopupFlags_MouseButtonRight);
  11439 //       return BeginPopup(id);
  11440 //   Which is essentially the same as:
  11441 //       id = str_id ? GetID(str_id) : GetItemID();
  11442 //       if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))
  11443 //           OpenPopup(id);
  11444 //       return BeginPopup(id);
  11445 //   The main difference being that this is tweaked to avoid computing the ID twice.
  11446 bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags)
  11447 {
  11448     ImGuiContext& g = *GImGui;
  11449     ImGuiWindow* window = g.CurrentWindow;
  11450     if (window->SkipItems)
  11451         return false;
  11452     ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID;    // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
  11453     IM_ASSERT(id != 0);                                             // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
  11454     int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
  11455     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
  11456         OpenPopupEx(id, popup_flags);
  11457     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
  11458 }
  11459 
  11460 bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags)
  11461 {
  11462     ImGuiContext& g = *GImGui;
  11463     ImGuiWindow* window = g.CurrentWindow;
  11464     if (!str_id)
  11465         str_id = "window_context";
  11466     ImGuiID id = window->GetID(str_id);
  11467     int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
  11468     if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
  11469         if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered())
  11470             OpenPopupEx(id, popup_flags);
  11471     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
  11472 }
  11473 
  11474 bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags)
  11475 {
  11476     ImGuiContext& g = *GImGui;
  11477     ImGuiWindow* window = g.CurrentWindow;
  11478     if (!str_id)
  11479         str_id = "void_context";
  11480     ImGuiID id = window->GetID(str_id);
  11481     int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
  11482     if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
  11483         if (GetTopMostPopupModal() == NULL)
  11484             OpenPopupEx(id, popup_flags);
  11485     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
  11486 }
  11487 
  11488 // 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.)
  11489 // 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.
  11490 // (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor
  11491 //  information are available, it may represent the entire platform monitor from the frame of reference of the current viewport.
  11492 //  this allows us to have tooltips/popups displayed out of the parent viewport.)
  11493 ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
  11494 {
  11495     ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
  11496     //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
  11497     //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
  11498 
  11499     // Combo Box policy (we want a connecting edge)
  11500     if (policy == ImGuiPopupPositionPolicy_ComboBox)
  11501     {
  11502         const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
  11503         for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
  11504         {
  11505             const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
  11506             if (n != -1 && dir == *last_dir) // Already tried this direction?
  11507                 continue;
  11508             ImVec2 pos;
  11509             if (dir == ImGuiDir_Down)  pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y);          // Below, Toward Right (default)
  11510             if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
  11511             if (dir == ImGuiDir_Left)  pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
  11512             if (dir == ImGuiDir_Up)    pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
  11513             if (!r_outer.Contains(ImRect(pos, pos + size)))
  11514                 continue;
  11515             *last_dir = dir;
  11516             return pos;
  11517         }
  11518     }
  11519 
  11520     // Tooltip and Default popup policy
  11521     // (Always first try the direction we used on the last frame, if any)
  11522     if (policy == ImGuiPopupPositionPolicy_Tooltip || policy == ImGuiPopupPositionPolicy_Default)
  11523     {
  11524         const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
  11525         for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
  11526         {
  11527             const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
  11528             if (n != -1 && dir == *last_dir) // Already tried this direction?
  11529                 continue;
  11530 
  11531             const float avail_w = (dir == ImGuiDir_Left ? r_avoid.Min.x : r_outer.Max.x) - (dir == ImGuiDir_Right ? r_avoid.Max.x : r_outer.Min.x);
  11532             const float avail_h = (dir == ImGuiDir_Up ? r_avoid.Min.y : r_outer.Max.y) - (dir == ImGuiDir_Down ? r_avoid.Max.y : r_outer.Min.y);
  11533 
  11534             // If there's not enough room on one axis, there's no point in positioning on a side on this axis (e.g. when not enough width, use a top/bottom position to maximize available width)
  11535             if (avail_w < size.x && (dir == ImGuiDir_Left || dir == ImGuiDir_Right))
  11536                 continue;
  11537             if (avail_h < size.y && (dir == ImGuiDir_Up || dir == ImGuiDir_Down))
  11538                 continue;
  11539 
  11540             ImVec2 pos;
  11541             pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
  11542             pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
  11543 
  11544             // Clamp top-left corner of popup
  11545             pos.x = ImMax(pos.x, r_outer.Min.x);
  11546             pos.y = ImMax(pos.y, r_outer.Min.y);
  11547 
  11548             *last_dir = dir;
  11549             return pos;
  11550         }
  11551     }
  11552 
  11553     // Fallback when not enough room:
  11554     *last_dir = ImGuiDir_None;
  11555 
  11556     // For tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
  11557     if (policy == ImGuiPopupPositionPolicy_Tooltip)
  11558         return ref_pos + ImVec2(2, 2);
  11559 
  11560     // Otherwise try to keep within display
  11561     ImVec2 pos = ref_pos;
  11562     pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
  11563     pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
  11564     return pos;
  11565 }
  11566 
  11567 // Note that this is used for popups, which can overlap the non work-area of individual viewports.
  11568 ImRect ImGui::GetPopupAllowedExtentRect(ImGuiWindow* window)
  11569 {
  11570     ImGuiContext& g = *GImGui;
  11571     IM_UNUSED(window);
  11572     ImRect r_screen = ((ImGuiViewportP*)(void*)GetMainViewport())->GetMainRect();
  11573     ImVec2 padding = g.Style.DisplaySafeAreaPadding;
  11574     r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
  11575     return r_screen;
  11576 }
  11577 
  11578 ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
  11579 {
  11580     ImGuiContext& g = *GImGui;
  11581 
  11582     ImRect r_outer = GetPopupAllowedExtentRect(window);
  11583     if (window->Flags & ImGuiWindowFlags_ChildMenu)
  11584     {
  11585         // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
  11586         // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
  11587         IM_ASSERT(g.CurrentWindow == window);
  11588         ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2].Window;
  11589         float horizontal_overlap = g.Style.ItemInnerSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x).
  11590         ImRect r_avoid;
  11591         if (parent_window->DC.MenuBarAppending)
  11592             r_avoid = ImRect(-FLT_MAX, parent_window->ClipRect.Min.y, FLT_MAX, parent_window->ClipRect.Max.y); // Avoid parent menu-bar. If we wanted multi-line menu-bar, we may instead want to have the calling window setup e.g. a NextWindowData.PosConstraintAvoidRect field
  11593         else
  11594             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);
  11595         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default);
  11596     }
  11597     if (window->Flags & ImGuiWindowFlags_Popup)
  11598     {
  11599         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, ImRect(window->Pos, window->Pos), ImGuiPopupPositionPolicy_Default); // Ideally we'd disable r_avoid here
  11600     }
  11601     if (window->Flags & ImGuiWindowFlags_Tooltip)
  11602     {
  11603         // Position tooltip (always follows mouse + clamp within outer boundaries)
  11604         // Note that drag and drop tooltips are NOT using this path: BeginTooltipEx() manually sets their position.
  11605         // In theory we could handle both cases in same location, but requires a bit of shuffling as drag and drop tooltips are calling SetWindowPos() leading to 'window_pos_set_by_api' being set in Begin()
  11606         IM_ASSERT(g.CurrentWindow == window);
  11607         const float scale = g.Style.MouseCursorScale;
  11608         const ImVec2 ref_pos = NavCalcPreferredRefPos();
  11609         const ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET * scale;
  11610         ImRect r_avoid;
  11611         if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
  11612             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
  11613         else
  11614             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * scale, ref_pos.y + 24 * scale); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important.
  11615         //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255, 0, 255, 255));
  11616         return FindBestWindowPosForPopupEx(tooltip_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip);
  11617     }
  11618     IM_ASSERT(0);
  11619     return window->Pos;
  11620 }
  11621 
  11622 //-----------------------------------------------------------------------------
  11623 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
  11624 //-----------------------------------------------------------------------------
  11625 
  11626 // FIXME-NAV: The existence of SetNavID vs SetFocusID vs FocusWindow() needs to be clarified/reworked.
  11627 // In our terminology those should be interchangeable, yet right now this is super confusing.
  11628 // Those two functions are merely a legacy artifact, so at minimum naming should be clarified.
  11629 
  11630 void ImGui::SetNavWindow(ImGuiWindow* window)
  11631 {
  11632     ImGuiContext& g = *GImGui;
  11633     if (g.NavWindow != window)
  11634     {
  11635         IMGUI_DEBUG_LOG_FOCUS("[focus] SetNavWindow(\"%s\")\n", window ? window->Name : "<NULL>");
  11636         g.NavWindow = window;
  11637         g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
  11638     }
  11639     g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false;
  11640     NavUpdateAnyRequestFlag();
  11641 }
  11642 
  11643 void ImGui::NavHighlightActivated(ImGuiID id)
  11644 {
  11645     ImGuiContext& g = *GImGui;
  11646     g.NavHighlightActivatedId = id;
  11647     g.NavHighlightActivatedTimer = NAV_ACTIVATE_HIGHLIGHT_TIMER;
  11648 }
  11649 
  11650 void ImGui::NavClearPreferredPosForAxis(ImGuiAxis axis)
  11651 {
  11652     ImGuiContext& g = *GImGui;
  11653     g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer][axis] = FLT_MAX;
  11654 }
  11655 
  11656 void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel)
  11657 {
  11658     ImGuiContext& g = *GImGui;
  11659     IM_ASSERT(g.NavWindow != NULL);
  11660     IM_ASSERT(nav_layer == ImGuiNavLayer_Main || nav_layer == ImGuiNavLayer_Menu);
  11661     g.NavId = id;
  11662     g.NavLayer = nav_layer;
  11663     SetNavFocusScope(focus_scope_id);
  11664     g.NavWindow->NavLastIds[nav_layer] = id;
  11665     g.NavWindow->NavRectRel[nav_layer] = rect_rel;
  11666 
  11667     // Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)
  11668     NavClearPreferredPosForAxis(ImGuiAxis_X);
  11669     NavClearPreferredPosForAxis(ImGuiAxis_Y);
  11670 }
  11671 
  11672 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
  11673 {
  11674     ImGuiContext& g = *GImGui;
  11675     IM_ASSERT(id != 0);
  11676 
  11677     if (g.NavWindow != window)
  11678        SetNavWindow(window);
  11679 
  11680     // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and g.CurrentFocusScopeId are valid.
  11681     // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text)
  11682     const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
  11683     g.NavId = id;
  11684     g.NavLayer = nav_layer;
  11685     SetNavFocusScope(g.CurrentFocusScopeId);
  11686     window->NavLastIds[nav_layer] = id;
  11687     if (g.LastItemData.ID == id)
  11688         window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, g.LastItemData.NavRect);
  11689 
  11690     if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)
  11691         g.NavDisableMouseHover = true;
  11692     else
  11693         g.NavDisableHighlight = true;
  11694 
  11695     // Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)
  11696     NavClearPreferredPosForAxis(ImGuiAxis_X);
  11697     NavClearPreferredPosForAxis(ImGuiAxis_Y);
  11698 }
  11699 
  11700 static ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
  11701 {
  11702     if (ImFabs(dx) > ImFabs(dy))
  11703         return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
  11704     return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
  11705 }
  11706 
  11707 static float inline NavScoreItemDistInterval(float cand_min, float cand_max, float curr_min, float curr_max)
  11708 {
  11709     if (cand_max < curr_min)
  11710         return cand_max - curr_min;
  11711     if (curr_max < cand_min)
  11712         return cand_min - curr_max;
  11713     return 0.0f;
  11714 }
  11715 
  11716 // Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057
  11717 static bool ImGui::NavScoreItem(ImGuiNavItemData* result)
  11718 {
  11719     ImGuiContext& g = *GImGui;
  11720     ImGuiWindow* window = g.CurrentWindow;
  11721     if (g.NavLayer != window->DC.NavLayerCurrent)
  11722         return false;
  11723 
  11724     // FIXME: Those are not good variables names
  11725     ImRect cand = g.LastItemData.NavRect;   // Current item nav rectangle
  11726     const ImRect curr = g.NavScoringRect;   // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width)
  11727     g.NavScoringDebugCount++;
  11728 
  11729     // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
  11730     if (window->ParentWindow == g.NavWindow)
  11731     {
  11732         IM_ASSERT((window->ChildFlags | g.NavWindow->ChildFlags) & ImGuiChildFlags_NavFlattened);
  11733         if (!window->ClipRect.Overlaps(cand))
  11734             return false;
  11735         cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
  11736     }
  11737 
  11738     // Compute distance between boxes
  11739     // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
  11740     float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
  11741     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
  11742     if (dby != 0.0f && dbx != 0.0f)
  11743         dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
  11744     float dist_box = ImFabs(dbx) + ImFabs(dby);
  11745 
  11746     // 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)
  11747     float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
  11748     float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
  11749     float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
  11750 
  11751     // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
  11752     ImGuiDir quadrant;
  11753     float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
  11754     if (dbx != 0.0f || dby != 0.0f)
  11755     {
  11756         // For non-overlapping boxes, use distance between boxes
  11757         // FIXME-NAV: Quadrant may be incorrect because of (1) dbx bias and (2) curr.Max.y bias applied by NavBiasScoringRect() where typically curr.Max.y==curr.Min.y
  11758         // One typical case where this happens, with style.WindowMenuButtonPosition == ImGuiDir_Right, pressing Left to navigate from Close to Collapse tends to fail.
  11759         // Also see #6344. Calling ImGetDirQuadrantFromDelta() with unbiased values may be good but side-effects are plenty.
  11760         dax = dbx;
  11761         day = dby;
  11762         dist_axial = dist_box;
  11763         quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
  11764     }
  11765     else if (dcx != 0.0f || dcy != 0.0f)
  11766     {
  11767         // For overlapping boxes with different centers, use distance between centers
  11768         dax = dcx;
  11769         day = dcy;
  11770         dist_axial = dist_center;
  11771         quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
  11772     }
  11773     else
  11774     {
  11775         // 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)
  11776         quadrant = (g.LastItemData.ID < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
  11777     }
  11778 
  11779     const ImGuiDir move_dir = g.NavMoveDir;
  11780 #if IMGUI_DEBUG_NAV_SCORING
  11781     char buf[200];
  11782     if (g.IO.KeyCtrl) // Hold CTRL to preview score in matching quadrant. CTRL+Arrow to rotate.
  11783     {
  11784         if (quadrant == move_dir)
  11785         {
  11786             ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
  11787             ImDrawList* draw_list = GetForegroundDrawList(window);
  11788             draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 80));
  11789             draw_list->AddRectFilled(cand.Min, cand.Min + CalcTextSize(buf), IM_COL32(255, 0, 0, 200));
  11790             draw_list->AddText(cand.Min, IM_COL32(255, 255, 255, 255), buf);
  11791         }
  11792     }
  11793     const bool debug_hovering = IsMouseHoveringRect(cand.Min, cand.Max);
  11794     const bool debug_tty = (g.IO.KeyCtrl && IsKeyPressed(ImGuiKey_Space));
  11795     if (debug_hovering || debug_tty)
  11796     {
  11797         ImFormatString(buf, IM_ARRAYSIZE(buf),
  11798             "d-box    (%7.3f,%7.3f) -> %7.3f\nd-center (%7.3f,%7.3f) -> %7.3f\nd-axial  (%7.3f,%7.3f) -> %7.3f\nnav %c, quadrant %c",
  11799             dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "-WENS"[move_dir+1], "-WENS"[quadrant+1]);
  11800         if (debug_hovering)
  11801         {
  11802             ImDrawList* draw_list = GetForegroundDrawList(window);
  11803             draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255, 200, 0, 100));
  11804             draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255, 255, 0, 200));
  11805             draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40, 0, 0, 200));
  11806             draw_list->AddText(cand.Max, ~0U, buf);
  11807         }
  11808         if (debug_tty) { IMGUI_DEBUG_LOG_NAV("id 0x%08X\n%s\n", g.LastItemData.ID, buf); }
  11809     }
  11810 #endif
  11811 
  11812     // Is it in the quadrant we're interested in moving to?
  11813     bool new_best = false;
  11814     if (quadrant == move_dir)
  11815     {
  11816         // Does it beat the current best candidate?
  11817         if (dist_box < result->DistBox)
  11818         {
  11819             result->DistBox = dist_box;
  11820             result->DistCenter = dist_center;
  11821             return true;
  11822         }
  11823         if (dist_box == result->DistBox)
  11824         {
  11825             // Try using distance between center points to break ties
  11826             if (dist_center < result->DistCenter)
  11827             {
  11828                 result->DistCenter = dist_center;
  11829                 new_best = true;
  11830             }
  11831             else if (dist_center == result->DistCenter)
  11832             {
  11833                 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
  11834                 // (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),
  11835                 // 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.
  11836                 if (((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
  11837                     new_best = true;
  11838             }
  11839         }
  11840     }
  11841 
  11842     // 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
  11843     // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
  11844     // 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.
  11845     // 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.
  11846     // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
  11847     if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial)  // Check axial match
  11848         if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
  11849             if ((move_dir == ImGuiDir_Left && dax < 0.0f) || (move_dir == ImGuiDir_Right && dax > 0.0f) || (move_dir == ImGuiDir_Up && day < 0.0f) || (move_dir == ImGuiDir_Down && day > 0.0f))
  11850             {
  11851                 result->DistAxial = dist_axial;
  11852                 new_best = true;
  11853             }
  11854 
  11855     return new_best;
  11856 }
  11857 
  11858 static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result)
  11859 {
  11860     ImGuiContext& g = *GImGui;
  11861     ImGuiWindow* window = g.CurrentWindow;
  11862     result->Window = window;
  11863     result->ID = g.LastItemData.ID;
  11864     result->FocusScopeId = g.CurrentFocusScopeId;
  11865     result->InFlags = g.LastItemData.InFlags;
  11866     result->RectRel = WindowRectAbsToRel(window, g.LastItemData.NavRect);
  11867     if (result->InFlags & ImGuiItemFlags_HasSelectionUserData)
  11868     {
  11869         IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);
  11870         result->SelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.
  11871     }
  11872 }
  11873 
  11874 // True when current work location may be scrolled horizontally when moving left / right.
  11875 // This is generally always true UNLESS within a column. We don't have a vertical equivalent.
  11876 void ImGui::NavUpdateCurrentWindowIsScrollPushableX()
  11877 {
  11878     ImGuiContext& g = *GImGui;
  11879     ImGuiWindow* window = g.CurrentWindow;
  11880     window->DC.NavIsScrollPushableX = (g.CurrentTable == NULL && window->DC.CurrentColumns == NULL);
  11881 }
  11882 
  11883 // We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)
  11884 // This is called after LastItemData is set, but NextItemData is also still valid.
  11885 static void ImGui::NavProcessItem()
  11886 {
  11887     ImGuiContext& g = *GImGui;
  11888     ImGuiWindow* window = g.CurrentWindow;
  11889     const ImGuiID id = g.LastItemData.ID;
  11890     const ImGuiItemFlags item_flags = g.LastItemData.InFlags;
  11891 
  11892     // When inside a container that isn't scrollable with Left<>Right, clip NavRect accordingly (#2221)
  11893     if (window->DC.NavIsScrollPushableX == false)
  11894     {
  11895         g.LastItemData.NavRect.Min.x = ImClamp(g.LastItemData.NavRect.Min.x, window->ClipRect.Min.x, window->ClipRect.Max.x);
  11896         g.LastItemData.NavRect.Max.x = ImClamp(g.LastItemData.NavRect.Max.x, window->ClipRect.Min.x, window->ClipRect.Max.x);
  11897     }
  11898     const ImRect nav_bb = g.LastItemData.NavRect;
  11899 
  11900     // Process Init Request
  11901     if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent && (item_flags & ImGuiItemFlags_Disabled) == 0)
  11902     {
  11903         // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
  11904         const bool candidate_for_nav_default_focus = (item_flags & ImGuiItemFlags_NoNavDefaultFocus) == 0;
  11905         if (candidate_for_nav_default_focus || g.NavInitResult.ID == 0)
  11906         {
  11907             NavApplyItemToResult(&g.NavInitResult);
  11908         }
  11909         if (candidate_for_nav_default_focus)
  11910         {
  11911             g.NavInitRequest = false; // Found a match, clear request
  11912             NavUpdateAnyRequestFlag();
  11913         }
  11914     }
  11915 
  11916     // Process Move Request (scoring for navigation)
  11917     // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy)
  11918     if (g.NavMoveScoringItems && (item_flags & ImGuiItemFlags_Disabled) == 0)
  11919     {
  11920         if ((g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi) || (window->Flags & ImGuiWindowFlags_NoNavInputs) == 0)
  11921         {
  11922             const bool is_tabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0;
  11923             if (is_tabbing)
  11924             {
  11925                 NavProcessItemForTabbingRequest(id, item_flags, g.NavMoveFlags);
  11926             }
  11927             else if (g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId))
  11928             {
  11929                 ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
  11930                 if (NavScoreItem(result))
  11931                     NavApplyItemToResult(result);
  11932 
  11933                 // Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
  11934                 const float VISIBLE_RATIO = 0.70f;
  11935                 if ((g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
  11936                     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)
  11937                         if (NavScoreItem(&g.NavMoveResultLocalVisible))
  11938                             NavApplyItemToResult(&g.NavMoveResultLocalVisible);
  11939             }
  11940         }
  11941     }
  11942 
  11943     // Update information for currently focused/navigated item
  11944     if (g.NavId == id)
  11945     {
  11946         if (g.NavWindow != window)
  11947             SetNavWindow(window); // Always refresh g.NavWindow, because some operations such as FocusItem() may not have a window.
  11948         g.NavLayer = window->DC.NavLayerCurrent;
  11949         SetNavFocusScope(g.CurrentFocusScopeId); // Will set g.NavFocusScopeId AND store g.NavFocusScopePath
  11950         g.NavFocusScopeId = g.CurrentFocusScopeId;
  11951         g.NavIdIsAlive = true;
  11952         if (g.LastItemData.InFlags & ImGuiItemFlags_HasSelectionUserData)
  11953         {
  11954             IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);
  11955             g.NavLastValidSelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.
  11956         }
  11957         window->NavRectRel[window->DC.NavLayerCurrent] = WindowRectAbsToRel(window, nav_bb); // Store item bounding box (relative to window position)
  11958     }
  11959 }
  11960 
  11961 // Handle "scoring" of an item for a tabbing/focusing request initiated by NavUpdateCreateTabbingRequest().
  11962 // Note that SetKeyboardFocusHere() API calls are considered tabbing requests!
  11963 // - Case 1: no nav/active id:    set result to first eligible item, stop storing.
  11964 // - Case 2: tab forward:         on ref id set counter, on counter elapse store result
  11965 // - Case 3: tab forward wrap:    set result to first eligible item (preemptively), on ref id set counter, on next frame if counter hasn't elapsed store result. // FIXME-TABBING: Could be done as a next-frame forwarded request
  11966 // - Case 4: tab backward:        store all results, on ref id pick prev, stop storing
  11967 // - Case 5: tab backward wrap:   store all results, on ref id if no result keep storing until last // FIXME-TABBING: Could be done as next-frame forwarded requested
  11968 void ImGui::NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags)
  11969 {
  11970     ImGuiContext& g = *GImGui;
  11971 
  11972     if ((move_flags & ImGuiNavMoveFlags_FocusApi) == 0)
  11973     {
  11974         if (g.NavLayer != g.CurrentWindow->DC.NavLayerCurrent)
  11975             return;
  11976         if (g.NavFocusScopeId != g.CurrentFocusScopeId)
  11977             return;
  11978     }
  11979 
  11980     // - Can always land on an item when using API call.
  11981     // - Tabbing with _NavEnableKeyboard (space/enter/arrows): goes through every item.
  11982     // - Tabbing without _NavEnableKeyboard: goes through inputable items only.
  11983     bool can_stop;
  11984     if (move_flags & ImGuiNavMoveFlags_FocusApi)
  11985         can_stop = true;
  11986     else
  11987         can_stop = (item_flags & ImGuiItemFlags_NoTabStop) == 0 && ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) || (item_flags & ImGuiItemFlags_Inputable));
  11988 
  11989     // Always store in NavMoveResultLocal (unlike directional request which uses NavMoveResultOther on sibling/flattened windows)
  11990     ImGuiNavItemData* result = &g.NavMoveResultLocal;
  11991     if (g.NavTabbingDir == +1)
  11992     {
  11993         // Tab Forward or SetKeyboardFocusHere() with >= 0
  11994         if (can_stop && g.NavTabbingResultFirst.ID == 0)
  11995             NavApplyItemToResult(&g.NavTabbingResultFirst);
  11996         if (can_stop && g.NavTabbingCounter > 0 && --g.NavTabbingCounter == 0)
  11997             NavMoveRequestResolveWithLastItem(result);
  11998         else if (g.NavId == id)
  11999             g.NavTabbingCounter = 1;
  12000     }
  12001     else if (g.NavTabbingDir == -1)
  12002     {
  12003         // Tab Backward
  12004         if (g.NavId == id)
  12005         {
  12006             if (result->ID)
  12007             {
  12008                 g.NavMoveScoringItems = false;
  12009                 NavUpdateAnyRequestFlag();
  12010             }
  12011         }
  12012         else if (can_stop)
  12013         {
  12014             // Keep applying until reaching NavId
  12015             NavApplyItemToResult(result);
  12016         }
  12017     }
  12018     else if (g.NavTabbingDir == 0)
  12019     {
  12020         if (can_stop && g.NavId == id)
  12021             NavMoveRequestResolveWithLastItem(result);
  12022         if (can_stop && g.NavTabbingResultFirst.ID == 0) // Tab init
  12023             NavApplyItemToResult(&g.NavTabbingResultFirst);
  12024     }
  12025 }
  12026 
  12027 bool ImGui::NavMoveRequestButNoResultYet()
  12028 {
  12029     ImGuiContext& g = *GImGui;
  12030     return g.NavMoveScoringItems && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
  12031 }
  12032 
  12033 // FIXME: ScoringRect is not set
  12034 void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags)
  12035 {
  12036     ImGuiContext& g = *GImGui;
  12037     IM_ASSERT(g.NavWindow != NULL);
  12038     //IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestSubmit: dir %c, window \"%s\"\n", "-WENS"[move_dir + 1], g.NavWindow->Name);
  12039 
  12040     if (move_flags & ImGuiNavMoveFlags_IsTabbing)
  12041         move_flags |= ImGuiNavMoveFlags_AllowCurrentNavId;
  12042 
  12043     g.NavMoveSubmitted = g.NavMoveScoringItems = true;
  12044     g.NavMoveDir = move_dir;
  12045     g.NavMoveDirForDebug = move_dir;
  12046     g.NavMoveClipDir = clip_dir;
  12047     g.NavMoveFlags = move_flags;
  12048     g.NavMoveScrollFlags = scroll_flags;
  12049     g.NavMoveForwardToNextFrame = false;
  12050     g.NavMoveKeyMods = (move_flags & ImGuiNavMoveFlags_FocusApi) ? 0 : g.IO.KeyMods;
  12051     g.NavMoveResultLocal.Clear();
  12052     g.NavMoveResultLocalVisible.Clear();
  12053     g.NavMoveResultOther.Clear();
  12054     g.NavTabbingCounter = 0;
  12055     g.NavTabbingResultFirst.Clear();
  12056     NavUpdateAnyRequestFlag();
  12057 }
  12058 
  12059 void ImGui::NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result)
  12060 {
  12061     ImGuiContext& g = *GImGui;
  12062     g.NavMoveScoringItems = false; // Ensure request doesn't need more processing
  12063     NavApplyItemToResult(result);
  12064     NavUpdateAnyRequestFlag();
  12065 }
  12066 
  12067 // Called by TreePop() to implement ImGuiTreeNodeFlags_NavLeftJumpsBackHere
  12068 void ImGui::NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, ImGuiTreeNodeStackData* tree_node_data)
  12069 {
  12070     ImGuiContext& g = *GImGui;
  12071     g.NavMoveScoringItems = false;
  12072     g.LastItemData.ID = tree_node_data->ID;
  12073     g.LastItemData.InFlags = tree_node_data->InFlags & ~ImGuiItemFlags_HasSelectionUserData; // Losing SelectionUserData, recovered next-frame (cheaper).
  12074     g.LastItemData.NavRect = tree_node_data->NavRect;
  12075     NavApplyItemToResult(result); // Result this instead of implementing a NavApplyPastTreeNodeToResult()
  12076     NavClearPreferredPosForAxis(ImGuiAxis_Y);
  12077     NavUpdateAnyRequestFlag();
  12078 }
  12079 
  12080 void ImGui::NavMoveRequestCancel()
  12081 {
  12082     ImGuiContext& g = *GImGui;
  12083     g.NavMoveSubmitted = g.NavMoveScoringItems = false;
  12084     NavUpdateAnyRequestFlag();
  12085 }
  12086 
  12087 // Forward will reuse the move request again on the next frame (generally with modifications done to it)
  12088 void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags)
  12089 {
  12090     ImGuiContext& g = *GImGui;
  12091     IM_ASSERT(g.NavMoveForwardToNextFrame == false);
  12092     NavMoveRequestCancel();
  12093     g.NavMoveForwardToNextFrame = true;
  12094     g.NavMoveDir = move_dir;
  12095     g.NavMoveClipDir = clip_dir;
  12096     g.NavMoveFlags = move_flags | ImGuiNavMoveFlags_Forwarded;
  12097     g.NavMoveScrollFlags = scroll_flags;
  12098 }
  12099 
  12100 // Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
  12101 // popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.
  12102 void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags wrap_flags)
  12103 {
  12104     ImGuiContext& g = *GImGui;
  12105     IM_ASSERT((wrap_flags & ImGuiNavMoveFlags_WrapMask_ ) != 0 && (wrap_flags & ~ImGuiNavMoveFlags_WrapMask_) == 0); // Call with _WrapX, _WrapY, _LoopX, _LoopY
  12106 
  12107     // In theory we should test for NavMoveRequestButNoResultYet() but there's no point doing it:
  12108     // as NavEndFrame() will do the same test. It will end up calling NavUpdateCreateWrappingRequest().
  12109     if (g.NavWindow == window && g.NavMoveScoringItems && g.NavLayer == ImGuiNavLayer_Main)
  12110         g.NavMoveFlags = (g.NavMoveFlags & ~ImGuiNavMoveFlags_WrapMask_) | wrap_flags;
  12111 }
  12112 
  12113 // FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
  12114 // This way we could find the last focused window among our children. It would be much less confusing this way?
  12115 static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window)
  12116 {
  12117     ImGuiWindow* parent = nav_window;
  12118     while (parent && parent->RootWindow != parent && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
  12119         parent = parent->ParentWindow;
  12120     if (parent && parent != nav_window)
  12121         parent->NavLastChildNavWindow = nav_window;
  12122 }
  12123 
  12124 // Restore the last focused child.
  12125 // Call when we are expected to land on the Main Layer (0) after FocusWindow()
  12126 static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
  12127 {
  12128     if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive)
  12129         return window->NavLastChildNavWindow;
  12130     return window;
  12131 }
  12132 
  12133 void ImGui::NavRestoreLayer(ImGuiNavLayer layer)
  12134 {
  12135     ImGuiContext& g = *GImGui;
  12136     if (layer == ImGuiNavLayer_Main)
  12137     {
  12138         ImGuiWindow* prev_nav_window = g.NavWindow;
  12139         g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow);    // FIXME-NAV: Should clear ongoing nav requests?
  12140         g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
  12141         if (prev_nav_window)
  12142             IMGUI_DEBUG_LOG_FOCUS("[focus] NavRestoreLayer: from \"%s\" to SetNavWindow(\"%s\")\n", prev_nav_window->Name, g.NavWindow->Name);
  12143     }
  12144     ImGuiWindow* window = g.NavWindow;
  12145     if (window->NavLastIds[layer] != 0)
  12146     {
  12147         SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]);
  12148     }
  12149     else
  12150     {
  12151         g.NavLayer = layer;
  12152         NavInitWindow(window, true);
  12153     }
  12154 }
  12155 
  12156 void ImGui::NavRestoreHighlightAfterMove()
  12157 {
  12158     ImGuiContext& g = *GImGui;
  12159     g.NavDisableHighlight = false;
  12160     g.NavDisableMouseHover = g.NavMousePosDirty = true;
  12161 }
  12162 
  12163 static inline void ImGui::NavUpdateAnyRequestFlag()
  12164 {
  12165     ImGuiContext& g = *GImGui;
  12166     g.NavAnyRequest = g.NavMoveScoringItems || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
  12167     if (g.NavAnyRequest)
  12168         IM_ASSERT(g.NavWindow != NULL);
  12169 }
  12170 
  12171 // This needs to be called before we submit any widget (aka in or before Begin)
  12172 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
  12173 {
  12174     ImGuiContext& g = *GImGui;
  12175     IM_ASSERT(window == g.NavWindow);
  12176 
  12177     if (window->Flags & ImGuiWindowFlags_NoNavInputs)
  12178     {
  12179         g.NavId = 0;
  12180         SetNavFocusScope(window->NavRootFocusScopeId);
  12181         return;
  12182     }
  12183 
  12184     bool init_for_nav = false;
  12185     if (window == window->RootWindow || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
  12186         init_for_nav = true;
  12187     IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer);
  12188     if (init_for_nav)
  12189     {
  12190         SetNavID(0, g.NavLayer, window->NavRootFocusScopeId, ImRect());
  12191         g.NavInitRequest = true;
  12192         g.NavInitRequestFromMove = false;
  12193         g.NavInitResult.ID = 0;
  12194         NavUpdateAnyRequestFlag();
  12195     }
  12196     else
  12197     {
  12198         g.NavId = window->NavLastIds[0];
  12199         SetNavFocusScope(window->NavRootFocusScopeId);
  12200     }
  12201 }
  12202 
  12203 static ImVec2 ImGui::NavCalcPreferredRefPos()
  12204 {
  12205     ImGuiContext& g = *GImGui;
  12206     ImGuiWindow* window = g.NavWindow;
  12207     const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID;
  12208 
  12209     // Testing for !activated_shortcut here could in theory be removed if we decided that activating a remote shortcut altered one of the g.NavDisableXXX flag.
  12210     if ((g.NavDisableHighlight || !g.NavDisableMouseHover || !window) && !activated_shortcut)
  12211     {
  12212         // Mouse (we need a fallback in case the mouse becomes invalid after being used)
  12213         // The +1.0f offset when stored by OpenPopupEx() allows reopening this or another popup (same or another mouse button) while not moving the mouse, it is pretty standard.
  12214         // In theory we could move that +1.0f offset in OpenPopupEx()
  12215         ImVec2 p = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : g.MouseLastValidPos;
  12216         return ImVec2(p.x + 1.0f, p.y);
  12217     }
  12218     else
  12219     {
  12220         // When navigation is active and mouse is disabled, pick a position around the bottom left of the currently navigated item
  12221         ImRect ref_rect;
  12222         if (activated_shortcut)
  12223             ref_rect = g.LastItemData.NavRect;
  12224         else
  12225             ref_rect = WindowRectRelToAbs(window, window->NavRectRel[g.NavLayer]);
  12226 
  12227         // Take account of upcoming scrolling (maybe set mouse pos should be done in EndFrame?)
  12228         if (window->LastFrameActive != g.FrameCount && (window->ScrollTarget.x != FLT_MAX || window->ScrollTarget.y != FLT_MAX))
  12229         {
  12230             ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
  12231             ref_rect.Translate(window->Scroll - next_scroll);
  12232         }
  12233         ImVec2 pos = ImVec2(ref_rect.Min.x + ImMin(g.Style.FramePadding.x * 4, ref_rect.GetWidth()), ref_rect.Max.y - ImMin(g.Style.FramePadding.y, ref_rect.GetHeight()));
  12234         ImGuiViewport* viewport = GetMainViewport();
  12235         return ImTrunc(ImClamp(pos, viewport->Pos, viewport->Pos + viewport->Size)); // ImTrunc() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta.
  12236     }
  12237 }
  12238 
  12239 float ImGui::GetNavTweakPressedAmount(ImGuiAxis axis)
  12240 {
  12241     ImGuiContext& g = *GImGui;
  12242     float repeat_delay, repeat_rate;
  12243     GetTypematicRepeatRate(ImGuiInputFlags_RepeatRateNavTweak, &repeat_delay, &repeat_rate);
  12244 
  12245     ImGuiKey key_less, key_more;
  12246     if (g.NavInputSource == ImGuiInputSource_Gamepad)
  12247     {
  12248         key_less = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadLeft : ImGuiKey_GamepadDpadUp;
  12249         key_more = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadRight : ImGuiKey_GamepadDpadDown;
  12250     }
  12251     else
  12252     {
  12253         key_less = (axis == ImGuiAxis_X) ? ImGuiKey_LeftArrow : ImGuiKey_UpArrow;
  12254         key_more = (axis == ImGuiAxis_X) ? ImGuiKey_RightArrow : ImGuiKey_DownArrow;
  12255     }
  12256     float amount = (float)GetKeyPressedAmount(key_more, repeat_delay, repeat_rate) - (float)GetKeyPressedAmount(key_less, repeat_delay, repeat_rate);
  12257     if (amount != 0.0f && IsKeyDown(key_less) && IsKeyDown(key_more)) // Cancel when opposite directions are held, regardless of repeat phase
  12258         amount = 0.0f;
  12259     return amount;
  12260 }
  12261 
  12262 static void ImGui::NavUpdate()
  12263 {
  12264     ImGuiContext& g = *GImGui;
  12265     ImGuiIO& io = g.IO;
  12266 
  12267     io.WantSetMousePos = false;
  12268     //if (g.NavScoringDebugCount > 0) IMGUI_DEBUG_LOG_NAV("[nav] NavScoringDebugCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.NavScoringDebugCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest);
  12269 
  12270     // Set input source based on which keys are last pressed (as some features differs when used with Gamepad vs Keyboard)
  12271     // FIXME-NAV: Now that keys are separated maybe we can get rid of NavInputSource?
  12272     const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
  12273     const ImGuiKey nav_gamepad_keys_to_change_source[] = { ImGuiKey_GamepadFaceRight, ImGuiKey_GamepadFaceLeft, ImGuiKey_GamepadFaceUp, ImGuiKey_GamepadFaceDown, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown };
  12274     if (nav_gamepad_active)
  12275         for (ImGuiKey key : nav_gamepad_keys_to_change_source)
  12276             if (IsKeyDown(key))
  12277                 g.NavInputSource = ImGuiInputSource_Gamepad;
  12278     const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
  12279     const ImGuiKey nav_keyboard_keys_to_change_source[] = { ImGuiKey_Space, ImGuiKey_Enter, ImGuiKey_Escape, ImGuiKey_RightArrow, ImGuiKey_LeftArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow };
  12280     if (nav_keyboard_active)
  12281         for (ImGuiKey key : nav_keyboard_keys_to_change_source)
  12282             if (IsKeyDown(key))
  12283                 g.NavInputSource = ImGuiInputSource_Keyboard;
  12284 
  12285     // Process navigation init request (select first/default focus)
  12286     g.NavJustMovedToId = 0;
  12287     g.NavJustMovedToFocusScopeId = g.NavJustMovedFromFocusScopeId = 0;
  12288     if (g.NavInitResult.ID != 0)
  12289         NavInitRequestApplyResult();
  12290     g.NavInitRequest = false;
  12291     g.NavInitRequestFromMove = false;
  12292     g.NavInitResult.ID = 0;
  12293 
  12294     // Process navigation move request
  12295     if (g.NavMoveSubmitted)
  12296         NavMoveRequestApplyResult();
  12297     g.NavTabbingCounter = 0;
  12298     g.NavMoveSubmitted = g.NavMoveScoringItems = false;
  12299 
  12300     // Schedule mouse position update (will be done at the bottom of this function, after 1) processing all move requests and 2) updating scrolling)
  12301     bool set_mouse_pos = false;
  12302     if (g.NavMousePosDirty && g.NavIdIsAlive)
  12303         if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
  12304             set_mouse_pos = true;
  12305     g.NavMousePosDirty = false;
  12306     IM_ASSERT(g.NavLayer == ImGuiNavLayer_Main || g.NavLayer == ImGuiNavLayer_Menu);
  12307 
  12308     // Store our return window (for returning from Menu Layer to Main Layer) and clear it as soon as we step back in our own Layer 0
  12309     if (g.NavWindow)
  12310         NavSaveLastChildNavWindowIntoParent(g.NavWindow);
  12311     if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main)
  12312         g.NavWindow->NavLastChildNavWindow = NULL;
  12313 
  12314     // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
  12315     NavUpdateWindowing();
  12316 
  12317     // Set output flags for user application
  12318     io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
  12319     io.NavVisible = (io.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL);
  12320 
  12321     // Process NavCancel input (to close a popup, get back to parent, clear focus)
  12322     NavUpdateCancelRequest();
  12323 
  12324     // Process manual activation request
  12325     g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = 0;
  12326     g.NavActivateFlags = ImGuiActivateFlags_None;
  12327     if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
  12328     {
  12329         const bool activate_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Space, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadActivate, ImGuiKeyOwner_NoOwner));
  12330         const bool activate_pressed = activate_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Space, 0, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadActivate, 0, ImGuiKeyOwner_NoOwner)));
  12331         const bool input_down = (nav_keyboard_active && (IsKeyDown(ImGuiKey_Enter, ImGuiKeyOwner_NoOwner) || IsKeyDown(ImGuiKey_KeypadEnter, ImGuiKeyOwner_NoOwner))) /* || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadInput, ImGuiKeyOwner_NoOwner)) */;
  12332         const bool input_pressed = input_down && ((nav_keyboard_active && (IsKeyPressed(ImGuiKey_Enter, 0, ImGuiKeyOwner_NoOwner) || IsKeyPressed(ImGuiKey_KeypadEnter, 0, ImGuiKeyOwner_NoOwner))) /* || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadInput, 0, ImGuiKeyOwner_NoOwner)) */);
  12333         if (g.ActiveId == 0 && activate_pressed)
  12334         {
  12335             g.NavActivateId = g.NavId;
  12336             g.NavActivateFlags = ImGuiActivateFlags_PreferTweak;
  12337         }
  12338         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && input_pressed)
  12339         {
  12340             g.NavActivateId = g.NavId;
  12341             g.NavActivateFlags = ImGuiActivateFlags_PreferInput;
  12342         }
  12343         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_down || input_down))
  12344             g.NavActivateDownId = g.NavId;
  12345         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_pressed || input_pressed))
  12346         {
  12347             g.NavActivatePressedId = g.NavId;
  12348             NavHighlightActivated(g.NavId);
  12349         }
  12350     }
  12351     if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
  12352         g.NavDisableHighlight = true;
  12353     if (g.NavActivateId != 0)
  12354         IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
  12355 
  12356     // Highlight
  12357     if (g.NavHighlightActivatedTimer > 0.0f)
  12358         g.NavHighlightActivatedTimer = ImMax(0.0f, g.NavHighlightActivatedTimer - io.DeltaTime);
  12359     if (g.NavHighlightActivatedTimer == 0.0f)
  12360         g.NavHighlightActivatedId = 0;
  12361 
  12362     // Process programmatic activation request
  12363     // FIXME-NAV: Those should eventually be queued (unlike focus they don't cancel each others)
  12364     if (g.NavNextActivateId != 0)
  12365     {
  12366         g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavNextActivateId;
  12367         g.NavActivateFlags = g.NavNextActivateFlags;
  12368     }
  12369     g.NavNextActivateId = 0;
  12370 
  12371     // Process move requests
  12372     NavUpdateCreateMoveRequest();
  12373     if (g.NavMoveDir == ImGuiDir_None)
  12374         NavUpdateCreateTabbingRequest();
  12375     NavUpdateAnyRequestFlag();
  12376     g.NavIdIsAlive = false;
  12377 
  12378     // Scrolling
  12379     if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
  12380     {
  12381         // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
  12382         ImGuiWindow* window = g.NavWindow;
  12383         const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.
  12384         const ImGuiDir move_dir = g.NavMoveDir;
  12385         if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavWindowHasScrollY && move_dir != ImGuiDir_None)
  12386         {
  12387             if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
  12388                 SetScrollX(window, ImTrunc(window->Scroll.x + ((move_dir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
  12389             if (move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down)
  12390                 SetScrollY(window, ImTrunc(window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
  12391         }
  12392 
  12393         // *Normal* Manual scroll with LStick
  12394         // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
  12395         if (nav_gamepad_active)
  12396         {
  12397             const ImVec2 scroll_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown);
  12398             const float tweak_factor = IsKeyDown(ImGuiKey_NavGamepadTweakSlow) ? 1.0f / 10.0f : IsKeyDown(ImGuiKey_NavGamepadTweakFast) ? 10.0f : 1.0f;
  12399             if (scroll_dir.x != 0.0f && window->ScrollbarX)
  12400                 SetScrollX(window, ImTrunc(window->Scroll.x + scroll_dir.x * scroll_speed * tweak_factor));
  12401             if (scroll_dir.y != 0.0f)
  12402                 SetScrollY(window, ImTrunc(window->Scroll.y + scroll_dir.y * scroll_speed * tweak_factor));
  12403         }
  12404     }
  12405 
  12406     // Always prioritize mouse highlight if navigation is disabled
  12407     if (!nav_keyboard_active && !nav_gamepad_active)
  12408     {
  12409         g.NavDisableHighlight = true;
  12410         g.NavDisableMouseHover = set_mouse_pos = false;
  12411     }
  12412 
  12413     // Update mouse position if requested
  12414     // (This will take into account the possibility that a Scroll was queued in the window to offset our absolute mouse position before scroll has been applied)
  12415     if (set_mouse_pos && (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
  12416         TeleportMousePos(NavCalcPreferredRefPos());
  12417 
  12418     // [DEBUG]
  12419     g.NavScoringDebugCount = 0;
  12420 #if IMGUI_DEBUG_NAV_RECTS
  12421     if (ImGuiWindow* debug_window = g.NavWindow)
  12422     {
  12423         ImDrawList* draw_list = GetForegroundDrawList(debug_window);
  12424         int layer = g.NavLayer; /* for (int layer = 0; layer < 2; layer++)*/ { ImRect r = WindowRectRelToAbs(debug_window, debug_window->NavRectRel[layer]); draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 200, 0, 255)); }
  12425         //if (1) { ImU32 col = (!debug_window->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); }
  12426     }
  12427 #endif
  12428 }
  12429 
  12430 void ImGui::NavInitRequestApplyResult()
  12431 {
  12432     // In very rare cases g.NavWindow may be null (e.g. clearing focus after requesting an init request, which does happen when releasing Alt while clicking on void)
  12433     ImGuiContext& g = *GImGui;
  12434     if (!g.NavWindow)
  12435         return;
  12436 
  12437     ImGuiNavItemData* result = &g.NavInitResult;
  12438     if (g.NavId != result->ID)
  12439     {
  12440         g.NavJustMovedFromFocusScopeId = g.NavFocusScopeId;
  12441         g.NavJustMovedToId = result->ID;
  12442         g.NavJustMovedToFocusScopeId = result->FocusScopeId;
  12443         g.NavJustMovedToKeyMods = 0;
  12444         g.NavJustMovedToIsTabbing = false;
  12445         g.NavJustMovedToHasSelectionData = (result->InFlags & ImGuiItemFlags_HasSelectionUserData) != 0;
  12446     }
  12447 
  12448     // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
  12449     // FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently.
  12450     IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: ApplyResult: NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
  12451     SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
  12452     g.NavIdIsAlive = true; // Mark as alive from previous frame as we got a result
  12453     if (result->SelectionUserData != ImGuiSelectionUserData_Invalid)
  12454         g.NavLastValidSelectionUserData = result->SelectionUserData;
  12455     if (g.NavInitRequestFromMove)
  12456         NavRestoreHighlightAfterMove();
  12457 }
  12458 
  12459 // Bias scoring rect ahead of scoring + update preferred pos (if missing) using source position
  12460 static void NavBiasScoringRect(ImRect& r, ImVec2& preferred_pos_rel, ImGuiDir move_dir, ImGuiNavMoveFlags move_flags)
  12461 {
  12462     // Bias initial rect
  12463     ImGuiContext& g = *GImGui;
  12464     const ImVec2 rel_to_abs_offset = g.NavWindow->DC.CursorStartPos;
  12465 
  12466     // Initialize bias on departure if we don't have any. So mouse-click + arrow will record bias.
  12467     // - We default to L/U bias, so moving down from a large source item into several columns will land on left-most column.
  12468     // - But each successful move sets new bias on one axis, only cleared when using mouse.
  12469     if ((move_flags & ImGuiNavMoveFlags_Forwarded) == 0)
  12470     {
  12471         if (preferred_pos_rel.x == FLT_MAX)
  12472             preferred_pos_rel.x = ImMin(r.Min.x + 1.0f, r.Max.x) - rel_to_abs_offset.x;
  12473         if (preferred_pos_rel.y == FLT_MAX)
  12474             preferred_pos_rel.y = r.GetCenter().y - rel_to_abs_offset.y;
  12475     }
  12476 
  12477     // Apply general bias on the other axis
  12478     if ((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) && preferred_pos_rel.x != FLT_MAX)
  12479         r.Min.x = r.Max.x = preferred_pos_rel.x + rel_to_abs_offset.x;
  12480     else if ((move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) && preferred_pos_rel.y != FLT_MAX)
  12481         r.Min.y = r.Max.y = preferred_pos_rel.y + rel_to_abs_offset.y;
  12482 }
  12483 
  12484 void ImGui::NavUpdateCreateMoveRequest()
  12485 {
  12486     ImGuiContext& g = *GImGui;
  12487     ImGuiIO& io = g.IO;
  12488     ImGuiWindow* window = g.NavWindow;
  12489     const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
  12490     const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
  12491 
  12492     if (g.NavMoveForwardToNextFrame && window != NULL)
  12493     {
  12494         // 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)
  12495         // (preserve most state, which were already set by the NavMoveRequestForward() function)
  12496         IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
  12497         IM_ASSERT(g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded);
  12498         IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir);
  12499     }
  12500     else
  12501     {
  12502         // Initiate directional inputs request
  12503         g.NavMoveDir = ImGuiDir_None;
  12504         g.NavMoveFlags = ImGuiNavMoveFlags_None;
  12505         g.NavMoveScrollFlags = ImGuiScrollFlags_None;
  12506         if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs))
  12507         {
  12508             const ImGuiInputFlags repeat_mode = ImGuiInputFlags_Repeat | (ImGuiInputFlags)ImGuiInputFlags_RepeatRateNavMove;
  12509             if (!IsActiveIdUsingNavDir(ImGuiDir_Left)  && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadLeft,  repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_LeftArrow,  repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Left; }
  12510             if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadRight, repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_RightArrow, repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Right; }
  12511             if (!IsActiveIdUsingNavDir(ImGuiDir_Up)    && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadUp,    repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_UpArrow,    repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Up; }
  12512             if (!IsActiveIdUsingNavDir(ImGuiDir_Down)  && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadDown,  repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_DownArrow,  repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Down; }
  12513         }
  12514         g.NavMoveClipDir = g.NavMoveDir;
  12515         g.NavScoringNoClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);
  12516     }
  12517 
  12518     // Update PageUp/PageDown/Home/End scroll
  12519     // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
  12520     float scoring_rect_offset_y = 0.0f;
  12521     if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active)
  12522         scoring_rect_offset_y = NavUpdatePageUpPageDown();
  12523     if (scoring_rect_offset_y != 0.0f)
  12524     {
  12525         g.NavScoringNoClipRect = window->InnerRect;
  12526         g.NavScoringNoClipRect.TranslateY(scoring_rect_offset_y);
  12527     }
  12528 
  12529     // [DEBUG] Always send a request when holding CTRL. Hold CTRL + Arrow change the direction.
  12530 #if IMGUI_DEBUG_NAV_SCORING
  12531     //if (io.KeyCtrl && IsKeyPressed(ImGuiKey_C))
  12532     //    g.NavMoveDirForDebug = (ImGuiDir)((g.NavMoveDirForDebug + 1) & 3);
  12533     if (io.KeyCtrl)
  12534     {
  12535         if (g.NavMoveDir == ImGuiDir_None)
  12536             g.NavMoveDir = g.NavMoveDirForDebug;
  12537         g.NavMoveClipDir = g.NavMoveDir;
  12538         g.NavMoveFlags |= ImGuiNavMoveFlags_DebugNoResult;
  12539     }
  12540 #endif
  12541 
  12542     // Submit
  12543     g.NavMoveForwardToNextFrame = false;
  12544     if (g.NavMoveDir != ImGuiDir_None)
  12545         NavMoveRequestSubmit(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags);
  12546 
  12547     // Moving with no reference triggers an init request (will be used as a fallback if the direction fails to find a match)
  12548     if (g.NavMoveSubmitted && g.NavId == 0)
  12549     {
  12550         IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", window ? window->Name : "<NULL>", g.NavLayer);
  12551         g.NavInitRequest = g.NavInitRequestFromMove = true;
  12552         g.NavInitResult.ID = 0;
  12553         g.NavDisableHighlight = false;
  12554     }
  12555 
  12556     // When using gamepad, we project the reference nav bounding box into window visible area.
  12557     // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling,
  12558     // since with gamepad all movements are relative (can't focus a visible object like we can with the mouse).
  12559     if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL)// && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded))
  12560     {
  12561         bool clamp_x = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapX)) == 0;
  12562         bool clamp_y = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapY)) == 0;
  12563         ImRect inner_rect_rel = WindowRectAbsToRel(window, ImRect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1)));
  12564 
  12565         // Take account of changing scroll to handle triggering a new move request on a scrolling frame. (#6171)
  12566         // Otherwise 'inner_rect_rel' would be off on the move result frame.
  12567         inner_rect_rel.Translate(CalcNextScrollFromScrollTargetAndClamp(window) - window->Scroll);
  12568 
  12569         if ((clamp_x || clamp_y) && !inner_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
  12570         {
  12571             IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel for gamepad move\n");
  12572             float pad_x = ImMin(inner_rect_rel.GetWidth(), window->CalcFontSize() * 0.5f);
  12573             float pad_y = ImMin(inner_rect_rel.GetHeight(), window->CalcFontSize() * 0.5f); // Terrible approximation for the intent of starting navigation from first fully visible item
  12574             inner_rect_rel.Min.x = clamp_x ? (inner_rect_rel.Min.x + pad_x) : -FLT_MAX;
  12575             inner_rect_rel.Max.x = clamp_x ? (inner_rect_rel.Max.x - pad_x) : +FLT_MAX;
  12576             inner_rect_rel.Min.y = clamp_y ? (inner_rect_rel.Min.y + pad_y) : -FLT_MAX;
  12577             inner_rect_rel.Max.y = clamp_y ? (inner_rect_rel.Max.y - pad_y) : +FLT_MAX;
  12578             window->NavRectRel[g.NavLayer].ClipWithFull(inner_rect_rel);
  12579             g.NavId = 0;
  12580         }
  12581     }
  12582 
  12583     // 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)
  12584     ImRect scoring_rect;
  12585     if (window != NULL)
  12586     {
  12587         ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);
  12588         scoring_rect = WindowRectRelToAbs(window, nav_rect_rel);
  12589         scoring_rect.TranslateY(scoring_rect_offset_y);
  12590         if (g.NavMoveSubmitted)
  12591             NavBiasScoringRect(scoring_rect, window->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer], g.NavMoveDir, g.NavMoveFlags);
  12592         IM_ASSERT(!scoring_rect.IsInverted()); // Ensure we have a non-inverted bounding box here will allow us to remove extraneous ImFabs() calls in NavScoreItem().
  12593         //GetForegroundDrawList()->AddRect(scoring_rect.Min, scoring_rect.Max, IM_COL32(255,200,0,255)); // [DEBUG]
  12594         //if (!g.NavScoringNoClipRect.IsInverted()) { GetForegroundDrawList()->AddRect(g.NavScoringNoClipRect.Min, g.NavScoringNoClipRect.Max, IM_COL32(255, 200, 0, 255)); } // [DEBUG]
  12595     }
  12596     g.NavScoringRect = scoring_rect;
  12597     g.NavScoringNoClipRect.Add(scoring_rect);
  12598 }
  12599 
  12600 void ImGui::NavUpdateCreateTabbingRequest()
  12601 {
  12602     ImGuiContext& g = *GImGui;
  12603     ImGuiWindow* window = g.NavWindow;
  12604     IM_ASSERT(g.NavMoveDir == ImGuiDir_None);
  12605     if (window == NULL || g.NavWindowingTarget != NULL || (window->Flags & ImGuiWindowFlags_NoNavInputs))
  12606         return;
  12607 
  12608     const bool tab_pressed = IsKeyPressed(ImGuiKey_Tab, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner) && !g.IO.KeyCtrl && !g.IO.KeyAlt;
  12609     if (!tab_pressed)
  12610         return;
  12611 
  12612     // Initiate tabbing request
  12613     // (this is ALWAYS ENABLED, regardless of ImGuiConfigFlags_NavEnableKeyboard flag!)
  12614     // See NavProcessItemForTabbingRequest() for a description of the various forward/backward tabbing cases with and without wrapping.
  12615     const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
  12616     if (nav_keyboard_active)
  12617         g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.NavDisableHighlight == true && g.ActiveId == 0) ? 0 : +1;
  12618     else
  12619         g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.ActiveId == 0) ? 0 : +1;
  12620     ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate;
  12621     ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
  12622     ImGuiDir clip_dir = (g.NavTabbingDir < 0) ? ImGuiDir_Up : ImGuiDir_Down;
  12623     NavMoveRequestSubmit(ImGuiDir_None, clip_dir, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.
  12624     g.NavTabbingCounter = -1;
  12625 }
  12626 
  12627 // Apply result from previous frame navigation directional move request. Always called from NavUpdate()
  12628 void ImGui::NavMoveRequestApplyResult()
  12629 {
  12630     ImGuiContext& g = *GImGui;
  12631 #if IMGUI_DEBUG_NAV_SCORING
  12632     if (g.NavMoveFlags & ImGuiNavMoveFlags_DebugNoResult) // [DEBUG] Scoring all items in NavWindow at all times
  12633         return;
  12634 #endif
  12635 
  12636     // Select which result to use
  12637     ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : (g.NavMoveResultOther.ID != 0) ? &g.NavMoveResultOther : NULL;
  12638 
  12639     // Tabbing forward wrap
  12640     if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && result == NULL)
  12641         if ((g.NavTabbingCounter == 1 || g.NavTabbingDir == 0) && g.NavTabbingResultFirst.ID)
  12642             result = &g.NavTabbingResultFirst;
  12643 
  12644     // In a situation when there are no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result)
  12645     const ImGuiAxis axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;
  12646     if (result == NULL)
  12647     {
  12648         if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing)
  12649             g.NavMoveFlags |= ImGuiNavMoveFlags_NoSetNavHighlight;
  12650         if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavHighlight) == 0)
  12651             NavRestoreHighlightAfterMove();
  12652         NavClearPreferredPosForAxis(axis); // On a failed move, clear preferred pos for this axis.
  12653         IMGUI_DEBUG_LOG_NAV("[nav] NavMoveSubmitted but not led to a result!\n");
  12654         return;
  12655     }
  12656 
  12657     // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
  12658     if (g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
  12659         if (g.NavMoveResultLocalVisible.ID != 0 && g.NavMoveResultLocalVisible.ID != g.NavId)
  12660             result = &g.NavMoveResultLocalVisible;
  12661 
  12662     // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
  12663     if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
  12664         if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
  12665             result = &g.NavMoveResultOther;
  12666     IM_ASSERT(g.NavWindow && result->Window);
  12667 
  12668     // Scroll to keep newly navigated item fully into view.
  12669     if (g.NavLayer == ImGuiNavLayer_Main)
  12670     {
  12671         ImRect rect_abs = WindowRectRelToAbs(result->Window, result->RectRel);
  12672         ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags);
  12673 
  12674         if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdgeY)
  12675         {
  12676             // FIXME: Should remove this? Or make more precise: use ScrollToRectEx() with edge?
  12677             float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
  12678             SetScrollY(result->Window, scroll_target);
  12679         }
  12680     }
  12681 
  12682     if (g.NavWindow != result->Window)
  12683     {
  12684         IMGUI_DEBUG_LOG_FOCUS("[focus] NavMoveRequest: SetNavWindow(\"%s\")\n", result->Window->Name);
  12685         g.NavWindow = result->Window;
  12686         g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
  12687     }
  12688 
  12689     // Clear active id unless requested not to
  12690     // FIXME: ImGuiNavMoveFlags_NoClearActiveId is currently unused as we don't have a clear strategy to preserve active id after interaction,
  12691     // so this is mostly provided as a gateway for further experiments (see #1418, #2890)
  12692     if (g.ActiveId != result->ID && (g.NavMoveFlags & ImGuiNavMoveFlags_NoClearActiveId) == 0)
  12693         ClearActiveID();
  12694 
  12695     // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
  12696     // PageUp/PageDown however sets always set NavJustMovedTo (vs Home/End which doesn't) mimicking Windows behavior.
  12697     if ((g.NavId != result->ID || (g.NavMoveFlags & ImGuiNavMoveFlags_IsPageMove)) && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSelect) == 0)
  12698     {
  12699         g.NavJustMovedFromFocusScopeId = g.NavFocusScopeId;
  12700         g.NavJustMovedToId = result->ID;
  12701         g.NavJustMovedToFocusScopeId = result->FocusScopeId;
  12702         g.NavJustMovedToKeyMods = g.NavMoveKeyMods;
  12703         g.NavJustMovedToIsTabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0;
  12704         g.NavJustMovedToHasSelectionData = (result->InFlags & ImGuiItemFlags_HasSelectionUserData) != 0;
  12705         //IMGUI_DEBUG_LOG_NAV("[nav] NavJustMovedFromFocusScopeId = 0x%08X, NavJustMovedToFocusScopeId = 0x%08X\n", g.NavJustMovedFromFocusScopeId, g.NavJustMovedToFocusScopeId);
  12706     }
  12707 
  12708     // Apply new NavID/Focus
  12709     IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
  12710     ImVec2 preferred_scoring_pos_rel = g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer];
  12711     SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
  12712     if (result->SelectionUserData != ImGuiSelectionUserData_Invalid)
  12713         g.NavLastValidSelectionUserData = result->SelectionUserData;
  12714 
  12715     // Restore last preferred position for current axis
  12716     // (storing in RootWindowForNav-> as the info is desirable at the beginning of a Move Request. In theory all storage should use RootWindowForNav..)
  12717     if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) == 0)
  12718     {
  12719         preferred_scoring_pos_rel[axis] = result->RectRel.GetCenter()[axis];
  12720         g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer] = preferred_scoring_pos_rel;
  12721     }
  12722 
  12723     // Tabbing: Activates Inputable, otherwise only Focus
  12724     if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && (result->InFlags & ImGuiItemFlags_Inputable) == 0)
  12725         g.NavMoveFlags &= ~ImGuiNavMoveFlags_Activate;
  12726 
  12727     // Activate
  12728     if (g.NavMoveFlags & ImGuiNavMoveFlags_Activate)
  12729     {
  12730         g.NavNextActivateId = result->ID;
  12731         g.NavNextActivateFlags = ImGuiActivateFlags_None;
  12732         if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing)
  12733             g.NavNextActivateFlags |= ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState | ImGuiActivateFlags_FromTabbing;
  12734     }
  12735 
  12736     // Enable nav highlight
  12737     if ((g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavHighlight) == 0)
  12738         NavRestoreHighlightAfterMove();
  12739 }
  12740 
  12741 // Process NavCancel input (to close a popup, get back to parent, clear focus)
  12742 // FIXME: In order to support e.g. Escape to clear a selection we'll need:
  12743 // - either to store the equivalent of ActiveIdUsingKeyInputMask for a FocusScope and test for it.
  12744 // - either to move most/all of those tests to the epilogue/end functions of the scope they are dealing with (e.g. exit child window in EndChild()) or in EndFrame(), to allow an earlier intercept
  12745 static void ImGui::NavUpdateCancelRequest()
  12746 {
  12747     ImGuiContext& g = *GImGui;
  12748     const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
  12749     const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
  12750     if (!(nav_keyboard_active && IsKeyPressed(ImGuiKey_Escape, 0, ImGuiKeyOwner_NoOwner)) && !(nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadCancel, 0, ImGuiKeyOwner_NoOwner)))
  12751         return;
  12752 
  12753     IMGUI_DEBUG_LOG_NAV("[nav] NavUpdateCancelRequest()\n");
  12754     if (g.ActiveId != 0)
  12755     {
  12756         ClearActiveID();
  12757     }
  12758     else if (g.NavLayer != ImGuiNavLayer_Main)
  12759     {
  12760         // Leave the "menu" layer
  12761         NavRestoreLayer(ImGuiNavLayer_Main);
  12762         NavRestoreHighlightAfterMove();
  12763     }
  12764     else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->RootWindowForNav->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->RootWindowForNav->ParentWindow)
  12765     {
  12766         // Exit child window
  12767         ImGuiWindow* child_window = g.NavWindow->RootWindowForNav;
  12768         ImGuiWindow* parent_window = child_window->ParentWindow;
  12769         IM_ASSERT(child_window->ChildId != 0);
  12770         FocusWindow(parent_window);
  12771         SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, WindowRectAbsToRel(parent_window, child_window->Rect()));
  12772         NavRestoreHighlightAfterMove();
  12773     }
  12774     else if (g.OpenPopupStack.Size > 0 && g.OpenPopupStack.back().Window != NULL && !(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
  12775     {
  12776         // Close open popup/menu
  12777         ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
  12778     }
  12779 #if 0
  12780     // DUCKSTATION-CHANGE: We want to keep nav active, since we handle menu exits ourselves.
  12781     else
  12782     {
  12783         // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
  12784         if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
  12785             g.NavWindow->NavLastIds[0] = 0;
  12786         g.NavId = 0;
  12787     }
  12788 #endif
  12789 }
  12790 
  12791 // Handle PageUp/PageDown/Home/End keys
  12792 // Called from NavUpdateCreateMoveRequest() which will use our output to create a move request
  12793 // FIXME-NAV: This doesn't work properly with NavFlattened siblings as we use NavWindow rectangle for reference
  12794 // FIXME-NAV: how to get Home/End to aim at the beginning/end of a 2D grid?
  12795 static float ImGui::NavUpdatePageUpPageDown()
  12796 {
  12797     ImGuiContext& g = *GImGui;
  12798     ImGuiWindow* window = g.NavWindow;
  12799     if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL)
  12800         return 0.0f;
  12801 
  12802     const bool page_up_held = IsKeyDown(ImGuiKey_PageUp, ImGuiKeyOwner_NoOwner);
  12803     const bool page_down_held = IsKeyDown(ImGuiKey_PageDown, ImGuiKeyOwner_NoOwner);
  12804     const bool home_pressed = IsKeyPressed(ImGuiKey_Home, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner);
  12805     const bool end_pressed = IsKeyPressed(ImGuiKey_End, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner);
  12806     if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out
  12807         return 0.0f;
  12808 
  12809     if (g.NavLayer != ImGuiNavLayer_Main)
  12810         NavRestoreLayer(ImGuiNavLayer_Main);
  12811 
  12812     if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavWindowHasScrollY)
  12813     {
  12814         // Fallback manual-scroll when window has no navigable item
  12815         if (IsKeyPressed(ImGuiKey_PageUp, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner))
  12816             SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
  12817         else if (IsKeyPressed(ImGuiKey_PageDown, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner))
  12818             SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
  12819         else if (home_pressed)
  12820             SetScrollY(window, 0.0f);
  12821         else if (end_pressed)
  12822             SetScrollY(window, window->ScrollMax.y);
  12823     }
  12824     else
  12825     {
  12826         ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
  12827         const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
  12828         float nav_scoring_rect_offset_y = 0.0f;
  12829         if (IsKeyPressed(ImGuiKey_PageUp, true))
  12830         {
  12831             nav_scoring_rect_offset_y = -page_offset_y;
  12832             g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item)
  12833             g.NavMoveClipDir = ImGuiDir_Up;
  12834             g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove;
  12835         }
  12836         else if (IsKeyPressed(ImGuiKey_PageDown, true))
  12837         {
  12838             nav_scoring_rect_offset_y = +page_offset_y;
  12839             g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item)
  12840             g.NavMoveClipDir = ImGuiDir_Down;
  12841             g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove;
  12842         }
  12843         else if (home_pressed)
  12844         {
  12845             // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
  12846             // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdgeY flag, we don't scroll immediately to avoid scrolling happening before nav result.
  12847             // Preserve current horizontal position if we have any.
  12848             nav_rect_rel.Min.y = nav_rect_rel.Max.y = 0.0f;
  12849             if (nav_rect_rel.IsInverted())
  12850                 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
  12851             g.NavMoveDir = ImGuiDir_Down;
  12852             g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY;
  12853             // FIXME-NAV: MoveClipDir left to _None, intentional?
  12854         }
  12855         else if (end_pressed)
  12856         {
  12857             nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ContentSize.y;
  12858             if (nav_rect_rel.IsInverted())
  12859                 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
  12860             g.NavMoveDir = ImGuiDir_Up;
  12861             g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY;
  12862             // FIXME-NAV: MoveClipDir left to _None, intentional?
  12863         }
  12864         return nav_scoring_rect_offset_y;
  12865     }
  12866     return 0.0f;
  12867 }
  12868 
  12869 static void ImGui::NavEndFrame()
  12870 {
  12871     ImGuiContext& g = *GImGui;
  12872 
  12873     // Show CTRL+TAB list window
  12874     if (g.NavWindowingTarget != NULL)
  12875         NavUpdateWindowingOverlay();
  12876 
  12877     // Perform wrap-around in menus
  12878     // FIXME-NAV: Wrap may need to apply a weight bias on the other axis. e.g. 4x4 grid with 2 last items missing on last item won't handle LoopY/WrapY correctly.
  12879     // FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame.
  12880     if (g.NavWindow && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & ImGuiNavMoveFlags_WrapMask_) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0)
  12881         NavUpdateCreateWrappingRequest();
  12882 }
  12883 
  12884 static void ImGui::NavUpdateCreateWrappingRequest()
  12885 {
  12886     ImGuiContext& g = *GImGui;
  12887     ImGuiWindow* window = g.NavWindow;
  12888 
  12889     bool do_forward = false;
  12890     ImRect bb_rel = window->NavRectRel[g.NavLayer];
  12891     ImGuiDir clip_dir = g.NavMoveDir;
  12892 
  12893     const ImGuiNavMoveFlags move_flags = g.NavMoveFlags;
  12894     //const ImGuiAxis move_axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;
  12895     if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
  12896     {
  12897         bb_rel.Min.x = bb_rel.Max.x = window->ContentSize.x + window->WindowPadding.x;
  12898         if (move_flags & ImGuiNavMoveFlags_WrapX)
  12899         {
  12900             bb_rel.TranslateY(-bb_rel.GetHeight()); // Previous row
  12901             clip_dir = ImGuiDir_Up;
  12902         }
  12903         do_forward = true;
  12904     }
  12905     if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
  12906     {
  12907         bb_rel.Min.x = bb_rel.Max.x = -window->WindowPadding.x;
  12908         if (move_flags & ImGuiNavMoveFlags_WrapX)
  12909         {
  12910             bb_rel.TranslateY(+bb_rel.GetHeight()); // Next row
  12911             clip_dir = ImGuiDir_Down;
  12912         }
  12913         do_forward = true;
  12914     }
  12915     if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
  12916     {
  12917         bb_rel.Min.y = bb_rel.Max.y = window->ContentSize.y + window->WindowPadding.y;
  12918         if (move_flags & ImGuiNavMoveFlags_WrapY)
  12919         {
  12920             bb_rel.TranslateX(-bb_rel.GetWidth()); // Previous column
  12921             clip_dir = ImGuiDir_Left;
  12922         }
  12923         do_forward = true;
  12924     }
  12925     if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
  12926     {
  12927         bb_rel.Min.y = bb_rel.Max.y = -window->WindowPadding.y;
  12928         if (move_flags & ImGuiNavMoveFlags_WrapY)
  12929         {
  12930             bb_rel.TranslateX(+bb_rel.GetWidth()); // Next column
  12931             clip_dir = ImGuiDir_Right;
  12932         }
  12933         do_forward = true;
  12934     }
  12935     if (!do_forward)
  12936         return;
  12937     window->NavRectRel[g.NavLayer] = bb_rel;
  12938     NavClearPreferredPosForAxis(ImGuiAxis_X);
  12939     NavClearPreferredPosForAxis(ImGuiAxis_Y);
  12940     NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags, g.NavMoveScrollFlags);
  12941 }
  12942 
  12943 static int ImGui::FindWindowFocusIndex(ImGuiWindow* window)
  12944 {
  12945     ImGuiContext& g = *GImGui;
  12946     IM_UNUSED(g);
  12947     int order = window->FocusOrder;
  12948     IM_ASSERT(window->RootWindow == window); // No child window (not testing _ChildWindow because of docking)
  12949     IM_ASSERT(g.WindowsFocusOrder[order] == window);
  12950     return order;
  12951 }
  12952 
  12953 static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
  12954 {
  12955     ImGuiContext& g = *GImGui;
  12956     for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
  12957         if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
  12958             return g.WindowsFocusOrder[i];
  12959     return NULL;
  12960 }
  12961 
  12962 static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
  12963 {
  12964     ImGuiContext& g = *GImGui;
  12965     IM_ASSERT(g.NavWindowingTarget);
  12966     if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
  12967         return;
  12968 
  12969     const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget);
  12970     ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
  12971     if (!window_target)
  12972         window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
  12973     if (window_target) // Don't reset windowing target if there's a single window in the list
  12974     {
  12975         g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
  12976         g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f);
  12977     }
  12978     g.NavWindowingToggleLayer = false;
  12979 }
  12980 
  12981 // Windowing management mode
  12982 // Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer)
  12983 // Gamepad:  Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)
  12984 static void ImGui::NavUpdateWindowing()
  12985 {
  12986     ImGuiContext& g = *GImGui;
  12987     ImGuiIO& io = g.IO;
  12988 
  12989     ImGuiWindow* apply_focus_window = NULL;
  12990     bool apply_toggle_layer = false;
  12991 
  12992     ImGuiWindow* modal_window = GetTopMostPopupModal();
  12993     bool allow_windowing = false;// (modal_window == NULL); // FIXME: This prevent CTRL+TAB from being usable with windows that are inside the Begin-stack of that modal.
  12994     if (!allow_windowing)
  12995         g.NavWindowingTarget = NULL;
  12996 
  12997     // Fade out
  12998     if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
  12999     {
  13000         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - io.DeltaTime * 10.0f, 0.0f);
  13001         if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
  13002             g.NavWindowingTargetAnim = NULL;
  13003     }
  13004 
  13005     // Start CTRL+Tab or Square+L/R window selection
  13006     // (g.ConfigNavWindowingKeyNext/g.ConfigNavWindowingKeyPrev defaults are ImGuiMod_Ctrl|ImGuiKey_Tab and ImGuiMod_Ctrl|ImGuiMod_Shift|ImGuiKey_Tab)
  13007     const ImGuiID owner_id = ImHashStr("###NavUpdateWindowing");
  13008     const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
  13009     const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
  13010     const bool keyboard_next_window = allow_windowing && g.ConfigNavWindowingKeyNext && Shortcut(g.ConfigNavWindowingKeyNext, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id);
  13011     const bool keyboard_prev_window = allow_windowing && g.ConfigNavWindowingKeyPrev && Shortcut(g.ConfigNavWindowingKeyPrev, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id);
  13012     const bool start_windowing_with_gamepad = allow_windowing && nav_gamepad_active && !g.NavWindowingTarget && IsKeyPressed(ImGuiKey_NavGamepadMenu, ImGuiInputFlags_None);
  13013     const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && (keyboard_next_window || keyboard_prev_window); // Note: enabled even without NavEnableKeyboard!
  13014     if (start_windowing_with_gamepad || start_windowing_with_keyboard)
  13015         if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
  13016         {
  13017             g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow;
  13018             g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
  13019             g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f);
  13020             g.NavWindowingToggleLayer = start_windowing_with_gamepad ? true : false; // Gamepad starts toggling layer
  13021             g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad;
  13022 
  13023             // Manually register ownership of our mods. Using a global route in the Shortcut() calls instead would probably be correct but may have more side-effects.
  13024             if (keyboard_next_window || keyboard_prev_window)
  13025                 SetKeyOwnersForKeyChord((g.ConfigNavWindowingKeyNext | g.ConfigNavWindowingKeyPrev) & ImGuiMod_Mask_, owner_id);
  13026         }
  13027 
  13028     // Gamepad update
  13029     g.NavWindowingTimer += io.DeltaTime;
  13030     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Gamepad)
  13031     {
  13032         // 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
  13033         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
  13034 
  13035         // Select window to focus
  13036         const int focus_change_dir = (int)IsKeyPressed(ImGuiKey_GamepadL1) - (int)IsKeyPressed(ImGuiKey_GamepadR1);
  13037         if (focus_change_dir != 0)
  13038         {
  13039             NavUpdateWindowingHighlightWindow(focus_change_dir);
  13040             g.NavWindowingHighlightAlpha = 1.0f;
  13041         }
  13042 
  13043         // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)
  13044         if (!IsKeyDown(ImGuiKey_NavGamepadMenu))
  13045         {
  13046             g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
  13047             if (g.NavWindowingToggleLayer && g.NavWindow)
  13048                 apply_toggle_layer = true;
  13049             else if (!g.NavWindowingToggleLayer)
  13050                 apply_focus_window = g.NavWindowingTarget;
  13051             g.NavWindowingTarget = NULL;
  13052         }
  13053     }
  13054 
  13055     // Keyboard: Focus
  13056     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Keyboard)
  13057     {
  13058         // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
  13059         ImGuiKeyChord shared_mods = ((g.ConfigNavWindowingKeyNext ? g.ConfigNavWindowingKeyNext : ImGuiMod_Mask_) & (g.ConfigNavWindowingKeyPrev ? g.ConfigNavWindowingKeyPrev : ImGuiMod_Mask_)) & ImGuiMod_Mask_;
  13060         IM_ASSERT(shared_mods != 0); // Next/Prev shortcut currently needs a shared modifier to "hold", otherwise Prev actions would keep cycling between two windows.
  13061         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
  13062         if (keyboard_next_window || keyboard_prev_window)
  13063             NavUpdateWindowingHighlightWindow(keyboard_next_window ? -1 : +1);
  13064         else if ((io.KeyMods & shared_mods) != shared_mods)
  13065             apply_focus_window = g.NavWindowingTarget;
  13066     }
  13067 
  13068     // Keyboard: Press and Release ALT to toggle menu layer
  13069     const ImGuiKey windowing_toggle_keys[] = { ImGuiKey_LeftAlt, ImGuiKey_RightAlt };
  13070     for (ImGuiKey windowing_toggle_key : windowing_toggle_keys)
  13071         if (nav_keyboard_active && IsKeyPressed(windowing_toggle_key, 0, ImGuiKeyOwner_NoOwner))
  13072         {
  13073             g.NavWindowingToggleLayer = true;
  13074             g.NavWindowingToggleKey = windowing_toggle_key;
  13075             g.NavInputSource = ImGuiInputSource_Keyboard;
  13076             break;
  13077         }
  13078     if (g.NavWindowingToggleLayer && g.NavInputSource == ImGuiInputSource_Keyboard)
  13079     {
  13080         // We cancel toggling nav layer when any text has been typed (generally while holding Alt). (See #370)
  13081         // We cancel toggling nav layer when other modifiers are pressed. (See #4439)
  13082         // - AltGR is Alt+Ctrl on some layout but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl).
  13083         // We cancel toggling nav layer if an owner has claimed the key.
  13084         if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper)
  13085             g.NavWindowingToggleLayer = false;
  13086         if (TestKeyOwner(g.NavWindowingToggleKey, ImGuiKeyOwner_NoOwner) == false || TestKeyOwner(ImGuiMod_Alt, ImGuiKeyOwner_NoOwner) == false)
  13087             g.NavWindowingToggleLayer = false;
  13088 
  13089         // Apply layer toggle on Alt release
  13090         // Important: as before version <18314 we lacked an explicit IO event for focus gain/loss, we also compare mouse validity to detect old backends clearing mouse pos on focus loss.
  13091         if (IsKeyReleased(g.NavWindowingToggleKey) && g.NavWindowingToggleLayer)
  13092             if (g.ActiveId == 0 || g.ActiveIdAllowOverlap)
  13093                 if (IsMousePosValid(&io.MousePos) == IsMousePosValid(&io.MousePosPrev))
  13094                     apply_toggle_layer = true;
  13095         if (!IsKeyDown(g.NavWindowingToggleKey))
  13096             g.NavWindowingToggleLayer = false;
  13097     }
  13098 
  13099     // Move window
  13100     if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
  13101     {
  13102         ImVec2 nav_move_dir;
  13103         if (g.NavInputSource == ImGuiInputSource_Keyboard && !io.KeyShift)
  13104             nav_move_dir = GetKeyMagnitude2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow);
  13105         if (g.NavInputSource == ImGuiInputSource_Gamepad)
  13106             nav_move_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown);
  13107         if (nav_move_dir.x != 0.0f || nav_move_dir.y != 0.0f)
  13108         {
  13109             const float NAV_MOVE_SPEED = 800.0f;
  13110             const float move_step = NAV_MOVE_SPEED * io.DeltaTime * ImMin(io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
  13111             g.NavWindowingAccumDeltaPos += nav_move_dir * move_step;
  13112             g.NavDisableMouseHover = true;
  13113             ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaPos);
  13114             if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)
  13115             {
  13116                 ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow;
  13117                 SetWindowPos(moving_window, moving_window->Pos + accum_floored, ImGuiCond_Always);
  13118                 g.NavWindowingAccumDeltaPos -= accum_floored;
  13119             }
  13120         }
  13121     }
  13122 
  13123     // Apply final focus
  13124     if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
  13125     {
  13126         ClearActiveID();
  13127         NavRestoreHighlightAfterMove();
  13128         ClosePopupsOverWindow(apply_focus_window, false);
  13129         FocusWindow(apply_focus_window, ImGuiFocusRequestFlags_RestoreFocusedChild);
  13130         apply_focus_window = g.NavWindow;
  13131         if (apply_focus_window->NavLastIds[0] == 0)
  13132             NavInitWindow(apply_focus_window, false);
  13133 
  13134         // If the window has ONLY a menu layer (no main layer), select it directly
  13135         // Use NavLayersActiveMaskNext since windows didn't have a chance to be Begin()-ed on this frame,
  13136         // so CTRL+Tab where the keys are only held for 1 frame will be able to use correct layers mask since
  13137         // the target window as already been previewed once.
  13138         // FIXME-NAV: This should be done in NavInit.. or in FocusWindow... However in both of those cases,
  13139         // we won't have a guarantee that windows has been visible before and therefore NavLayersActiveMask*
  13140         // won't be valid.
  13141         if (apply_focus_window->DC.NavLayersActiveMaskNext == (1 << ImGuiNavLayer_Menu))
  13142             g.NavLayer = ImGuiNavLayer_Menu;
  13143     }
  13144     if (apply_focus_window)
  13145         g.NavWindowingTarget = NULL;
  13146 
  13147     // Apply menu/layer toggle
  13148     if (apply_toggle_layer && g.NavWindow)
  13149     {
  13150         ClearActiveID();
  13151 
  13152         // Move to parent menu if necessary
  13153         ImGuiWindow* new_nav_window = g.NavWindow;
  13154         while (new_nav_window->ParentWindow
  13155             && (new_nav_window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0
  13156             && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
  13157             && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
  13158             new_nav_window = new_nav_window->ParentWindow;
  13159         if (new_nav_window != g.NavWindow)
  13160         {
  13161             ImGuiWindow* old_nav_window = g.NavWindow;
  13162             FocusWindow(new_nav_window);
  13163             new_nav_window->NavLastChildNavWindow = old_nav_window;
  13164         }
  13165 
  13166         // Toggle layer
  13167         const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;
  13168         if (new_nav_layer != g.NavLayer)
  13169         {
  13170             // Reinitialize navigation when entering menu bar with the Alt key (FIXME: could be a properly of the layer?)
  13171             if (new_nav_layer == ImGuiNavLayer_Menu)
  13172                 g.NavWindow->NavLastIds[new_nav_layer] = 0;
  13173             NavRestoreLayer(new_nav_layer);
  13174             NavRestoreHighlightAfterMove();
  13175         }
  13176     }
  13177 }
  13178 
  13179 // Window has already passed the IsWindowNavFocusable()
  13180 static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
  13181 {
  13182     if (window->Flags & ImGuiWindowFlags_Popup)
  13183         return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingPopup);
  13184     if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
  13185         return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingMainMenuBar);
  13186     return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingUntitled);
  13187 }
  13188 
  13189 // Overlay displayed when using CTRL+TAB. Called by EndFrame().
  13190 void ImGui::NavUpdateWindowingOverlay()
  13191 {
  13192     ImGuiContext& g = *GImGui;
  13193     IM_ASSERT(g.NavWindowingTarget != NULL);
  13194 
  13195     if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
  13196         return;
  13197 
  13198     if (g.NavWindowingListWindow == NULL)
  13199         g.NavWindowingListWindow = FindWindowByName("###NavWindowingList");
  13200     const ImGuiViewport* viewport = GetMainViewport();
  13201     SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
  13202     SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
  13203     PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
  13204     Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
  13205     if (g.ContextName[0] != 0)
  13206         SeparatorText(g.ContextName);
  13207     for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
  13208     {
  13209         ImGuiWindow* window = g.WindowsFocusOrder[n];
  13210         IM_ASSERT(window != NULL); // Fix static analyzers
  13211         if (!IsWindowNavFocusable(window))
  13212             continue;
  13213         const char* label = window->Name;
  13214         if (label == FindRenderedTextEnd(label))
  13215             label = GetFallbackWindowNameForWindowingList(window);
  13216         Selectable(label, g.NavWindowingTarget == window);
  13217     }
  13218     End();
  13219     PopStyleVar();
  13220 }
  13221 
  13222 
  13223 //-----------------------------------------------------------------------------
  13224 // [SECTION] DRAG AND DROP
  13225 //-----------------------------------------------------------------------------
  13226 
  13227 bool ImGui::IsDragDropActive()
  13228 {
  13229     ImGuiContext& g = *GImGui;
  13230     return g.DragDropActive;
  13231 }
  13232 
  13233 void ImGui::ClearDragDrop()
  13234 {
  13235     ImGuiContext& g = *GImGui;
  13236     if (g.DragDropActive)
  13237         IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] ClearDragDrop()\n");
  13238     g.DragDropActive = false;
  13239     g.DragDropPayload.Clear();
  13240     g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
  13241     g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
  13242     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
  13243     g.DragDropAcceptFrameCount = -1;
  13244 
  13245     g.DragDropPayloadBufHeap.clear();
  13246     memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
  13247 }
  13248 
  13249 bool ImGui::BeginTooltipHidden()
  13250 {
  13251     ImGuiContext& g = *GImGui;
  13252     bool ret = Begin("##Tooltip_Hidden", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize);
  13253     SetWindowHiddenAndSkipItemsForCurrentFrame(g.CurrentWindow);
  13254     return ret;
  13255 }
  13256 
  13257 // When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
  13258 // If the item has an identifier:
  13259 // - This assume/require the item to be activated (typically via ButtonBehavior).
  13260 // - Therefore if you want to use this with a mouse button other than left mouse button, it is up to the item itself to activate with another button.
  13261 // - We then pull and use the mouse button that was used to activate the item and use it to carry on the drag.
  13262 // If the item has no identifier:
  13263 // - Currently always assume left mouse button.
  13264 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
  13265 {
  13266     ImGuiContext& g = *GImGui;
  13267     ImGuiWindow* window = g.CurrentWindow;
  13268 
  13269     // FIXME-DRAGDROP: While in the common-most "drag from non-zero active id" case we can tell the mouse button,
  13270     // in both SourceExtern and id==0 cases we may requires something else (explicit flags or some heuristic).
  13271     ImGuiMouseButton mouse_button = ImGuiMouseButton_Left;
  13272 
  13273     bool source_drag_active = false;
  13274     ImGuiID source_id = 0;
  13275     ImGuiID source_parent_id = 0;
  13276     if ((flags & ImGuiDragDropFlags_SourceExtern) == 0)
  13277     {
  13278         source_id = g.LastItemData.ID;
  13279         if (source_id != 0)
  13280         {
  13281             // Common path: items with ID
  13282             if (g.ActiveId != source_id)
  13283                 return false;
  13284             if (g.ActiveIdMouseButton != -1)
  13285                 mouse_button = g.ActiveIdMouseButton;
  13286             if (g.IO.MouseDown[mouse_button] == false || window->SkipItems)
  13287                 return false;
  13288             g.ActiveIdAllowOverlap = false;
  13289         }
  13290         else
  13291         {
  13292             // Uncommon path: items without ID
  13293             if (g.IO.MouseDown[mouse_button] == false || window->SkipItems)
  13294                 return false;
  13295             if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
  13296                 return false;
  13297 
  13298             // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
  13299             // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag.
  13300             if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
  13301             {
  13302                 IM_ASSERT(0);
  13303                 return false;
  13304             }
  13305 
  13306             // Magic fallback to handle items with no assigned ID, e.g. Text(), Image()
  13307             // We build a throwaway ID based on current ID stack + relative AABB of items in window.
  13308             // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING/RESIZINGG OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
  13309             // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
  13310             // Rely on keeping other window->LastItemXXX fields intact.
  13311             source_id = g.LastItemData.ID = window->GetIDFromRectangle(g.LastItemData.Rect);
  13312             KeepAliveID(source_id);
  13313             bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id, g.LastItemData.InFlags);
  13314             if (is_hovered && g.IO.MouseClicked[mouse_button])
  13315             {
  13316                 SetActiveID(source_id, window);
  13317                 FocusWindow(window);
  13318             }
  13319             if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
  13320                 g.ActiveIdAllowOverlap = is_hovered;
  13321         }
  13322         if (g.ActiveId != source_id)
  13323             return false;
  13324         source_parent_id = window->IDStack.back();
  13325         source_drag_active = IsMouseDragging(mouse_button);
  13326 
  13327         // Disable navigation and key inputs while dragging + cancel existing request if any
  13328         SetActiveIdUsingAllKeyboardKeys();
  13329     }
  13330     else
  13331     {
  13332         // When ImGuiDragDropFlags_SourceExtern is set:
  13333         window = NULL;
  13334         source_id = ImHashStr("#SourceExtern");
  13335         source_drag_active = true;
  13336         mouse_button = g.IO.MouseDown[0] ? 0 : -1;
  13337         KeepAliveID(source_id);
  13338         SetActiveID(source_id, NULL);
  13339     }
  13340 
  13341     IM_ASSERT(g.DragDropWithinTarget == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
  13342     if (!source_drag_active)
  13343         return false;
  13344 
  13345     // Activate drag and drop
  13346     if (!g.DragDropActive)
  13347     {
  13348         IM_ASSERT(source_id != 0);
  13349         ClearDragDrop();
  13350         IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] BeginDragDropSource() DragDropActive = true, source_id = 0x%08X%s\n",
  13351             source_id, (flags & ImGuiDragDropFlags_SourceExtern) ? " (EXTERN)" : "");
  13352         ImGuiPayload& payload = g.DragDropPayload;
  13353         payload.SourceId = source_id;
  13354         payload.SourceParentId = source_parent_id;
  13355         g.DragDropActive = true;
  13356         g.DragDropSourceFlags = flags;
  13357         g.DragDropMouseButton = mouse_button;
  13358         if (payload.SourceId == g.ActiveId)
  13359             g.ActiveIdNoClearOnFocusLoss = true;
  13360     }
  13361     g.DragDropSourceFrameCount = g.FrameCount;
  13362     g.DragDropWithinSource = true;
  13363 
  13364     if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
  13365     {
  13366         // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
  13367         // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
  13368         bool ret;
  13369         if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
  13370             ret = BeginTooltipHidden();
  13371         else
  13372             ret = BeginTooltip();
  13373         IM_ASSERT(ret); // FIXME-NEWBEGIN: If this ever becomes false, we need to Begin("##Hidden", NULL, ImGuiWindowFlags_NoSavedSettings) + SetWindowHiddendAndSkipItemsForCurrentFrame().
  13374         IM_UNUSED(ret);
  13375     }
  13376 
  13377     if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
  13378         g.LastItemData.StatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
  13379 
  13380     return true;
  13381 }
  13382 
  13383 void ImGui::EndDragDropSource()
  13384 {
  13385     ImGuiContext& g = *GImGui;
  13386     IM_ASSERT(g.DragDropActive);
  13387     IM_ASSERT(g.DragDropWithinSource && "Not after a BeginDragDropSource()?");
  13388 
  13389     if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
  13390         EndTooltip();
  13391 
  13392     // Discard the drag if have not called SetDragDropPayload()
  13393     if (g.DragDropPayload.DataFrameCount == -1)
  13394         ClearDragDrop();
  13395     g.DragDropWithinSource = false;
  13396 }
  13397 
  13398 // Use 'cond' to choose to submit payload on drag start or every frame
  13399 bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
  13400 {
  13401     ImGuiContext& g = *GImGui;
  13402     ImGuiPayload& payload = g.DragDropPayload;
  13403     if (cond == 0)
  13404         cond = ImGuiCond_Always;
  13405 
  13406     IM_ASSERT(type != NULL);
  13407     IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
  13408     IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
  13409     IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
  13410     IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource()
  13411 
  13412     if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
  13413     {
  13414         // Copy payload
  13415         ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
  13416         g.DragDropPayloadBufHeap.resize(0);
  13417         if (data_size > sizeof(g.DragDropPayloadBufLocal))
  13418         {
  13419             // Store in heap
  13420             g.DragDropPayloadBufHeap.resize((int)data_size);
  13421             payload.Data = g.DragDropPayloadBufHeap.Data;
  13422             memcpy(payload.Data, data, data_size);
  13423         }
  13424         else if (data_size > 0)
  13425         {
  13426             // Store locally
  13427             memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
  13428             payload.Data = g.DragDropPayloadBufLocal;
  13429             memcpy(payload.Data, data, data_size);
  13430         }
  13431         else
  13432         {
  13433             payload.Data = NULL;
  13434         }
  13435         payload.DataSize = (int)data_size;
  13436     }
  13437     payload.DataFrameCount = g.FrameCount;
  13438 
  13439     // Return whether the payload has been accepted
  13440     return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
  13441 }
  13442 
  13443 bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
  13444 {
  13445     ImGuiContext& g = *GImGui;
  13446     if (!g.DragDropActive)
  13447         return false;
  13448 
  13449     ImGuiWindow* window = g.CurrentWindow;
  13450     ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
  13451     if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow)
  13452         return false;
  13453     IM_ASSERT(id != 0);
  13454     if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
  13455         return false;
  13456     if (window->SkipItems)
  13457         return false;
  13458 
  13459     IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
  13460     g.DragDropTargetRect = bb;
  13461     g.DragDropTargetClipRect = window->ClipRect; // May want to be overridden by user depending on use case?
  13462     g.DragDropTargetId = id;
  13463     g.DragDropWithinTarget = true;
  13464     return true;
  13465 }
  13466 
  13467 // We don't use BeginDragDropTargetCustom() and duplicate its code because:
  13468 // 1) we use LastItemData's ImGuiItemStatusFlags_HoveredRect which handles items that push a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
  13469 // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
  13470 // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
  13471 bool ImGui::BeginDragDropTarget()
  13472 {
  13473     ImGuiContext& g = *GImGui;
  13474     if (!g.DragDropActive)
  13475         return false;
  13476 
  13477     ImGuiWindow* window = g.CurrentWindow;
  13478     if (!(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect))
  13479         return false;
  13480     ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
  13481     if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow || window->SkipItems)
  13482         return false;
  13483 
  13484     const ImRect& display_rect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? g.LastItemData.DisplayRect : g.LastItemData.Rect;
  13485     ImGuiID id = g.LastItemData.ID;
  13486     if (id == 0)
  13487     {
  13488         id = window->GetIDFromRectangle(display_rect);
  13489         KeepAliveID(id);
  13490     }
  13491     if (g.DragDropPayload.SourceId == id)
  13492         return false;
  13493 
  13494     IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
  13495     g.DragDropTargetRect = display_rect;
  13496     g.DragDropTargetClipRect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasClipRect) ? g.LastItemData.ClipRect : window->ClipRect;
  13497     g.DragDropTargetId = id;
  13498     g.DragDropWithinTarget = true;
  13499     return true;
  13500 }
  13501 
  13502 bool ImGui::IsDragDropPayloadBeingAccepted()
  13503 {
  13504     ImGuiContext& g = *GImGui;
  13505     return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
  13506 }
  13507 
  13508 const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
  13509 {
  13510     ImGuiContext& g = *GImGui;
  13511     ImGuiPayload& payload = g.DragDropPayload;
  13512     IM_ASSERT(g.DragDropActive);                        // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
  13513     IM_ASSERT(payload.DataFrameCount != -1);            // Forgot to call EndDragDropTarget() ?
  13514     if (type != NULL && !payload.IsDataType(type))
  13515         return NULL;
  13516 
  13517     // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
  13518     // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
  13519     const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
  13520     ImRect r = g.DragDropTargetRect;
  13521     float r_surface = r.GetWidth() * r.GetHeight();
  13522     if (r_surface > g.DragDropAcceptIdCurrRectSurface)
  13523         return NULL;
  13524 
  13525     g.DragDropAcceptFlags = flags;
  13526     g.DragDropAcceptIdCurr = g.DragDropTargetId;
  13527     g.DragDropAcceptIdCurrRectSurface = r_surface;
  13528     //IMGUI_DEBUG_LOG("AcceptDragDropPayload(): %08X: accept\n", g.DragDropTargetId);
  13529 
  13530     // Render default drop visuals
  13531     payload.Preview = was_accepted_previously;
  13532     flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that live for 1 frame)
  13533     if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
  13534         RenderDragDropTargetRect(r, g.DragDropTargetClipRect);
  13535 
  13536     g.DragDropAcceptFrameCount = g.FrameCount;
  13537     if ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) && g.DragDropMouseButton == -1)
  13538         payload.Delivery = was_accepted_previously && (g.DragDropSourceFrameCount < g.FrameCount);
  13539     else
  13540         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()
  13541     if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
  13542         return NULL;
  13543 
  13544     if (payload.Delivery)
  13545         IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] AcceptDragDropPayload(): 0x%08X: payload delivery\n", g.DragDropTargetId);
  13546     return &payload;
  13547 }
  13548 
  13549 // FIXME-STYLE FIXME-DRAGDROP: Settle on a proper default visuals for drop target.
  13550 void ImGui::RenderDragDropTargetRect(const ImRect& bb, const ImRect& item_clip_rect)
  13551 {
  13552     ImGuiContext& g = *GImGui;
  13553     ImGuiWindow* window = g.CurrentWindow;
  13554     ImRect bb_display = bb;
  13555     bb_display.ClipWith(item_clip_rect); // Clip THEN expand so we have a way to visualize that target is not entirely visible.
  13556     bb_display.Expand(3.5f);
  13557     bool push_clip_rect = !window->ClipRect.Contains(bb_display);
  13558     if (push_clip_rect)
  13559         window->DrawList->PushClipRectFullScreen();
  13560     window->DrawList->AddRect(bb_display.Min, bb_display.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f);
  13561     if (push_clip_rect)
  13562         window->DrawList->PopClipRect();
  13563 }
  13564 
  13565 const ImGuiPayload* ImGui::GetDragDropPayload()
  13566 {
  13567     ImGuiContext& g = *GImGui;
  13568     return (g.DragDropActive && g.DragDropPayload.DataFrameCount != -1) ? &g.DragDropPayload : NULL;
  13569 }
  13570 
  13571 void ImGui::EndDragDropTarget()
  13572 {
  13573     ImGuiContext& g = *GImGui;
  13574     IM_ASSERT(g.DragDropActive);
  13575     IM_ASSERT(g.DragDropWithinTarget);
  13576     g.DragDropWithinTarget = false;
  13577 
  13578     // Clear drag and drop state payload right after delivery
  13579     if (g.DragDropPayload.Delivery)
  13580         ClearDragDrop();
  13581 }
  13582 
  13583 //-----------------------------------------------------------------------------
  13584 // [SECTION] LOGGING/CAPTURING
  13585 //-----------------------------------------------------------------------------
  13586 // All text output from the interface can be captured into tty/file/clipboard.
  13587 // By default, tree nodes are automatically opened during logging.
  13588 //-----------------------------------------------------------------------------
  13589 
  13590 // Pass text data straight to log (without being displayed)
  13591 static inline void LogTextV(ImGuiContext& g, const char* fmt, va_list args)
  13592 {
  13593     if (g.LogFile)
  13594     {
  13595         g.LogBuffer.Buf.resize(0);
  13596         g.LogBuffer.appendfv(fmt, args);
  13597         ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile);
  13598     }
  13599     else
  13600     {
  13601         g.LogBuffer.appendfv(fmt, args);
  13602     }
  13603 }
  13604 
  13605 void ImGui::LogText(const char* fmt, ...)
  13606 {
  13607     ImGuiContext& g = *GImGui;
  13608     if (!g.LogEnabled)
  13609         return;
  13610 
  13611     va_list args;
  13612     va_start(args, fmt);
  13613     LogTextV(g, fmt, args);
  13614     va_end(args);
  13615 }
  13616 
  13617 void ImGui::LogTextV(const char* fmt, va_list args)
  13618 {
  13619     ImGuiContext& g = *GImGui;
  13620     if (!g.LogEnabled)
  13621         return;
  13622 
  13623     LogTextV(g, fmt, args);
  13624 }
  13625 
  13626 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
  13627 // We split text into individual lines to add current tree level padding
  13628 // FIXME: This code is a little complicated perhaps, considering simplifying the whole system.
  13629 void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
  13630 {
  13631     ImGuiContext& g = *GImGui;
  13632     ImGuiWindow* window = g.CurrentWindow;
  13633 
  13634     const char* prefix = g.LogNextPrefix;
  13635     const char* suffix = g.LogNextSuffix;
  13636     g.LogNextPrefix = g.LogNextSuffix = NULL;
  13637 
  13638     if (!text_end)
  13639         text_end = FindRenderedTextEnd(text, text_end);
  13640 
  13641     const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + g.Style.FramePadding.y + 1);
  13642     if (ref_pos)
  13643         g.LogLinePosY = ref_pos->y;
  13644     if (log_new_line)
  13645     {
  13646         LogText(IM_NEWLINE);
  13647         g.LogLineFirstItem = true;
  13648     }
  13649 
  13650     if (prefix)
  13651         LogRenderedText(ref_pos, prefix, prefix + strlen(prefix)); // Calculate end ourself to ensure "##" are included here.
  13652 
  13653     // Re-adjust padding if we have popped out of our starting depth
  13654     if (g.LogDepthRef > window->DC.TreeDepth)
  13655         g.LogDepthRef = window->DC.TreeDepth;
  13656     const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);
  13657 
  13658     const char* text_remaining = text;
  13659     for (;;)
  13660     {
  13661         // Split the string. Each new line (after a '\n') is followed by indentation corresponding to the current depth of our log entry.
  13662         // We don't add a trailing \n yet to allow a subsequent item on the same line to be captured.
  13663         const char* line_start = text_remaining;
  13664         const char* line_end = ImStreolRange(line_start, text_end);
  13665         const bool is_last_line = (line_end == text_end);
  13666         if (line_start != line_end || !is_last_line)
  13667         {
  13668             const int line_length = (int)(line_end - line_start);
  13669             const int indentation = g.LogLineFirstItem ? tree_depth * 4 : 1;
  13670             LogText("%*s%.*s", indentation, "", line_length, line_start);
  13671             g.LogLineFirstItem = false;
  13672             if (*line_end == '\n')
  13673             {
  13674                 LogText(IM_NEWLINE);
  13675                 g.LogLineFirstItem = true;
  13676             }
  13677         }
  13678         if (is_last_line)
  13679             break;
  13680         text_remaining = line_end + 1;
  13681     }
  13682 
  13683     if (suffix)
  13684         LogRenderedText(ref_pos, suffix, suffix + strlen(suffix));
  13685 }
  13686 
  13687 // Start logging/capturing text output
  13688 void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth)
  13689 {
  13690     ImGuiContext& g = *GImGui;
  13691     ImGuiWindow* window = g.CurrentWindow;
  13692     IM_ASSERT(g.LogEnabled == false);
  13693     IM_ASSERT(g.LogFile == NULL);
  13694     IM_ASSERT(g.LogBuffer.empty());
  13695     g.LogEnabled = g.ItemUnclipByLog = true;
  13696     g.LogType = type;
  13697     g.LogNextPrefix = g.LogNextSuffix = NULL;
  13698     g.LogDepthRef = window->DC.TreeDepth;
  13699     g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);
  13700     g.LogLinePosY = FLT_MAX;
  13701     g.LogLineFirstItem = true;
  13702 }
  13703 
  13704 // Important: doesn't copy underlying data, use carefully (prefix/suffix must be in scope at the time of the next LogRenderedText)
  13705 void ImGui::LogSetNextTextDecoration(const char* prefix, const char* suffix)
  13706 {
  13707     ImGuiContext& g = *GImGui;
  13708     g.LogNextPrefix = prefix;
  13709     g.LogNextSuffix = suffix;
  13710 }
  13711 
  13712 void ImGui::LogToTTY(int auto_open_depth)
  13713 {
  13714     ImGuiContext& g = *GImGui;
  13715     if (g.LogEnabled)
  13716         return;
  13717     IM_UNUSED(auto_open_depth);
  13718 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
  13719     LogBegin(ImGuiLogType_TTY, auto_open_depth);
  13720     g.LogFile = stdout;
  13721 #endif
  13722 }
  13723 
  13724 // Start logging/capturing text output to given file
  13725 void ImGui::LogToFile(int auto_open_depth, const char* filename)
  13726 {
  13727     ImGuiContext& g = *GImGui;
  13728     if (g.LogEnabled)
  13729         return;
  13730 
  13731     // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still
  13732     // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE.
  13733     // By opening the file in binary mode "ab" we have consistent output everywhere.
  13734     if (!filename)
  13735         filename = g.IO.LogFilename;
  13736     if (!filename || !filename[0])
  13737         return;
  13738     ImFileHandle f = ImFileOpen(filename, "ab");
  13739     if (!f)
  13740     {
  13741         IM_ASSERT(0);
  13742         return;
  13743     }
  13744 
  13745     LogBegin(ImGuiLogType_File, auto_open_depth);
  13746     g.LogFile = f;
  13747 }
  13748 
  13749 // Start logging/capturing text output to clipboard
  13750 void ImGui::LogToClipboard(int auto_open_depth)
  13751 {
  13752     ImGuiContext& g = *GImGui;
  13753     if (g.LogEnabled)
  13754         return;
  13755     LogBegin(ImGuiLogType_Clipboard, auto_open_depth);
  13756 }
  13757 
  13758 void ImGui::LogToBuffer(int auto_open_depth)
  13759 {
  13760     ImGuiContext& g = *GImGui;
  13761     if (g.LogEnabled)
  13762         return;
  13763     LogBegin(ImGuiLogType_Buffer, auto_open_depth);
  13764 }
  13765 
  13766 void ImGui::LogFinish()
  13767 {
  13768     ImGuiContext& g = *GImGui;
  13769     if (!g.LogEnabled)
  13770         return;
  13771 
  13772     LogText(IM_NEWLINE);
  13773     switch (g.LogType)
  13774     {
  13775     case ImGuiLogType_TTY:
  13776 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
  13777         fflush(g.LogFile);
  13778 #endif
  13779         break;
  13780     case ImGuiLogType_File:
  13781         ImFileClose(g.LogFile);
  13782         break;
  13783     case ImGuiLogType_Buffer:
  13784         break;
  13785     case ImGuiLogType_Clipboard:
  13786         if (!g.LogBuffer.empty())
  13787             SetClipboardText(g.LogBuffer.begin());
  13788         break;
  13789     case ImGuiLogType_None:
  13790         IM_ASSERT(0);
  13791         break;
  13792     }
  13793 
  13794     g.LogEnabled = g.ItemUnclipByLog = false;
  13795     g.LogType = ImGuiLogType_None;
  13796     g.LogFile = NULL;
  13797     g.LogBuffer.clear();
  13798 }
  13799 
  13800 // Helper to display logging buttons
  13801 // FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!)
  13802 void ImGui::LogButtons()
  13803 {
  13804     ImGuiContext& g = *GImGui;
  13805 
  13806     PushID("LogButtons");
  13807 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
  13808     const bool log_to_tty = Button("Log To TTY"); SameLine();
  13809 #else
  13810     const bool log_to_tty = false;
  13811 #endif
  13812     const bool log_to_file = Button("Log To File"); SameLine();
  13813     const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
  13814     PushItemFlag(ImGuiItemFlags_NoTabStop, true);
  13815     SetNextItemWidth(80.0f);
  13816     SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL);
  13817     PopItemFlag();
  13818     PopID();
  13819 
  13820     // Start logging at the end of the function so that the buttons don't appear in the log
  13821     if (log_to_tty)
  13822         LogToTTY();
  13823     if (log_to_file)
  13824         LogToFile();
  13825     if (log_to_clipboard)
  13826         LogToClipboard();
  13827 }
  13828 
  13829 
  13830 //-----------------------------------------------------------------------------
  13831 // [SECTION] SETTINGS
  13832 //-----------------------------------------------------------------------------
  13833 // - UpdateSettings() [Internal]
  13834 // - MarkIniSettingsDirty() [Internal]
  13835 // - FindSettingsHandler() [Internal]
  13836 // - ClearIniSettings() [Internal]
  13837 // - LoadIniSettingsFromDisk()
  13838 // - LoadIniSettingsFromMemory()
  13839 // - SaveIniSettingsToDisk()
  13840 // - SaveIniSettingsToMemory()
  13841 //-----------------------------------------------------------------------------
  13842 // - CreateNewWindowSettings() [Internal]
  13843 // - FindWindowSettingsByID() [Internal]
  13844 // - FindWindowSettingsByWindow() [Internal]
  13845 // - ClearWindowSettings() [Internal]
  13846 // - WindowSettingsHandler_***() [Internal]
  13847 //-----------------------------------------------------------------------------
  13848 
  13849 // Called by NewFrame()
  13850 void ImGui::UpdateSettings()
  13851 {
  13852     // Load settings on first frame (if not explicitly loaded manually before)
  13853     ImGuiContext& g = *GImGui;
  13854     if (!g.SettingsLoaded)
  13855     {
  13856         IM_ASSERT(g.SettingsWindows.empty());
  13857         if (g.IO.IniFilename)
  13858             LoadIniSettingsFromDisk(g.IO.IniFilename);
  13859         g.SettingsLoaded = true;
  13860     }
  13861 
  13862     // Save settings (with a delay after the last modification, so we don't spam disk too much)
  13863     if (g.SettingsDirtyTimer > 0.0f)
  13864     {
  13865         g.SettingsDirtyTimer -= g.IO.DeltaTime;
  13866         if (g.SettingsDirtyTimer <= 0.0f)
  13867         {
  13868             if (g.IO.IniFilename != NULL)
  13869                 SaveIniSettingsToDisk(g.IO.IniFilename);
  13870             else
  13871                 g.IO.WantSaveIniSettings = true;  // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
  13872             g.SettingsDirtyTimer = 0.0f;
  13873         }
  13874     }
  13875 }
  13876 
  13877 void ImGui::MarkIniSettingsDirty()
  13878 {
  13879     ImGuiContext& g = *GImGui;
  13880     if (g.SettingsDirtyTimer <= 0.0f)
  13881         g.SettingsDirtyTimer = g.IO.IniSavingRate;
  13882 }
  13883 
  13884 void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
  13885 {
  13886     ImGuiContext& g = *GImGui;
  13887     if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
  13888         if (g.SettingsDirtyTimer <= 0.0f)
  13889             g.SettingsDirtyTimer = g.IO.IniSavingRate;
  13890 }
  13891 
  13892 void ImGui::AddSettingsHandler(const ImGuiSettingsHandler* handler)
  13893 {
  13894     ImGuiContext& g = *GImGui;
  13895     IM_ASSERT(FindSettingsHandler(handler->TypeName) == NULL);
  13896     g.SettingsHandlers.push_back(*handler);
  13897 }
  13898 
  13899 void ImGui::RemoveSettingsHandler(const char* type_name)
  13900 {
  13901     ImGuiContext& g = *GImGui;
  13902     if (ImGuiSettingsHandler* handler = FindSettingsHandler(type_name))
  13903         g.SettingsHandlers.erase(handler);
  13904 }
  13905 
  13906 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
  13907 {
  13908     ImGuiContext& g = *GImGui;
  13909     const ImGuiID type_hash = ImHashStr(type_name);
  13910     for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
  13911         if (handler.TypeHash == type_hash)
  13912             return &handler;
  13913     return NULL;
  13914 }
  13915 
  13916 // Clear all settings (windows, tables, docking etc.)
  13917 void ImGui::ClearIniSettings()
  13918 {
  13919     ImGuiContext& g = *GImGui;
  13920     g.SettingsIniData.clear();
  13921     for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
  13922         if (handler.ClearAllFn != NULL)
  13923             handler.ClearAllFn(&g, &handler);
  13924 }
  13925 
  13926 void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
  13927 {
  13928     size_t file_data_size = 0;
  13929     char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
  13930     if (!file_data)
  13931         return;
  13932     if (file_data_size > 0)
  13933         LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
  13934     IM_FREE(file_data);
  13935 }
  13936 
  13937 // Zero-tolerance, no error reporting, cheap .ini parsing
  13938 // Set ini_size==0 to let us use strlen(ini_data). Do not call this function with a 0 if your buffer is actually empty!
  13939 void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
  13940 {
  13941     ImGuiContext& g = *GImGui;
  13942     IM_ASSERT(g.Initialized);
  13943     //IM_ASSERT(!g.WithinFrameScope && "Cannot be called between NewFrame() and EndFrame()");
  13944     //IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
  13945 
  13946     // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
  13947     // 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..
  13948     if (ini_size == 0)
  13949         ini_size = strlen(ini_data);
  13950     g.SettingsIniData.Buf.resize((int)ini_size + 1);
  13951     char* const buf = g.SettingsIniData.Buf.Data;
  13952     char* const buf_end = buf + ini_size;
  13953     memcpy(buf, ini_data, ini_size);
  13954     buf_end[0] = 0;
  13955 
  13956     // Call pre-read handlers
  13957     // Some types will clear their data (e.g. dock information) some types will allow merge/override (window)
  13958     for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
  13959         if (handler.ReadInitFn != NULL)
  13960             handler.ReadInitFn(&g, &handler);
  13961 
  13962     void* entry_data = NULL;
  13963     ImGuiSettingsHandler* entry_handler = NULL;
  13964 
  13965     char* line_end = NULL;
  13966     for (char* line = buf; line < buf_end; line = line_end + 1)
  13967     {
  13968         // Skip new lines markers, then find end of the line
  13969         while (*line == '\n' || *line == '\r')
  13970             line++;
  13971         line_end = line;
  13972         while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
  13973             line_end++;
  13974         line_end[0] = 0;
  13975         if (line[0] == ';')
  13976             continue;
  13977         if (line[0] == '[' && line_end > line && line_end[-1] == ']')
  13978         {
  13979             // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
  13980             line_end[-1] = 0;
  13981             const char* name_end = line_end - 1;
  13982             const char* type_start = line + 1;
  13983             char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']');
  13984             const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
  13985             if (!type_end || !name_start)
  13986                 continue;
  13987             *type_end = 0; // Overwrite first ']'
  13988             name_start++;  // Skip second '['
  13989             entry_handler = FindSettingsHandler(type_start);
  13990             entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
  13991         }
  13992         else if (entry_handler != NULL && entry_data != NULL)
  13993         {
  13994             // Let type handler parse the line
  13995             entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
  13996         }
  13997     }
  13998     g.SettingsLoaded = true;
  13999 
  14000     // [DEBUG] Restore untouched copy so it can be browsed in Metrics (not strictly necessary)
  14001     memcpy(buf, ini_data, ini_size);
  14002 
  14003     // Call post-read handlers
  14004     for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
  14005         if (handler.ApplyAllFn != NULL)
  14006             handler.ApplyAllFn(&g, &handler);
  14007 }
  14008 
  14009 void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
  14010 {
  14011     ImGuiContext& g = *GImGui;
  14012     g.SettingsDirtyTimer = 0.0f;
  14013     if (!ini_filename)
  14014         return;
  14015 
  14016     size_t ini_data_size = 0;
  14017     const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
  14018     ImFileHandle f = ImFileOpen(ini_filename, "wt");
  14019     if (!f)
  14020         return;
  14021     ImFileWrite(ini_data, sizeof(char), ini_data_size, f);
  14022     ImFileClose(f);
  14023 }
  14024 
  14025 // Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
  14026 const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
  14027 {
  14028     ImGuiContext& g = *GImGui;
  14029     g.SettingsDirtyTimer = 0.0f;
  14030     g.SettingsIniData.Buf.resize(0);
  14031     g.SettingsIniData.Buf.push_back(0);
  14032     for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
  14033         handler.WriteAllFn(&g, &handler, &g.SettingsIniData);
  14034     if (out_size)
  14035         *out_size = (size_t)g.SettingsIniData.size();
  14036     return g.SettingsIniData.c_str();
  14037 }
  14038 
  14039 ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
  14040 {
  14041     ImGuiContext& g = *GImGui;
  14042 
  14043     if (g.IO.ConfigDebugIniSettings == false)
  14044     {
  14045         // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
  14046         // Preserve the full string when ConfigDebugVerboseIniSettings is set to make .ini inspection easier.
  14047         if (const char* p = strstr(name, "###"))
  14048             name = p;
  14049     }
  14050     const size_t name_len = strlen(name);
  14051 
  14052     // Allocate chunk
  14053     const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1;
  14054     ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size);
  14055     IM_PLACEMENT_NEW(settings) ImGuiWindowSettings();
  14056     settings->ID = ImHashStr(name, name_len);
  14057     memcpy(settings->GetName(), name, name_len + 1);   // Store with zero terminator
  14058 
  14059     return settings;
  14060 }
  14061 
  14062 // We don't provide a FindWindowSettingsByName() because Docking system doesn't always hold on names.
  14063 // This is called once per window .ini entry + once per newly instantiated window.
  14064 ImGuiWindowSettings* ImGui::FindWindowSettingsByID(ImGuiID id)
  14065 {
  14066     ImGuiContext& g = *GImGui;
  14067     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
  14068         if (settings->ID == id && !settings->WantDelete)
  14069             return settings;
  14070     return NULL;
  14071 }
  14072 
  14073 // This is faster if you are holding on a Window already as we don't need to perform a search.
  14074 ImGuiWindowSettings* ImGui::FindWindowSettingsByWindow(ImGuiWindow* window)
  14075 {
  14076     ImGuiContext& g = *GImGui;
  14077     if (window->SettingsOffset != -1)
  14078         return g.SettingsWindows.ptr_from_offset(window->SettingsOffset);
  14079     return FindWindowSettingsByID(window->ID);
  14080 }
  14081 
  14082 // This will revert window to its initial state, including enabling the ImGuiCond_FirstUseEver/ImGuiCond_Once conditions once more.
  14083 void ImGui::ClearWindowSettings(const char* name)
  14084 {
  14085     //IMGUI_DEBUG_LOG("ClearWindowSettings('%s')\n", name);
  14086     ImGuiWindow* window = FindWindowByName(name);
  14087     if (window != NULL)
  14088     {
  14089         window->Flags |= ImGuiWindowFlags_NoSavedSettings;
  14090         InitOrLoadWindowSettings(window, NULL);
  14091     }
  14092     if (ImGuiWindowSettings* settings = window ? FindWindowSettingsByWindow(window) : FindWindowSettingsByID(ImHashStr(name)))
  14093         settings->WantDelete = true;
  14094 }
  14095 
  14096 static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
  14097 {
  14098     ImGuiContext& g = *ctx;
  14099     for (ImGuiWindow* window : g.Windows)
  14100         window->SettingsOffset = -1;
  14101     g.SettingsWindows.clear();
  14102 }
  14103 
  14104 static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
  14105 {
  14106     ImGuiID id = ImHashStr(name);
  14107     ImGuiWindowSettings* settings = ImGui::FindWindowSettingsByID(id);
  14108     if (settings)
  14109         *settings = ImGuiWindowSettings(); // Clear existing if recycling previous entry
  14110     else
  14111         settings = ImGui::CreateNewWindowSettings(name);
  14112     settings->ID = id;
  14113     settings->WantApply = true;
  14114     return (void*)settings;
  14115 }
  14116 
  14117 static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
  14118 {
  14119     ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
  14120     int x, y;
  14121     int i;
  14122     if (sscanf(line, "Pos=%i,%i", &x, &y) == 2)         { settings->Pos = ImVec2ih((short)x, (short)y); }
  14123     else if (sscanf(line, "Size=%i,%i", &x, &y) == 2)   { settings->Size = ImVec2ih((short)x, (short)y); }
  14124     else if (sscanf(line, "Collapsed=%d", &i) == 1)     { settings->Collapsed = (i != 0); }
  14125     else if (sscanf(line, "IsChild=%d", &i) == 1)       { settings->IsChild = (i != 0); }
  14126 }
  14127 
  14128 // Apply to existing windows (if any)
  14129 static void WindowSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
  14130 {
  14131     ImGuiContext& g = *ctx;
  14132     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
  14133         if (settings->WantApply)
  14134         {
  14135             if (ImGuiWindow* window = ImGui::FindWindowByID(settings->ID))
  14136                 ApplyWindowSettings(window, settings);
  14137             settings->WantApply = false;
  14138         }
  14139 }
  14140 
  14141 static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
  14142 {
  14143     // Gather data from windows that were active during this session
  14144     // (if a window wasn't opened in this session we preserve its settings)
  14145     ImGuiContext& g = *ctx;
  14146     for (ImGuiWindow* window : g.Windows)
  14147     {
  14148         if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
  14149             continue;
  14150 
  14151         ImGuiWindowSettings* settings = ImGui::FindWindowSettingsByWindow(window);
  14152         if (!settings)
  14153         {
  14154             settings = ImGui::CreateNewWindowSettings(window->Name);
  14155             window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
  14156         }
  14157         IM_ASSERT(settings->ID == window->ID);
  14158         settings->Pos = ImVec2ih(window->Pos);
  14159         settings->Size = ImVec2ih(window->SizeFull);
  14160         settings->IsChild = (window->Flags & ImGuiWindowFlags_ChildWindow) != 0;
  14161         settings->Collapsed = window->Collapsed;
  14162         settings->WantDelete = false;
  14163     }
  14164 
  14165     // Write to text buffer
  14166     buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve
  14167     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
  14168     {
  14169         if (settings->WantDelete)
  14170             continue;
  14171         const char* settings_name = settings->GetName();
  14172         buf->appendf("[%s][%s]\n", handler->TypeName, settings_name);
  14173         if (settings->IsChild)
  14174         {
  14175             buf->appendf("IsChild=1\n");
  14176             buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
  14177         }
  14178         else
  14179         {
  14180             buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
  14181             buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
  14182             if (settings->Collapsed)
  14183                 buf->appendf("Collapsed=1\n");
  14184         }
  14185         buf->append("\n");
  14186     }
  14187 }
  14188 
  14189 
  14190 //-----------------------------------------------------------------------------
  14191 // [SECTION] LOCALIZATION
  14192 //-----------------------------------------------------------------------------
  14193 
  14194 void ImGui::LocalizeRegisterEntries(const ImGuiLocEntry* entries, int count)
  14195 {
  14196     ImGuiContext& g = *GImGui;
  14197     for (int n = 0; n < count; n++)
  14198         g.LocalizationTable[entries[n].Key] = entries[n].Text;
  14199 }
  14200 
  14201 
  14202 //-----------------------------------------------------------------------------
  14203 // [SECTION] VIEWPORTS, PLATFORM WINDOWS
  14204 //-----------------------------------------------------------------------------
  14205 // - GetMainViewport()
  14206 // - SetWindowViewport() [Internal]
  14207 // - UpdateViewportsNewFrame() [Internal]
  14208 // (this section is more complete in the 'docking' branch)
  14209 //-----------------------------------------------------------------------------
  14210 
  14211 ImGuiViewport* ImGui::GetMainViewport()
  14212 {
  14213     ImGuiContext& g = *GImGui;
  14214     return g.Viewports[0];
  14215 }
  14216 
  14217 void ImGui::SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport)
  14218 {
  14219     window->Viewport = viewport;
  14220 }
  14221 
  14222 // Update viewports and monitor infos
  14223 static void ImGui::UpdateViewportsNewFrame()
  14224 {
  14225     ImGuiContext& g = *GImGui;
  14226     IM_ASSERT(g.Viewports.Size == 1);
  14227 
  14228     // Update main viewport with current platform position.
  14229     // FIXME-VIEWPORT: Size is driven by backend/user code for backward-compatibility but we should aim to make this more consistent.
  14230     ImGuiViewportP* main_viewport = g.Viewports[0];
  14231     main_viewport->Flags = ImGuiViewportFlags_IsPlatformWindow | ImGuiViewportFlags_OwnedByApp;
  14232     main_viewport->Pos = ImVec2(0.0f, 0.0f);
  14233     main_viewport->Size = g.IO.DisplaySize;
  14234 
  14235     for (ImGuiViewportP* viewport : g.Viewports)
  14236     {
  14237         // Lock down space taken by menu bars and status bars, reset the offset for fucntions like BeginMainMenuBar() to alter them again.
  14238         viewport->WorkOffsetMin = viewport->BuildWorkOffsetMin;
  14239         viewport->WorkOffsetMax = viewport->BuildWorkOffsetMax;
  14240         viewport->BuildWorkOffsetMin = viewport->BuildWorkOffsetMax = ImVec2(0.0f, 0.0f);
  14241         viewport->UpdateWorkRect();
  14242     }
  14243 }
  14244 
  14245 //-----------------------------------------------------------------------------
  14246 // [SECTION] DOCKING
  14247 //-----------------------------------------------------------------------------
  14248 
  14249 // (this section is filled in the 'docking' branch)
  14250 
  14251 
  14252 //-----------------------------------------------------------------------------
  14253 // [SECTION] PLATFORM DEPENDENT HELPERS
  14254 //-----------------------------------------------------------------------------
  14255 // - Default clipboard handlers
  14256 // - Default shell function handlers
  14257 // - Default IME handlers
  14258 //-----------------------------------------------------------------------------
  14259 
  14260 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
  14261 
  14262 #ifdef _MSC_VER
  14263 #pragma comment(lib, "user32")
  14264 #pragma comment(lib, "kernel32")
  14265 #endif
  14266 
  14267 // Win32 clipboard implementation
  14268 // We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown()
  14269 static const char* GetClipboardTextFn_DefaultImpl(void* user_data_ctx)
  14270 {
  14271     ImGuiContext& g = *(ImGuiContext*)user_data_ctx;
  14272     g.ClipboardHandlerData.clear();
  14273     if (!::OpenClipboard(NULL))
  14274         return NULL;
  14275     HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
  14276     if (wbuf_handle == NULL)
  14277     {
  14278         ::CloseClipboard();
  14279         return NULL;
  14280     }
  14281     if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle))
  14282     {
  14283         int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL);
  14284         g.ClipboardHandlerData.resize(buf_len);
  14285         ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL);
  14286     }
  14287     ::GlobalUnlock(wbuf_handle);
  14288     ::CloseClipboard();
  14289     return g.ClipboardHandlerData.Data;
  14290 }
  14291 
  14292 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
  14293 {
  14294     if (!::OpenClipboard(NULL))
  14295         return;
  14296     const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);
  14297     HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR));
  14298     if (wbuf_handle == NULL)
  14299     {
  14300         ::CloseClipboard();
  14301         return;
  14302     }
  14303     WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle);
  14304     ::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length);
  14305     ::GlobalUnlock(wbuf_handle);
  14306     ::EmptyClipboard();
  14307     if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
  14308         ::GlobalFree(wbuf_handle);
  14309     ::CloseClipboard();
  14310 }
  14311 
  14312 #elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS)
  14313 
  14314 #include <Carbon/Carbon.h>  // Use old API to avoid need for separate .mm file
  14315 static PasteboardRef main_clipboard = 0;
  14316 
  14317 // OSX clipboard implementation
  14318 // If you enable this you will need to add '-framework ApplicationServices' to your linker command-line!
  14319 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
  14320 {
  14321     if (!main_clipboard)
  14322         PasteboardCreate(kPasteboardClipboard, &main_clipboard);
  14323     PasteboardClear(main_clipboard);
  14324     CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text));
  14325     if (cf_data)
  14326     {
  14327         PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0);
  14328         CFRelease(cf_data);
  14329     }
  14330 }
  14331 
  14332 static const char* GetClipboardTextFn_DefaultImpl(void* user_data_ctx)
  14333 {
  14334     ImGuiContext& g = *(ImGuiContext*)user_data_ctx;
  14335     if (!main_clipboard)
  14336         PasteboardCreate(kPasteboardClipboard, &main_clipboard);
  14337     PasteboardSynchronize(main_clipboard);
  14338 
  14339     ItemCount item_count = 0;
  14340     PasteboardGetItemCount(main_clipboard, &item_count);
  14341     for (ItemCount i = 0; i < item_count; i++)
  14342     {
  14343         PasteboardItemID item_id = 0;
  14344         PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id);
  14345         CFArrayRef flavor_type_array = 0;
  14346         PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array);
  14347         for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++)
  14348         {
  14349             CFDataRef cf_data;
  14350             if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr)
  14351             {
  14352                 g.ClipboardHandlerData.clear();
  14353                 int length = (int)CFDataGetLength(cf_data);
  14354                 g.ClipboardHandlerData.resize(length + 1);
  14355                 CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data);
  14356                 g.ClipboardHandlerData[length] = 0;
  14357                 CFRelease(cf_data);
  14358                 return g.ClipboardHandlerData.Data;
  14359             }
  14360         }
  14361     }
  14362     return NULL;
  14363 }
  14364 
  14365 #else
  14366 
  14367 // Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers.
  14368 static const char* GetClipboardTextFn_DefaultImpl(void* user_data_ctx)
  14369 {
  14370     ImGuiContext& g = *(ImGuiContext*)user_data_ctx;
  14371     return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin();
  14372 }
  14373 
  14374 static void SetClipboardTextFn_DefaultImpl(void* user_data_ctx, const char* text)
  14375 {
  14376     ImGuiContext& g = *(ImGuiContext*)user_data_ctx;
  14377     g.ClipboardHandlerData.clear();
  14378     const char* text_end = text + strlen(text);
  14379     g.ClipboardHandlerData.resize((int)(text_end - text) + 1);
  14380     memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text));
  14381     g.ClipboardHandlerData[(int)(text_end - text)] = 0;
  14382 }
  14383 
  14384 #endif // Default clipboard handlers
  14385 
  14386 //-----------------------------------------------------------------------------
  14387 
  14388 #ifndef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
  14389 #if defined(__APPLE__) && TARGET_OS_IPHONE
  14390 #define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
  14391 #endif
  14392 
  14393 #if defined(_WIN32) && defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
  14394 #define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
  14395 #endif
  14396 #endif
  14397 
  14398 #ifndef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
  14399 #ifdef _WIN32
  14400 #include <shellapi.h>   // ShellExecuteA()
  14401 #ifdef _MSC_VER
  14402 #pragma comment(lib, "shell32")
  14403 #endif
  14404 static bool PlatformOpenInShellFn_DefaultImpl(ImGuiContext*, const char* path)
  14405 {
  14406     return (INT_PTR)::ShellExecuteA(NULL, "open", path, NULL, NULL, SW_SHOWDEFAULT) > 32;
  14407 }
  14408 #else
  14409 #include <sys/wait.h>
  14410 #include <unistd.h>
  14411 static bool PlatformOpenInShellFn_DefaultImpl(ImGuiContext*, const char* path)
  14412 {
  14413 #if defined(__APPLE__)
  14414     const char* args[] { "open", "--", path, NULL };
  14415 #else
  14416     const char* args[] { "xdg-open", path, NULL };
  14417 #endif
  14418     pid_t pid = fork();
  14419     if (pid < 0)
  14420         return false;
  14421     if (!pid)
  14422     {
  14423         execvp(args[0], const_cast<char **>(args));
  14424         exit(-1);
  14425     }
  14426     else
  14427     {
  14428         int status;
  14429         waitpid(pid, &status, 0);
  14430         return WEXITSTATUS(status) == 0;
  14431     }
  14432 }
  14433 #endif
  14434 #else
  14435 static bool PlatformOpenInShellFn_DefaultImpl(ImGuiContext*, const char*) { return false; }
  14436 #endif // Default shell handlers
  14437 
  14438 //-----------------------------------------------------------------------------
  14439 
  14440 // Win32 API IME support (for Asian languages, etc.)
  14441 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
  14442 
  14443 #include <imm.h>
  14444 #ifdef _MSC_VER
  14445 #pragma comment(lib, "imm32")
  14446 #endif
  14447 
  14448 static void PlatformSetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data)
  14449 {
  14450     // Notify OS Input Method Editor of text input position
  14451     HWND hwnd = (HWND)viewport->PlatformHandleRaw;
  14452     if (hwnd == 0)
  14453         return;
  14454 
  14455     //::ImmAssociateContextEx(hwnd, NULL, data->WantVisible ? IACE_DEFAULT : 0);
  14456     if (HIMC himc = ::ImmGetContext(hwnd))
  14457     {
  14458         COMPOSITIONFORM composition_form = {};
  14459         composition_form.ptCurrentPos.x = (LONG)data->InputPos.x;
  14460         composition_form.ptCurrentPos.y = (LONG)data->InputPos.y;
  14461         composition_form.dwStyle = CFS_FORCE_POSITION;
  14462         ::ImmSetCompositionWindow(himc, &composition_form);
  14463         CANDIDATEFORM candidate_form = {};
  14464         candidate_form.dwStyle = CFS_CANDIDATEPOS;
  14465         candidate_form.ptCurrentPos.x = (LONG)data->InputPos.x;
  14466         candidate_form.ptCurrentPos.y = (LONG)data->InputPos.y;
  14467         ::ImmSetCandidateWindow(himc, &candidate_form);
  14468         ::ImmReleaseContext(hwnd, himc);
  14469     }
  14470 }
  14471 
  14472 #else
  14473 
  14474 static void PlatformSetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport*, ImGuiPlatformImeData*) {}
  14475 
  14476 #endif // Default IME handlers
  14477 
  14478 //-----------------------------------------------------------------------------
  14479 // [SECTION] METRICS/DEBUGGER WINDOW
  14480 //-----------------------------------------------------------------------------
  14481 // - DebugRenderViewportThumbnail() [Internal]
  14482 // - RenderViewportsThumbnails() [Internal]
  14483 // - DebugTextEncoding()
  14484 // - MetricsHelpMarker() [Internal]
  14485 // - ShowFontAtlas() [Internal]
  14486 // - ShowMetricsWindow()
  14487 // - DebugNodeColumns() [Internal]
  14488 // - DebugNodeDrawList() [Internal]
  14489 // - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal]
  14490 // - DebugNodeFont() [Internal]
  14491 // - DebugNodeFontGlyph() [Internal]
  14492 // - DebugNodeStorage() [Internal]
  14493 // - DebugNodeTabBar() [Internal]
  14494 // - DebugNodeViewport() [Internal]
  14495 // - DebugNodeWindow() [Internal]
  14496 // - DebugNodeWindowSettings() [Internal]
  14497 // - DebugNodeWindowsList() [Internal]
  14498 // - DebugNodeWindowsListByBeginStackParent() [Internal]
  14499 //-----------------------------------------------------------------------------
  14500 
  14501 #ifndef IMGUI_DISABLE_DEBUG_TOOLS
  14502 
  14503 void ImGui::DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb)
  14504 {
  14505     ImGuiContext& g = *GImGui;
  14506     ImGuiWindow* window = g.CurrentWindow;
  14507 
  14508     ImVec2 scale = bb.GetSize() / viewport->Size;
  14509     ImVec2 off = bb.Min - viewport->Pos * scale;
  14510     float alpha_mul = 1.0f;
  14511     window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f));
  14512     for (ImGuiWindow* thumb_window : g.Windows)
  14513     {
  14514         if (!thumb_window->WasActive || (thumb_window->Flags & ImGuiWindowFlags_ChildWindow))
  14515             continue;
  14516 
  14517         ImRect thumb_r = thumb_window->Rect();
  14518         ImRect title_r = thumb_window->TitleBarRect();
  14519         thumb_r = ImRect(ImTrunc(off + thumb_r.Min * scale), ImTrunc(off +  thumb_r.Max * scale));
  14520         title_r = ImRect(ImTrunc(off + title_r.Min * scale), ImTrunc(off +  ImVec2(title_r.Max.x, title_r.Min.y + title_r.GetHeight() * 3.0f) * scale)); // Exaggerate title bar height
  14521         thumb_r.ClipWithFull(bb);
  14522         title_r.ClipWithFull(bb);
  14523         const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight);
  14524         window->DrawList->AddRectFilled(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_WindowBg, alpha_mul));
  14525         window->DrawList->AddRectFilled(title_r.Min, title_r.Max, GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg, alpha_mul));
  14526         window->DrawList->AddRect(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_Border, alpha_mul));
  14527         window->DrawList->AddText(g.Font, g.FontSize * 1.0f, title_r.Min, GetColorU32(ImGuiCol_Text, alpha_mul), thumb_window->Name, FindRenderedTextEnd(thumb_window->Name));
  14528     }
  14529     draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul));
  14530     if (viewport->ID == g.DebugMetricsConfig.HighlightViewportID)
  14531         window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
  14532 }
  14533 
  14534 static void RenderViewportsThumbnails()
  14535 {
  14536     ImGuiContext& g = *GImGui;
  14537     ImGuiWindow* window = g.CurrentWindow;
  14538 
  14539     float SCALE = 1.0f / 8.0f;
  14540     ImRect bb_full(g.Viewports[0]->Pos, g.Viewports[0]->Pos + g.Viewports[0]->Size);
  14541     ImVec2 p = window->DC.CursorPos;
  14542     ImVec2 off = p - bb_full.Min * SCALE;
  14543 
  14544     // Draw viewports
  14545     for (ImGuiViewportP* viewport : g.Viewports)
  14546     {
  14547         ImRect viewport_draw_bb(off + (viewport->Pos) * SCALE, off + (viewport->Pos + viewport->Size) * SCALE);
  14548         ImGui::DebugRenderViewportThumbnail(window->DrawList, viewport, viewport_draw_bb);
  14549     }
  14550     ImGui::Dummy(bb_full.GetSize() * SCALE);
  14551 }
  14552 
  14553 // Draw an arbitrary US keyboard layout to visualize translated keys
  14554 void ImGui::DebugRenderKeyboardPreview(ImDrawList* draw_list)
  14555 {
  14556     const float scale = ImGui::GetFontSize() / 13.0f;
  14557     const ImVec2 key_size = ImVec2(35.0f, 35.0f) * scale;
  14558     const float  key_rounding = 3.0f * scale;
  14559     const ImVec2 key_face_size = ImVec2(25.0f, 25.0f) * scale;
  14560     const ImVec2 key_face_pos = ImVec2(5.0f, 3.0f) * scale;
  14561     const float  key_face_rounding = 2.0f * scale;
  14562     const ImVec2 key_label_pos = ImVec2(7.0f, 4.0f) * scale;
  14563     const ImVec2 key_step = ImVec2(key_size.x - 1.0f, key_size.y - 1.0f);
  14564     const float  key_row_offset = 9.0f * scale;
  14565 
  14566     ImVec2 board_min = GetCursorScreenPos();
  14567     ImVec2 board_max = ImVec2(board_min.x + 3 * key_step.x + 2 * key_row_offset + 10.0f, board_min.y + 3 * key_step.y + 10.0f);
  14568     ImVec2 start_pos = ImVec2(board_min.x + 5.0f - key_step.x, board_min.y);
  14569 
  14570     struct KeyLayoutData { int Row, Col; const char* Label; ImGuiKey Key; };
  14571     const KeyLayoutData keys_to_display[] =
  14572     {
  14573         { 0, 0, "", ImGuiKey_Tab },      { 0, 1, "Q", ImGuiKey_Q }, { 0, 2, "W", ImGuiKey_W }, { 0, 3, "E", ImGuiKey_E }, { 0, 4, "R", ImGuiKey_R },
  14574         { 1, 0, "", ImGuiKey_CapsLock }, { 1, 1, "A", ImGuiKey_A }, { 1, 2, "S", ImGuiKey_S }, { 1, 3, "D", ImGuiKey_D }, { 1, 4, "F", ImGuiKey_F },
  14575         { 2, 0, "", ImGuiKey_LeftShift },{ 2, 1, "Z", ImGuiKey_Z }, { 2, 2, "X", ImGuiKey_X }, { 2, 3, "C", ImGuiKey_C }, { 2, 4, "V", ImGuiKey_V }
  14576     };
  14577 
  14578     // Elements rendered manually via ImDrawList API are not clipped automatically.
  14579     // While not strictly necessary, here IsItemVisible() is used to avoid rendering these shapes when they are out of view.
  14580     Dummy(board_max - board_min);
  14581     if (!IsItemVisible())
  14582         return;
  14583     draw_list->PushClipRect(board_min, board_max, true);
  14584     for (int n = 0; n < IM_ARRAYSIZE(keys_to_display); n++)
  14585     {
  14586         const KeyLayoutData* key_data = &keys_to_display[n];
  14587         ImVec2 key_min = ImVec2(start_pos.x + key_data->Col * key_step.x + key_data->Row * key_row_offset, start_pos.y + key_data->Row * key_step.y);
  14588         ImVec2 key_max = key_min + key_size;
  14589         draw_list->AddRectFilled(key_min, key_max, IM_COL32(204, 204, 204, 255), key_rounding);
  14590         draw_list->AddRect(key_min, key_max, IM_COL32(24, 24, 24, 255), key_rounding);
  14591         ImVec2 face_min = ImVec2(key_min.x + key_face_pos.x, key_min.y + key_face_pos.y);
  14592         ImVec2 face_max = ImVec2(face_min.x + key_face_size.x, face_min.y + key_face_size.y);
  14593         draw_list->AddRect(face_min, face_max, IM_COL32(193, 193, 193, 255), key_face_rounding, ImDrawFlags_None, 2.0f);
  14594         draw_list->AddRectFilled(face_min, face_max, IM_COL32(252, 252, 252, 255), key_face_rounding);
  14595         ImVec2 label_min = ImVec2(key_min.x + key_label_pos.x, key_min.y + key_label_pos.y);
  14596         draw_list->AddText(label_min, IM_COL32(64, 64, 64, 255), key_data->Label);
  14597         if (IsKeyDown(key_data->Key))
  14598             draw_list->AddRectFilled(key_min, key_max, IM_COL32(255, 0, 0, 128), key_rounding);
  14599     }
  14600     draw_list->PopClipRect();
  14601 }
  14602 
  14603 // Helper tool to diagnose between text encoding issues and font loading issues. Pass your UTF-8 string and verify that there are correct.
  14604 void ImGui::DebugTextEncoding(const char* str)
  14605 {
  14606     Text("Text: \"%s\"", str);
  14607     if (!BeginTable("##DebugTextEncoding", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable))
  14608         return;
  14609     TableSetupColumn("Offset");
  14610     TableSetupColumn("UTF-8");
  14611     TableSetupColumn("Glyph");
  14612     TableSetupColumn("Codepoint");
  14613     TableHeadersRow();
  14614     for (const char* p = str; *p != 0; )
  14615     {
  14616         unsigned int c;
  14617         const int c_utf8_len = ImTextCharFromUtf8(&c, p, NULL);
  14618         TableNextColumn();
  14619         Text("%d", (int)(p - str));
  14620         TableNextColumn();
  14621         for (int byte_index = 0; byte_index < c_utf8_len; byte_index++)
  14622         {
  14623             if (byte_index > 0)
  14624                 SameLine();
  14625             Text("0x%02X", (int)(unsigned char)p[byte_index]);
  14626         }
  14627         TableNextColumn();
  14628         if (GetFont()->FindGlyphNoFallback((ImWchar)c))
  14629             TextUnformatted(p, p + c_utf8_len);
  14630         else
  14631             TextUnformatted((c == IM_UNICODE_CODEPOINT_INVALID) ? "[invalid]" : "[missing]");
  14632         TableNextColumn();
  14633         Text("U+%04X", (int)c);
  14634         p += c_utf8_len;
  14635     }
  14636     EndTable();
  14637 }
  14638 
  14639 static void DebugFlashStyleColorStop()
  14640 {
  14641     ImGuiContext& g = *GImGui;
  14642     if (g.DebugFlashStyleColorIdx != ImGuiCol_COUNT)
  14643         g.Style.Colors[g.DebugFlashStyleColorIdx] = g.DebugFlashStyleColorBackup;
  14644     g.DebugFlashStyleColorIdx = ImGuiCol_COUNT;
  14645 }
  14646 
  14647 // Flash a given style color for some + inhibit modifications of this color via PushStyleColor() calls.
  14648 void ImGui::DebugFlashStyleColor(ImGuiCol idx)
  14649 {
  14650     ImGuiContext& g = *GImGui;
  14651     DebugFlashStyleColorStop();
  14652     g.DebugFlashStyleColorTime = 0.5f;
  14653     g.DebugFlashStyleColorIdx = idx;
  14654     g.DebugFlashStyleColorBackup = g.Style.Colors[idx];
  14655 }
  14656 
  14657 void ImGui::UpdateDebugToolFlashStyleColor()
  14658 {
  14659     ImGuiContext& g = *GImGui;
  14660     if (g.DebugFlashStyleColorTime <= 0.0f)
  14661         return;
  14662     ColorConvertHSVtoRGB(cosf(g.DebugFlashStyleColorTime * 6.0f) * 0.5f + 0.5f, 0.5f, 0.5f, g.Style.Colors[g.DebugFlashStyleColorIdx].x, g.Style.Colors[g.DebugFlashStyleColorIdx].y, g.Style.Colors[g.DebugFlashStyleColorIdx].z);
  14663     g.Style.Colors[g.DebugFlashStyleColorIdx].w = 1.0f;
  14664     if ((g.DebugFlashStyleColorTime -= g.IO.DeltaTime) <= 0.0f)
  14665         DebugFlashStyleColorStop();
  14666 }
  14667 
  14668 // Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds.
  14669 static void MetricsHelpMarker(const char* desc)
  14670 {
  14671     ImGui::TextDisabled("(?)");
  14672     if (ImGui::BeginItemTooltip())
  14673     {
  14674         ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
  14675         ImGui::TextUnformatted(desc);
  14676         ImGui::PopTextWrapPos();
  14677         ImGui::EndTooltip();
  14678     }
  14679 }
  14680 
  14681 // [DEBUG] List fonts in a font atlas and display its texture
  14682 void ImGui::ShowFontAtlas(ImFontAtlas* atlas)
  14683 {
  14684     for (ImFont* font : atlas->Fonts)
  14685     {
  14686         PushID(font);
  14687         DebugNodeFont(font);
  14688         PopID();
  14689     }
  14690     if (TreeNode("Font Atlas", "Font Atlas (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight))
  14691     {
  14692         ImGuiContext& g = *GImGui;
  14693         ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
  14694         Checkbox("Tint with Text Color", &cfg->ShowAtlasTintedWithTextColor); // Using text color ensure visibility of core atlas data, but will alter custom colored icons
  14695         ImVec4 tint_col = cfg->ShowAtlasTintedWithTextColor ? GetStyleColorVec4(ImGuiCol_Text) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
  14696         ImVec4 border_col = GetStyleColorVec4(ImGuiCol_Border);
  14697         Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), tint_col, border_col);
  14698         TreePop();
  14699     }
  14700 }
  14701 
  14702 void ImGui::ShowMetricsWindow(bool* p_open)
  14703 {
  14704     ImGuiContext& g = *GImGui;
  14705     ImGuiIO& io = g.IO;
  14706     ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
  14707     if (cfg->ShowDebugLog)
  14708         ShowDebugLogWindow(&cfg->ShowDebugLog);
  14709     if (cfg->ShowIDStackTool)
  14710         ShowIDStackToolWindow(&cfg->ShowIDStackTool);
  14711 
  14712     if (!Begin("Dear ImGui Metrics/Debugger", p_open) || GetCurrentWindow()->BeginCount > 1)
  14713     {
  14714         End();
  14715         return;
  14716     }
  14717 
  14718     // [DEBUG] Clear debug breaks hooks after exactly one cycle.
  14719     DebugBreakClearData();
  14720 
  14721     // Basic info
  14722     Text("Dear ImGui %s", GetVersion());
  14723     if (g.ContextName[0] != 0)
  14724     {
  14725         SameLine();
  14726         Text("(Context Name: \"%s\")", g.ContextName);
  14727     }
  14728     Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
  14729     Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
  14730     Text("%d visible windows, %d current allocations", io.MetricsRenderWindows, g.DebugAllocInfo.TotalAllocCount - g.DebugAllocInfo.TotalFreeCount);
  14731     //SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; }
  14732 
  14733     Separator();
  14734 
  14735     // Debugging enums
  14736     enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentIdeal, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type
  14737     const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentIdeal", "ContentRegionRect" };
  14738     enum { TRT_OuterRect, TRT_InnerRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsWorkRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersIdeal, TRT_ColumnsContentFrozen, TRT_ColumnsContentUnfrozen, TRT_Count }; // Tables Rect Type
  14739     const char* trt_rects_names[TRT_Count] = { "OuterRect", "InnerRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsWorkRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentFrozen", "ColumnsContentUnfrozen" };
  14740     if (cfg->ShowWindowsRectsType < 0)
  14741         cfg->ShowWindowsRectsType = WRT_WorkRect;
  14742     if (cfg->ShowTablesRectsType < 0)
  14743         cfg->ShowTablesRectsType = TRT_WorkRect;
  14744 
  14745     struct Funcs
  14746     {
  14747         static ImRect GetTableRect(ImGuiTable* table, int rect_type, int n)
  14748         {
  14749             ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); // Always using last submitted instance
  14750             if (rect_type == TRT_OuterRect)                     { return table->OuterRect; }
  14751             else if (rect_type == TRT_InnerRect)                { return table->InnerRect; }
  14752             else if (rect_type == TRT_WorkRect)                 { return table->WorkRect; }
  14753             else if (rect_type == TRT_HostClipRect)             { return table->HostClipRect; }
  14754             else if (rect_type == TRT_InnerClipRect)            { return table->InnerClipRect; }
  14755             else if (rect_type == TRT_BackgroundClipRect)       { return table->BgClipRect; }
  14756             else if (rect_type == TRT_ColumnsRect)              { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->MinX, table->InnerClipRect.Min.y, c->MaxX, table->InnerClipRect.Min.y + table_instance->LastOuterHeight); }
  14757             else if (rect_type == TRT_ColumnsWorkRect)          { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->WorkRect.Min.y, c->WorkMaxX, table->WorkRect.Max.y); }
  14758             else if (rect_type == TRT_ColumnsClipRect)          { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; }
  14759             else if (rect_type == TRT_ColumnsContentHeadersUsed){ ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersUsed, table->InnerClipRect.Min.y + table_instance->LastTopHeadersRowHeight); } // Note: y1/y2 not always accurate
  14760             else if (rect_type == TRT_ColumnsContentHeadersIdeal){ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersIdeal, table->InnerClipRect.Min.y + table_instance->LastTopHeadersRowHeight); }
  14761             else if (rect_type == TRT_ColumnsContentFrozen)     { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXFrozen, table->InnerClipRect.Min.y + table_instance->LastFrozenHeight); }
  14762             else if (rect_type == TRT_ColumnsContentUnfrozen)   { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y + table_instance->LastFrozenHeight, c->ContentMaxXUnfrozen, table->InnerClipRect.Max.y); }
  14763             IM_ASSERT(0);
  14764             return ImRect();
  14765         }
  14766 
  14767         static ImRect GetWindowRect(ImGuiWindow* window, int rect_type)
  14768         {
  14769             if (rect_type == WRT_OuterRect)                 { return window->Rect(); }
  14770             else if (rect_type == WRT_OuterRectClipped)     { return window->OuterRectClipped; }
  14771             else if (rect_type == WRT_InnerRect)            { return window->InnerRect; }
  14772             else if (rect_type == WRT_InnerClipRect)        { return window->InnerClipRect; }
  14773             else if (rect_type == WRT_WorkRect)             { return window->WorkRect; }
  14774             else if (rect_type == WRT_Content)              { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
  14775             else if (rect_type == WRT_ContentIdeal)         { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSizeIdeal); }
  14776             else if (rect_type == WRT_ContentRegionRect)    { return window->ContentRegionRect; }
  14777             IM_ASSERT(0);
  14778             return ImRect();
  14779         }
  14780     };
  14781 
  14782     // Tools
  14783     if (TreeNode("Tools"))
  14784     {
  14785         // Debug Break features
  14786         // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
  14787         SeparatorTextEx(0, "Debug breaks", NULL, CalcTextSize("(?)").x + g.Style.SeparatorTextPadding.x);
  14788         SameLine();
  14789         MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash.");
  14790         if (Checkbox("Show Item Picker", &g.DebugItemPickerActive) && g.DebugItemPickerActive)
  14791             DebugStartItemPicker();
  14792         Checkbox("Show \"Debug Break\" buttons in other sections (io.ConfigDebugIsDebuggerPresent)", &g.IO.ConfigDebugIsDebuggerPresent);
  14793 
  14794         SeparatorText("Visualize");
  14795 
  14796         Checkbox("Show Debug Log", &cfg->ShowDebugLog);
  14797         SameLine();
  14798         MetricsHelpMarker("You can also call ImGui::ShowDebugLogWindow() from your code.");
  14799 
  14800         Checkbox("Show ID Stack Tool", &cfg->ShowIDStackTool);
  14801         SameLine();
  14802         MetricsHelpMarker("You can also call ImGui::ShowIDStackToolWindow() from your code.");
  14803 
  14804         Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder);
  14805         Checkbox("Show windows rectangles", &cfg->ShowWindowsRects);
  14806         SameLine();
  14807         SetNextItemWidth(GetFontSize() * 12);
  14808         cfg->ShowWindowsRects |= Combo("##show_windows_rect_type", &cfg->ShowWindowsRectsType, wrt_rects_names, WRT_Count, WRT_Count);
  14809         if (cfg->ShowWindowsRects && g.NavWindow != NULL)
  14810         {
  14811             BulletText("'%s':", g.NavWindow->Name);
  14812             Indent();
  14813             for (int rect_n = 0; rect_n < WRT_Count; rect_n++)
  14814             {
  14815                 ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n);
  14816                 Text("(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), wrt_rects_names[rect_n]);
  14817             }
  14818             Unindent();
  14819         }
  14820 
  14821         Checkbox("Show tables rectangles", &cfg->ShowTablesRects);
  14822         SameLine();
  14823         SetNextItemWidth(GetFontSize() * 12);
  14824         cfg->ShowTablesRects |= Combo("##show_table_rects_type", &cfg->ShowTablesRectsType, trt_rects_names, TRT_Count, TRT_Count);
  14825         if (cfg->ShowTablesRects && g.NavWindow != NULL)
  14826         {
  14827             for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)
  14828             {
  14829                 ImGuiTable* table = g.Tables.TryGetMapData(table_n);
  14830                 if (table == NULL || table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow))
  14831                     continue;
  14832 
  14833                 BulletText("Table 0x%08X (%d columns, in '%s')", table->ID, table->ColumnsCount, table->OuterWindow->Name);
  14834                 if (IsItemHovered())
  14835                     GetForegroundDrawList()->AddRect(table->OuterRect.Min - ImVec2(1, 1), table->OuterRect.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
  14836                 Indent();
  14837                 char buf[128];
  14838                 for (int rect_n = 0; rect_n < TRT_Count; rect_n++)
  14839                 {
  14840                     if (rect_n >= TRT_ColumnsRect)
  14841                     {
  14842                         if (rect_n != TRT_ColumnsRect && rect_n != TRT_ColumnsClipRect)
  14843                             continue;
  14844                         for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
  14845                         {
  14846                             ImRect r = Funcs::GetTableRect(table, rect_n, column_n);
  14847                             ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) Col %d %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), column_n, trt_rects_names[rect_n]);
  14848                             Selectable(buf);
  14849                             if (IsItemHovered())
  14850                                 GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
  14851                         }
  14852                     }
  14853                     else
  14854                     {
  14855                         ImRect r = Funcs::GetTableRect(table, rect_n, -1);
  14856                         ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), trt_rects_names[rect_n]);
  14857                         Selectable(buf);
  14858                         if (IsItemHovered())
  14859                             GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
  14860                     }
  14861                 }
  14862                 Unindent();
  14863             }
  14864         }
  14865         Checkbox("Show groups rectangles", &g.DebugShowGroupRects); // Storing in context as this is used by group code and prefers to be in hot-data
  14866 
  14867         SeparatorText("Validate");
  14868 
  14869         Checkbox("Debug Begin/BeginChild return value", &io.ConfigDebugBeginReturnValueLoop);
  14870         SameLine();
  14871         MetricsHelpMarker("Some calls to Begin()/BeginChild() will return false.\n\nWill cycle through window depths then repeat. Windows should be flickering while running.");
  14872 
  14873         Checkbox("UTF-8 Encoding viewer", &cfg->ShowTextEncodingViewer);
  14874         SameLine();
  14875         MetricsHelpMarker("You can also call ImGui::DebugTextEncoding() from your code with a given string to test that your UTF-8 encoding settings are correct.");
  14876         if (cfg->ShowTextEncodingViewer)
  14877         {
  14878             static char buf[64] = "";
  14879             SetNextItemWidth(-FLT_MIN);
  14880             InputText("##DebugTextEncodingBuf", buf, IM_ARRAYSIZE(buf));
  14881             if (buf[0] != 0)
  14882                 DebugTextEncoding(buf);
  14883         }
  14884 
  14885         TreePop();
  14886     }
  14887 
  14888     // Windows
  14889     if (TreeNode("Windows", "Windows (%d)", g.Windows.Size))
  14890     {
  14891         //SetNextItemOpen(true, ImGuiCond_Once);
  14892         DebugNodeWindowsList(&g.Windows, "By display order");
  14893         DebugNodeWindowsList(&g.WindowsFocusOrder, "By focus order (root windows)");
  14894         if (TreeNode("By submission order (begin stack)"))
  14895         {
  14896             // Here we display windows in their submitted order/hierarchy, however note that the Begin stack doesn't constitute a Parent<>Child relationship!
  14897             ImVector<ImGuiWindow*>& temp_buffer = g.WindowsTempSortBuffer;
  14898             temp_buffer.resize(0);
  14899             for (ImGuiWindow* window : g.Windows)
  14900                 if (window->LastFrameActive + 1 >= g.FrameCount)
  14901                     temp_buffer.push_back(window);
  14902             struct Func { static int IMGUI_CDECL WindowComparerByBeginOrder(const void* lhs, const void* rhs) { return ((int)(*(const ImGuiWindow* const *)lhs)->BeginOrderWithinContext - (*(const ImGuiWindow* const*)rhs)->BeginOrderWithinContext); } };
  14903             ImQsort(temp_buffer.Data, (size_t)temp_buffer.Size, sizeof(ImGuiWindow*), Func::WindowComparerByBeginOrder);
  14904             DebugNodeWindowsListByBeginStackParent(temp_buffer.Data, temp_buffer.Size, NULL);
  14905             TreePop();
  14906         }
  14907 
  14908         TreePop();
  14909     }
  14910 
  14911     // DrawLists
  14912     int drawlist_count = 0;
  14913     for (ImGuiViewportP* viewport : g.Viewports)
  14914         drawlist_count += viewport->DrawDataP.CmdLists.Size;
  14915     if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count))
  14916     {
  14917         Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh);
  14918         Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes);
  14919         for (ImGuiViewportP* viewport : g.Viewports)
  14920             for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists)
  14921                 DebugNodeDrawList(NULL, viewport, draw_list, "DrawList");
  14922         TreePop();
  14923     }
  14924 
  14925     // Viewports
  14926     if (TreeNode("Viewports", "Viewports (%d)", g.Viewports.Size))
  14927     {
  14928         SetNextItemOpen(true, ImGuiCond_Once);
  14929         if (TreeNode("Windows Minimap"))
  14930         {
  14931             RenderViewportsThumbnails();
  14932             TreePop();
  14933         }
  14934         cfg->HighlightViewportID = 0;
  14935 
  14936         for (ImGuiViewportP* viewport : g.Viewports)
  14937             DebugNodeViewport(viewport);
  14938         TreePop();
  14939     }
  14940 
  14941     // Details for Popups
  14942     if (TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
  14943     {
  14944         for (const ImGuiPopupData& popup_data : g.OpenPopupStack)
  14945         {
  14946             // As it's difficult to interact with tree nodes while popups are open, we display everything inline.
  14947             ImGuiWindow* window = popup_data.Window;
  14948             BulletText("PopupID: %08x, Window: '%s' (%s%s), RestoreNavWindow '%s', ParentWindow '%s'",
  14949                 popup_data.PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? "Child;" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? "Menu;" : "",
  14950                 popup_data.RestoreNavWindow ? popup_data.RestoreNavWindow->Name : "NULL", window && window->ParentWindow ? window->ParentWindow->Name : "NULL");
  14951         }
  14952         TreePop();
  14953     }
  14954 
  14955     // Details for TabBars
  14956     if (TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetAliveCount()))
  14957     {
  14958         for (int n = 0; n < g.TabBars.GetMapSize(); n++)
  14959             if (ImGuiTabBar* tab_bar = g.TabBars.TryGetMapData(n))
  14960             {
  14961                 PushID(tab_bar);
  14962                 DebugNodeTabBar(tab_bar, "TabBar");
  14963                 PopID();
  14964             }
  14965         TreePop();
  14966     }
  14967 
  14968     // Details for Tables
  14969     if (TreeNode("Tables", "Tables (%d)", g.Tables.GetAliveCount()))
  14970     {
  14971         for (int n = 0; n < g.Tables.GetMapSize(); n++)
  14972             if (ImGuiTable* table = g.Tables.TryGetMapData(n))
  14973                 DebugNodeTable(table);
  14974         TreePop();
  14975     }
  14976 
  14977     // Details for Fonts
  14978     ImFontAtlas* atlas = g.IO.Fonts;
  14979     if (TreeNode("Fonts", "Fonts (%d)", atlas->Fonts.Size))
  14980     {
  14981         ShowFontAtlas(atlas);
  14982         TreePop();
  14983     }
  14984 
  14985     // Details for InputText
  14986     if (TreeNode("InputText"))
  14987     {
  14988         DebugNodeInputTextState(&g.InputTextState);
  14989         TreePop();
  14990     }
  14991 
  14992     // Details for TypingSelect
  14993     if (TreeNode("TypingSelect", "TypingSelect (%d)", g.TypingSelectState.SearchBuffer[0] != 0 ? 1 : 0))
  14994     {
  14995         DebugNodeTypingSelectState(&g.TypingSelectState);
  14996         TreePop();
  14997     }
  14998 
  14999     // Details for MultiSelect
  15000     if (TreeNode("MultiSelect", "MultiSelect (%d)", g.MultiSelectStorage.GetAliveCount()))
  15001     {
  15002         ImGuiBoxSelectState* bs = &g.BoxSelectState;
  15003         BulletText("BoxSelect ID=0x%08X, Starting = %d, Active %d", bs->ID, bs->IsStarting, bs->IsActive);
  15004         for (int n = 0; n < g.MultiSelectStorage.GetMapSize(); n++)
  15005             if (ImGuiMultiSelectState* state = g.MultiSelectStorage.TryGetMapData(n))
  15006                 DebugNodeMultiSelectState(state);
  15007         TreePop();
  15008     }
  15009 
  15010     // Details for Docking
  15011 #ifdef IMGUI_HAS_DOCK
  15012     if (TreeNode("Docking"))
  15013     {
  15014         TreePop();
  15015     }
  15016 #endif // #ifdef IMGUI_HAS_DOCK
  15017 
  15018     // Settings
  15019     if (TreeNode("Settings"))
  15020     {
  15021         if (SmallButton("Clear"))
  15022             ClearIniSettings();
  15023         SameLine();
  15024         if (SmallButton("Save to memory"))
  15025             SaveIniSettingsToMemory();
  15026         SameLine();
  15027         if (SmallButton("Save to disk"))
  15028             SaveIniSettingsToDisk(g.IO.IniFilename);
  15029         SameLine();
  15030         if (g.IO.IniFilename)
  15031             Text("\"%s\"", g.IO.IniFilename);
  15032         else
  15033             TextUnformatted("<NULL>");
  15034         Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings);
  15035         Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer);
  15036         if (TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size))
  15037         {
  15038             for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
  15039                 BulletText("\"%s\"", handler.TypeName);
  15040             TreePop();
  15041         }
  15042         if (TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size()))
  15043         {
  15044             for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
  15045                 DebugNodeWindowSettings(settings);
  15046             TreePop();
  15047         }
  15048 
  15049         if (TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size()))
  15050         {
  15051             for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))
  15052                 DebugNodeTableSettings(settings);
  15053             TreePop();
  15054         }
  15055 
  15056 #ifdef IMGUI_HAS_DOCK
  15057 #endif // #ifdef IMGUI_HAS_DOCK
  15058 
  15059         if (TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size()))
  15060         {
  15061             InputTextMultiline("##Ini", (char*)(void*)g.SettingsIniData.c_str(), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, GetTextLineHeight() * 20), ImGuiInputTextFlags_ReadOnly);
  15062             TreePop();
  15063         }
  15064         TreePop();
  15065     }
  15066 
  15067     // Settings
  15068     if (TreeNode("Memory allocations"))
  15069     {
  15070         ImGuiDebugAllocInfo* info = &g.DebugAllocInfo;
  15071         Text("%d current allocations", info->TotalAllocCount - info->TotalFreeCount);
  15072         if (SmallButton("GC now")) { g.GcCompactAll = true; }
  15073         Text("Recent frames with allocations:");
  15074         int buf_size = IM_ARRAYSIZE(info->LastEntriesBuf);
  15075         for (int n = buf_size - 1; n >= 0; n--)
  15076         {
  15077             ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[(info->LastEntriesIdx - n + buf_size) % buf_size];
  15078             BulletText("Frame %06d: %+3d ( %2d alloc, %2d free )", entry->FrameCount, entry->AllocCount - entry->FreeCount, entry->AllocCount, entry->FreeCount);
  15079             if (n == 0)
  15080             {
  15081                 SameLine();
  15082                 Text("<- %d frames ago", g.FrameCount - entry->FrameCount);
  15083             }
  15084         }
  15085         TreePop();
  15086     }
  15087 
  15088     if (TreeNode("Inputs"))
  15089     {
  15090         Text("KEYBOARD/GAMEPAD/MOUSE KEYS");
  15091         {
  15092             // We iterate both legacy native range and named ImGuiKey ranges, which is a little odd but this allows displaying the data for old/new backends.
  15093             // User code should never have to go through such hoops! You can generally iterate between ImGuiKey_NamedKey_BEGIN and ImGuiKey_NamedKey_END.
  15094             Indent();
  15095 #ifdef IMGUI_DISABLE_OBSOLETE_KEYIO
  15096             struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } };
  15097 #else
  15098             struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key >= 0 && key < 512 && GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array
  15099             //Text("Legacy raw:");      for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key++) { if (io.KeysDown[key]) { SameLine(); Text("\"%s\" %d", GetKeyName(key), key); } }
  15100 #endif
  15101             Text("Keys down:");         for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !IsKeyDown(key)) continue;     SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); SameLine(); Text("(%.02f)", GetKeyData(key)->DownDuration); }
  15102             Text("Keys pressed:");      for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !IsKeyPressed(key)) continue;  SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); }
  15103             Text("Keys released:");     for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !IsKeyReleased(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); }
  15104             Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : "");
  15105             Text("Chars queue:");       for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; SameLine(); Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public.
  15106             DebugRenderKeyboardPreview(GetWindowDrawList());
  15107             Unindent();
  15108         }
  15109 
  15110         Text("MOUSE STATE");
  15111         {
  15112             Indent();
  15113             if (IsMousePosValid())
  15114                 Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y);
  15115             else
  15116                 Text("Mouse pos: <INVALID>");
  15117             Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y);
  15118             int count = IM_ARRAYSIZE(io.MouseDown);
  15119             Text("Mouse down:");     for (int i = 0; i < count; i++) if (IsMouseDown(i)) { SameLine(); Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); }
  15120             Text("Mouse clicked:");  for (int i = 0; i < count; i++) if (IsMouseClicked(i)) { SameLine(); Text("b%d (%d)", i, io.MouseClickedCount[i]); }
  15121             Text("Mouse released:"); for (int i = 0; i < count; i++) if (IsMouseReleased(i)) { SameLine(); Text("b%d", i); }
  15122             Text("Mouse wheel: %.1f", io.MouseWheel);
  15123             Text("MouseStationaryTimer: %.2f", g.MouseStationaryTimer);
  15124             Text("Mouse source: %s", GetMouseSourceName(io.MouseSource));
  15125             Text("Pen Pressure: %.1f", io.PenPressure); // Note: currently unused
  15126             Unindent();
  15127         }
  15128 
  15129         Text("MOUSE WHEELING");
  15130         {
  15131             Indent();
  15132             Text("WheelingWindow: '%s'", g.WheelingWindow ? g.WheelingWindow->Name : "NULL");
  15133             Text("WheelingWindowReleaseTimer: %.2f", g.WheelingWindowReleaseTimer);
  15134             Text("WheelingAxisAvg[] = { %.3f, %.3f }, Main Axis: %s", g.WheelingAxisAvg.x, g.WheelingAxisAvg.y, (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? "X" : (g.WheelingAxisAvg.x < g.WheelingAxisAvg.y) ? "Y" : "<none>");
  15135             Unindent();
  15136         }
  15137 
  15138         Text("KEY OWNERS");
  15139         {
  15140             Indent();
  15141             if (BeginChild("##owners", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY, ImGuiWindowFlags_NoSavedSettings))
  15142                 for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
  15143                 {
  15144                     ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
  15145                     if (owner_data->OwnerCurr == ImGuiKeyOwner_NoOwner)
  15146                         continue;
  15147                     Text("%s: 0x%08X%s", GetKeyName(key), owner_data->OwnerCurr,
  15148                         owner_data->LockUntilRelease ? " LockUntilRelease" : owner_data->LockThisFrame ? " LockThisFrame" : "");
  15149                     DebugLocateItemOnHover(owner_data->OwnerCurr);
  15150                 }
  15151             EndChild();
  15152             Unindent();
  15153         }
  15154         Text("SHORTCUT ROUTING");
  15155         SameLine();
  15156         MetricsHelpMarker("Declared shortcut routes automatically set key owner when mods matches.");
  15157         {
  15158             Indent();
  15159             if (BeginChild("##routes", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY, ImGuiWindowFlags_NoSavedSettings))
  15160                 for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
  15161                 {
  15162                     ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable;
  15163                     for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; )
  15164                     {
  15165                         ImGuiKeyRoutingData* routing_data = &rt->Entries[idx];
  15166                         ImGuiKeyChord key_chord = key | routing_data->Mods;
  15167                         Text("%s: 0x%08X (scored %d)", GetKeyChordName(key_chord), routing_data->RoutingCurr, routing_data->RoutingCurrScore);
  15168                         DebugLocateItemOnHover(routing_data->RoutingCurr);
  15169                         if (g.IO.ConfigDebugIsDebuggerPresent)
  15170                         {
  15171                             SameLine();
  15172                             if (DebugBreakButton("**DebugBreak**", "in SetShortcutRouting() for this KeyChord"))
  15173                                 g.DebugBreakInShortcutRouting = key_chord;
  15174                         }
  15175                         idx = routing_data->NextEntryIndex;
  15176                     }
  15177                 }
  15178             EndChild();
  15179             Text("(ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: 0x%X)", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);
  15180             Unindent();
  15181         }
  15182         TreePop();
  15183     }
  15184 
  15185     if (TreeNode("Internal state"))
  15186     {
  15187         Text("WINDOWING");
  15188         Indent();
  15189         Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
  15190         Text("HoveredWindow->Root: '%s'", g.HoveredWindow ? g.HoveredWindow->RootWindow->Name : "NULL");
  15191         Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL");
  15192         Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
  15193         Unindent();
  15194 
  15195         Text("ITEMS");
  15196         Indent();
  15197         Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, GetInputSourceName(g.ActiveIdSource));
  15198         DebugLocateItemOnHover(g.ActiveId);
  15199         Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
  15200         Text("ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: %X", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);
  15201         Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame
  15202         Text("HoverItemDelayId: 0x%08X, Timer: %.2f, ClearTimer: %.2f", g.HoverItemDelayId, g.HoverItemDelayTimer, g.HoverItemDelayClearTimer);
  15203         Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
  15204         DebugLocateItemOnHover(g.DragDropPayload.SourceId);
  15205         Unindent();
  15206 
  15207         Text("NAV,FOCUS");
  15208         Indent();
  15209         Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
  15210         Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
  15211         DebugLocateItemOnHover(g.NavId);
  15212         Text("NavInputSource: %s", GetInputSourceName(g.NavInputSource));
  15213         Text("NavLastValidSelectionUserData = %" IM_PRId64 " (0x%" IM_PRIX64 ")", g.NavLastValidSelectionUserData, g.NavLastValidSelectionUserData);
  15214         Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
  15215         Text("NavActivateId/DownId/PressedId: %08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId, g.NavActivatePressedId);
  15216         Text("NavActivateFlags: %04X", g.NavActivateFlags);
  15217         Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
  15218         Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId);
  15219         Text("NavFocusRoute[] = ");
  15220         for (int path_n = g.NavFocusRoute.Size - 1; path_n >= 0; path_n--)
  15221         {
  15222             const ImGuiFocusScopeData& focus_scope = g.NavFocusRoute[path_n];
  15223             SameLine(0.0f, 0.0f);
  15224             Text("0x%08X/", focus_scope.ID);
  15225             SetItemTooltip("In window \"%s\"", FindWindowByID(focus_scope.WindowID)->Name);
  15226         }
  15227         Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
  15228         Unindent();
  15229 
  15230         TreePop();
  15231     }
  15232 
  15233     // Overlay: Display windows Rectangles and Begin Order
  15234     if (cfg->ShowWindowsRects || cfg->ShowWindowsBeginOrder)
  15235     {
  15236         for (ImGuiWindow* window : g.Windows)
  15237         {
  15238             if (!window->WasActive)
  15239                 continue;
  15240             ImDrawList* draw_list = GetForegroundDrawList(window);
  15241             if (cfg->ShowWindowsRects)
  15242             {
  15243                 ImRect r = Funcs::GetWindowRect(window, cfg->ShowWindowsRectsType);
  15244                 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
  15245             }
  15246             if (cfg->ShowWindowsBeginOrder && !(window->Flags & ImGuiWindowFlags_ChildWindow))
  15247             {
  15248                 char buf[32];
  15249                 ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
  15250                 float font_size = GetFontSize();
  15251                 draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
  15252                 draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf);
  15253             }
  15254         }
  15255     }
  15256 
  15257     // Overlay: Display Tables Rectangles
  15258     if (cfg->ShowTablesRects)
  15259     {
  15260         for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)
  15261         {
  15262             ImGuiTable* table = g.Tables.TryGetMapData(table_n);
  15263             if (table == NULL || table->LastFrameActive < g.FrameCount - 1)
  15264                 continue;
  15265             ImDrawList* draw_list = GetForegroundDrawList(table->OuterWindow);
  15266             if (cfg->ShowTablesRectsType >= TRT_ColumnsRect)
  15267             {
  15268                 for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
  15269                 {
  15270                     ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, column_n);
  15271                     ImU32 col = (table->HoveredColumnBody == column_n) ? IM_COL32(255, 255, 128, 255) : IM_COL32(255, 0, 128, 255);
  15272                     float thickness = (table->HoveredColumnBody == column_n) ? 3.0f : 1.0f;
  15273                     draw_list->AddRect(r.Min, r.Max, col, 0.0f, 0, thickness);
  15274                 }
  15275             }
  15276             else
  15277             {
  15278                 ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, -1);
  15279                 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
  15280             }
  15281         }
  15282     }
  15283 
  15284 #ifdef IMGUI_HAS_DOCK
  15285     // Overlay: Display Docking info
  15286     if (show_docking_nodes && g.IO.KeyCtrl)
  15287     {
  15288     }
  15289 #endif // #ifdef IMGUI_HAS_DOCK
  15290 
  15291     End();
  15292 }
  15293 
  15294 void ImGui::DebugBreakClearData()
  15295 {
  15296     // Those fields are scattered in their respective subsystem to stay in hot-data locations
  15297     ImGuiContext& g = *GImGui;
  15298     g.DebugBreakInWindow = 0;
  15299     g.DebugBreakInTable = 0;
  15300     g.DebugBreakInShortcutRouting = ImGuiKey_None;
  15301 }
  15302 
  15303 void ImGui::DebugBreakButtonTooltip(bool keyboard_only, const char* description_of_location)
  15304 {
  15305     if (!BeginItemTooltip())
  15306         return;
  15307     Text("To call IM_DEBUG_BREAK() %s:", description_of_location);
  15308     Separator();
  15309     TextUnformatted(keyboard_only ? "- Press 'Pause/Break' on keyboard." : "- Press 'Pause/Break' on keyboard.\n- or Click (may alter focus/active id).\n- or navigate using keyboard and press space.");
  15310     Separator();
  15311     TextUnformatted("Choose one way that doesn't interfere with what you are trying to debug!\nYou need a debugger attached or this will crash!");
  15312     EndTooltip();
  15313 }
  15314 
  15315 // Special button that doesn't take focus, doesn't take input owner, and can be activated without a click etc.
  15316 // In order to reduce interferences with the contents we are trying to debug into.
  15317 bool ImGui::DebugBreakButton(const char* label, const char* description_of_location)
  15318 {
  15319     ImGuiWindow* window = GetCurrentWindow();
  15320     if (window->SkipItems)
  15321         return false;
  15322 
  15323     ImGuiContext& g = *GImGui;
  15324     const ImGuiID id = window->GetID(label);
  15325     const ImVec2 label_size = CalcTextSize(label, NULL, true);
  15326     ImVec2 pos = window->DC.CursorPos + ImVec2(0.0f, window->DC.CurrLineTextBaseOffset);
  15327     ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x * 2.0f, label_size.y);
  15328 
  15329     const ImRect bb(pos, pos + size);
  15330     ItemSize(size, 0.0f);
  15331     if (!ItemAdd(bb, id))
  15332         return false;
  15333 
  15334     // WE DO NOT USE ButtonEx() or ButtonBehavior() in order to reduce our side-effects.
  15335     bool hovered = ItemHoverable(bb, id, g.CurrentItemFlags);
  15336     bool pressed = hovered && (IsKeyChordPressed(g.DebugBreakKeyChord) || IsMouseClicked(0) || g.NavActivateId == id);
  15337     DebugBreakButtonTooltip(false, description_of_location);
  15338 
  15339     ImVec4 col4f = GetStyleColorVec4(hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
  15340     ImVec4 hsv;
  15341     ColorConvertRGBtoHSV(col4f.x, col4f.y, col4f.z, hsv.x, hsv.y, hsv.z);
  15342     ColorConvertHSVtoRGB(hsv.x + 0.20f, hsv.y, hsv.z, col4f.x, col4f.y, col4f.z);
  15343 
  15344     RenderNavHighlight(bb, id);
  15345     RenderFrame(bb.Min, bb.Max, GetColorU32(col4f), true, g.Style.FrameRounding);
  15346     RenderTextClipped(bb.Min, bb.Max, label, NULL, &label_size, g.Style.ButtonTextAlign, &bb);
  15347 
  15348     IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
  15349     return pressed;
  15350 }
  15351 
  15352 // [DEBUG] Display contents of Columns
  15353 void ImGui::DebugNodeColumns(ImGuiOldColumns* columns)
  15354 {
  15355     if (!TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
  15356         return;
  15357     BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX);
  15358     for (ImGuiOldColumnData& column : columns->Columns)
  15359         BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", (int)columns->Columns.index_from_ptr(&column), column.OffsetNorm, GetColumnOffsetFromNorm(columns, column.OffsetNorm));
  15360     TreePop();
  15361 }
  15362 
  15363 static void FormatTextureIDForDebugDisplay(char* buf, int buf_size, ImTextureID tex_id)
  15364 {
  15365     union { void* ptr; int integer; } tex_id_opaque;
  15366     memcpy(&tex_id_opaque, &tex_id, ImMin(sizeof(void*), sizeof(tex_id)));
  15367     if (sizeof(tex_id) >= sizeof(void*))
  15368         ImFormatString(buf, buf_size, "0x%p", tex_id_opaque.ptr);
  15369     else
  15370         ImFormatString(buf, buf_size, "0x%04X", tex_id_opaque.integer);
  15371 }
  15372 
  15373 // [DEBUG] Display contents of ImDrawList
  15374 void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label)
  15375 {
  15376     ImGuiContext& g = *GImGui;
  15377     IM_UNUSED(viewport); // Used in docking branch
  15378     ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
  15379     int cmd_count = draw_list->CmdBuffer.Size;
  15380     if (cmd_count > 0 && draw_list->CmdBuffer.back().ElemCount == 0 && draw_list->CmdBuffer.back().UserCallback == NULL)
  15381         cmd_count--;
  15382     bool node_open = TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, cmd_count);
  15383     if (draw_list == GetWindowDrawList())
  15384     {
  15385         SameLine();
  15386         TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
  15387         if (node_open)
  15388             TreePop();
  15389         return;
  15390     }
  15391 
  15392     ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list
  15393     if (window && IsItemHovered() && fg_draw_list)
  15394         fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
  15395     if (!node_open)
  15396         return;
  15397 
  15398     if (window && !window->WasActive)
  15399         TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!");
  15400 
  15401     for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.Data; pcmd < draw_list->CmdBuffer.Data + cmd_count; pcmd++)
  15402     {
  15403         if (pcmd->UserCallback)
  15404         {
  15405             BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
  15406             continue;
  15407         }
  15408 
  15409         char texid_desc[20];
  15410         FormatTextureIDForDebugDisplay(texid_desc, IM_ARRAYSIZE(texid_desc), pcmd->TextureId);
  15411         char buf[300];
  15412         ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d tris, Tex %s, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
  15413             pcmd->ElemCount / 3, texid_desc, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
  15414         bool pcmd_node_open = TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf);
  15415         if (IsItemHovered() && (cfg->ShowDrawCmdMesh || cfg->ShowDrawCmdBoundingBoxes) && fg_draw_list)
  15416             DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, cfg->ShowDrawCmdMesh, cfg->ShowDrawCmdBoundingBoxes);
  15417         if (!pcmd_node_open)
  15418             continue;
  15419 
  15420         // Calculate approximate coverage area (touched pixel count)
  15421         // This will be in pixels squared as long there's no post-scaling happening to the renderer output.
  15422         const ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
  15423         const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + pcmd->VtxOffset;
  15424         float total_area = 0.0f;
  15425         for (unsigned int idx_n = pcmd->IdxOffset; idx_n < pcmd->IdxOffset + pcmd->ElemCount; )
  15426         {
  15427             ImVec2 triangle[3];
  15428             for (int n = 0; n < 3; n++, idx_n++)
  15429                 triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos;
  15430             total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]);
  15431         }
  15432 
  15433         // Display vertex information summary. Hover to get all triangles drawn in wire-frame
  15434         ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area);
  15435         Selectable(buf);
  15436         if (IsItemHovered() && fg_draw_list)
  15437             DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, true, false);
  15438 
  15439         // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
  15440         ImGuiListClipper clipper;
  15441         clipper.Begin(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
  15442         while (clipper.Step())
  15443             for (int prim = clipper.DisplayStart, idx_i = pcmd->IdxOffset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++)
  15444             {
  15445                 char* buf_p = buf, * buf_end = buf + IM_ARRAYSIZE(buf);
  15446                 ImVec2 triangle[3];
  15447                 for (int n = 0; n < 3; n++, idx_i++)
  15448                 {
  15449                     const ImDrawVert& v = vtx_buffer[idx_buffer ? idx_buffer[idx_i] : idx_i];
  15450                     triangle[n] = v.pos;
  15451                     buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n",
  15452                         (n == 0) ? "Vert:" : "     ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
  15453                 }
  15454 
  15455                 Selectable(buf, false);
  15456                 if (fg_draw_list && IsItemHovered())
  15457                 {
  15458                     ImDrawListFlags backup_flags = fg_draw_list->Flags;
  15459                     fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
  15460                     fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f);
  15461                     fg_draw_list->Flags = backup_flags;
  15462                 }
  15463             }
  15464         TreePop();
  15465     }
  15466     TreePop();
  15467 }
  15468 
  15469 // [DEBUG] Display mesh/aabb of a ImDrawCmd
  15470 void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb)
  15471 {
  15472     IM_ASSERT(show_mesh || show_aabb);
  15473 
  15474     // Draw wire-frame version of all triangles
  15475     ImRect clip_rect = draw_cmd->ClipRect;
  15476     ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
  15477     ImDrawListFlags backup_flags = out_draw_list->Flags;
  15478     out_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
  15479     for (unsigned int idx_n = draw_cmd->IdxOffset, idx_end = draw_cmd->IdxOffset + draw_cmd->ElemCount; idx_n < idx_end; )
  15480     {
  15481         ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; // We don't hold on those pointers past iterations as ->AddPolyline() may invalidate them if out_draw_list==draw_list
  15482         ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset;
  15483 
  15484         ImVec2 triangle[3];
  15485         for (int n = 0; n < 3; n++, idx_n++)
  15486             vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos));
  15487         if (show_mesh)
  15488             out_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f); // In yellow: mesh triangles
  15489     }
  15490     // Draw bounding boxes
  15491     if (show_aabb)
  15492     {
  15493         out_draw_list->AddRect(ImTrunc(clip_rect.Min), ImTrunc(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU
  15494         out_draw_list->AddRect(ImTrunc(vtxs_rect.Min), ImTrunc(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles
  15495     }
  15496     out_draw_list->Flags = backup_flags;
  15497 }
  15498 
  15499 // [DEBUG] Display details for a single font, called by ShowStyleEditor().
  15500 void ImGui::DebugNodeFont(ImFont* font)
  15501 {
  15502     bool opened = TreeNode(font, "Font: \"%s\"\n%.2f px, %d glyphs, %d file(s)",
  15503         font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size, font->ConfigDataCount);
  15504     SameLine();
  15505     if (SmallButton("Set as default"))
  15506         GetIO().FontDefault = font;
  15507     if (!opened)
  15508         return;
  15509 
  15510     // Display preview text
  15511     PushFont(font);
  15512     Text("The quick brown fox jumps over the lazy dog");
  15513     PopFont();
  15514 
  15515     // Display details
  15516     SetNextItemWidth(GetFontSize() * 8);
  15517     DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f");
  15518     SameLine(); MetricsHelpMarker(
  15519         "Note that the default embedded font is NOT meant to be scaled.\n\n"
  15520         "Font are currently rendered into bitmaps at a given size at the time of building the atlas. "
  15521         "You may oversample them to get some flexibility with scaling. "
  15522         "You can also render at multiple sizes and select which one to use at runtime.\n\n"
  15523         "(Glimmer of hope: the atlas system will be rewritten in the future to make scaling more flexible.)");
  15524     Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent);
  15525     char c_str[5];
  15526     Text("Fallback character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->FallbackChar), font->FallbackChar);
  15527     Text("Ellipsis character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->EllipsisChar), font->EllipsisChar);
  15528     const int surface_sqrt = (int)ImSqrt((float)font->MetricsTotalSurface);
  15529     Text("Texture Area: about %d px ~%dx%d px", font->MetricsTotalSurface, surface_sqrt, surface_sqrt);
  15530     for (int config_i = 0; config_i < font->ConfigDataCount; config_i++)
  15531         if (font->ConfigData)
  15532             if (const ImFontConfig* cfg = &font->ConfigData[config_i])
  15533                 BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d, Offset: (%.1f,%.1f)",
  15534                     config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH, cfg->GlyphOffset.x, cfg->GlyphOffset.y);
  15535 
  15536     // Display all glyphs of the fonts in separate pages of 256 characters
  15537     if (TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size))
  15538     {
  15539         ImDrawList* draw_list = GetWindowDrawList();
  15540         const ImU32 glyph_col = GetColorU32(ImGuiCol_Text);
  15541         const float cell_size = font->FontSize * 1;
  15542         const float cell_spacing = GetStyle().ItemSpacing.y;
  15543         for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256)
  15544         {
  15545             // Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k)
  15546             // This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT
  15547             // is large // (if ImWchar==ImWchar32 we will do at least about 272 queries here)
  15548             if (!(base & 4095) && font->IsGlyphRangeUnused(base, base + 4095))
  15549             {
  15550                 base += 4096 - 256;
  15551                 continue;
  15552             }
  15553 
  15554             int count = 0;
  15555             for (unsigned int n = 0; n < 256; n++)
  15556                 if (font->FindGlyphNoFallback((ImWchar)(base + n)))
  15557                     count++;
  15558             if (count <= 0)
  15559                 continue;
  15560             if (!TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph"))
  15561                 continue;
  15562 
  15563             // Draw a 16x16 grid of glyphs
  15564             ImVec2 base_pos = GetCursorScreenPos();
  15565             for (unsigned int n = 0; n < 256; n++)
  15566             {
  15567                 // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions
  15568                 // available here and thus cannot easily generate a zero-terminated UTF-8 encoded string.
  15569                 ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing));
  15570                 ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size);
  15571                 const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n));
  15572                 draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50));
  15573                 if (!glyph)
  15574                     continue;
  15575                 font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n));
  15576                 if (IsMouseHoveringRect(cell_p1, cell_p2) && BeginTooltip())
  15577                 {
  15578                     DebugNodeFontGlyph(font, glyph);
  15579                     EndTooltip();
  15580                 }
  15581             }
  15582             Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16));
  15583             TreePop();
  15584         }
  15585         TreePop();
  15586     }
  15587     TreePop();
  15588 }
  15589 
  15590 void ImGui::DebugNodeFontGlyph(ImFont*, const ImFontGlyph* glyph)
  15591 {
  15592     Text("Codepoint: U+%04X", glyph->Codepoint);
  15593     Separator();
  15594     Text("Visible: %d", glyph->Visible);
  15595     Text("AdvanceX: %.1f", glyph->AdvanceX);
  15596     Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1);
  15597     Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1);
  15598 }
  15599 
  15600 // [DEBUG] Display contents of ImGuiStorage
  15601 void ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label)
  15602 {
  15603     if (!TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes()))
  15604         return;
  15605     for (const ImGuiStoragePair& p : storage->Data)
  15606     {
  15607         BulletText("Key 0x%08X Value { i: %d }", p.key, p.val_i); // Important: we currently don't store a type, real value may not be integer.
  15608         DebugLocateItemOnHover(p.key);
  15609     }
  15610     TreePop();
  15611 }
  15612 
  15613 // [DEBUG] Display contents of ImGuiTabBar
  15614 void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label)
  15615 {
  15616     // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
  15617     char buf[256];
  15618     char* p = buf;
  15619     const char* buf_end = buf + IM_ARRAYSIZE(buf);
  15620     const bool is_active = (tab_bar->PrevFrameVisible >= GetFrameCount() - 2);
  15621     p += ImFormatString(p, buf_end - p, "%s 0x%08X (%d tabs)%s  {", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*");
  15622     for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++)
  15623     {
  15624         ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
  15625         p += ImFormatString(p, buf_end - p, "%s'%s'", tab_n > 0 ? ", " : "", TabBarGetTabName(tab_bar, tab));
  15626     }
  15627     p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } ");
  15628     if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
  15629     bool open = TreeNode(label, "%s", buf);
  15630     if (!is_active) { PopStyleColor(); }
  15631     if (is_active && IsItemHovered())
  15632     {
  15633         ImDrawList* draw_list = GetForegroundDrawList();
  15634         draw_list->AddRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, IM_COL32(255, 255, 0, 255));
  15635         draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));
  15636         draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));
  15637     }
  15638     if (open)
  15639     {
  15640         for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
  15641         {
  15642             ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
  15643             PushID(tab);
  15644             if (SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } SameLine(0, 2);
  15645             if (SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } SameLine();
  15646             Text("%02d%c Tab 0x%08X '%s' Offset: %.2f, Width: %.2f/%.2f",
  15647                 tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, TabBarGetTabName(tab_bar, tab), tab->Offset, tab->Width, tab->ContentWidth);
  15648             PopID();
  15649         }
  15650         TreePop();
  15651     }
  15652 }
  15653 
  15654 void ImGui::DebugNodeViewport(ImGuiViewportP* viewport)
  15655 {
  15656     ImGuiContext& g = *GImGui;
  15657     SetNextItemOpen(true, ImGuiCond_Once);
  15658     bool open = TreeNode("viewport0", "Viewport #%d", 0);
  15659     if (IsItemHovered())
  15660         g.DebugMetricsConfig.HighlightViewportID = viewport->ID;
  15661     if (open)
  15662     {
  15663         ImGuiWindowFlags flags = viewport->Flags;
  15664         BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Offset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f",
  15665             viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y,
  15666             viewport->WorkOffsetMin.x, viewport->WorkOffsetMin.y, viewport->WorkOffsetMax.x, viewport->WorkOffsetMax.y);
  15667         BulletText("Flags: 0x%04X =%s%s%s", viewport->Flags,
  15668             (flags & ImGuiViewportFlags_IsPlatformWindow)  ? " IsPlatformWindow"  : "",
  15669             (flags & ImGuiViewportFlags_IsPlatformMonitor) ? " IsPlatformMonitor" : "",
  15670             (flags & ImGuiViewportFlags_OwnedByApp)        ? " OwnedByApp"        : "");
  15671         for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists)
  15672             DebugNodeDrawList(NULL, viewport, draw_list, "DrawList");
  15673         TreePop();
  15674     }
  15675 }
  15676 
  15677 void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)
  15678 {
  15679     if (window == NULL)
  15680     {
  15681         BulletText("%s: NULL", label);
  15682         return;
  15683     }
  15684 
  15685     ImGuiContext& g = *GImGui;
  15686     const bool is_active = window->WasActive;
  15687     ImGuiTreeNodeFlags tree_node_flags = (window == g.NavWindow) ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None;
  15688     if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
  15689     const bool open = TreeNodeEx(label, tree_node_flags, "%s '%s'%s", label, window->Name, is_active ? "" : " *Inactive*");
  15690     if (!is_active) { PopStyleColor(); }
  15691     if (IsItemHovered() && is_active)
  15692         GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
  15693     if (!open)
  15694         return;
  15695 
  15696     if (window->MemoryCompacted)
  15697         TextDisabled("Note: some memory buffers have been compacted/freed.");
  15698 
  15699     if (g.IO.ConfigDebugIsDebuggerPresent && DebugBreakButton("**DebugBreak**", "in Begin()"))
  15700         g.DebugBreakInWindow = window->ID;
  15701 
  15702     ImGuiWindowFlags flags = window->Flags;
  15703     DebugNodeDrawList(window, window->Viewport, window->DrawList, "DrawList");
  15704     BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f) Ideal (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y, window->ContentSizeIdeal.x, window->ContentSizeIdeal.y);
  15705     BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
  15706         (flags & ImGuiWindowFlags_ChildWindow)  ? "Child " : "",      (flags & ImGuiWindowFlags_Tooltip)     ? "Tooltip "   : "",  (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
  15707         (flags & ImGuiWindowFlags_Modal)        ? "Modal " : "",      (flags & ImGuiWindowFlags_ChildMenu)   ? "ChildMenu " : "",  (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
  15708         (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
  15709     if (flags & ImGuiWindowFlags_ChildWindow)
  15710         BulletText("ChildFlags: 0x%08X (%s%s%s%s..)", window->ChildFlags,
  15711             (window->ChildFlags & ImGuiChildFlags_Border) ? "Border " : "",
  15712             (window->ChildFlags & ImGuiChildFlags_ResizeX) ? "ResizeX " : "",
  15713             (window->ChildFlags & ImGuiChildFlags_ResizeY) ? "ResizeY " : "",
  15714             (window->ChildFlags & ImGuiChildFlags_NavFlattened) ? "NavFlattened " : "");
  15715     BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y, window->ScrollbarX ? "X" : "", window->ScrollbarY ? "Y" : "");
  15716     BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
  15717     BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems);
  15718     for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)
  15719     {
  15720         ImRect r = window->NavRectRel[layer];
  15721         if (r.Min.x >= r.Max.y && r.Min.y >= r.Max.y)
  15722             BulletText("NavLastIds[%d]: 0x%08X", layer, window->NavLastIds[layer]);
  15723         else
  15724             BulletText("NavLastIds[%d]: 0x%08X at +(%.1f,%.1f)(%.1f,%.1f)", layer, window->NavLastIds[layer], r.Min.x, r.Min.y, r.Max.x, r.Max.y);
  15725         DebugLocateItemOnHover(window->NavLastIds[layer]);
  15726     }
  15727     const ImVec2* pr = window->NavPreferredScoringPosRel;
  15728     for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)
  15729         BulletText("NavPreferredScoringPosRel[%d] = {%.1f,%.1f)", layer, (pr[layer].x == FLT_MAX ? -99999.0f : pr[layer].x), (pr[layer].y == FLT_MAX ? -99999.0f : pr[layer].y)); // Display as 99999.0f so it looks neater.
  15730     BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
  15731     if (window->RootWindow != window)               { DebugNodeWindow(window->RootWindow, "RootWindow"); }
  15732     if (window->ParentWindow != NULL)               { DebugNodeWindow(window->ParentWindow, "ParentWindow"); }
  15733     if (window->ParentWindowForFocusRoute != NULL)  { DebugNodeWindow(window->ParentWindowForFocusRoute, "ParentWindowForFocusRoute"); }
  15734     if (window->DC.ChildWindows.Size > 0)           { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); }
  15735     if (window->ColumnsStorage.Size > 0 && TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
  15736     {
  15737         for (ImGuiOldColumns& columns : window->ColumnsStorage)
  15738             DebugNodeColumns(&columns);
  15739         TreePop();
  15740     }
  15741     DebugNodeStorage(&window->StateStorage, "Storage");
  15742     TreePop();
  15743 }
  15744 
  15745 void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings* settings)
  15746 {
  15747     if (settings->WantDelete)
  15748         BeginDisabled();
  15749     Text("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d",
  15750         settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed);
  15751     if (settings->WantDelete)
  15752         EndDisabled();
  15753 }
  15754 
  15755 void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label)
  15756 {
  15757     if (!TreeNode(label, "%s (%d)", label, windows->Size))
  15758         return;
  15759     for (int i = windows->Size - 1; i >= 0; i--) // Iterate front to back
  15760     {
  15761         PushID((*windows)[i]);
  15762         DebugNodeWindow((*windows)[i], "Window");
  15763         PopID();
  15764     }
  15765     TreePop();
  15766 }
  15767 
  15768 // FIXME-OPT: This is technically suboptimal, but it is simpler this way.
  15769 void ImGui::DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack)
  15770 {
  15771     for (int i = 0; i < windows_size; i++)
  15772     {
  15773         ImGuiWindow* window = windows[i];
  15774         if (window->ParentWindowInBeginStack != parent_in_begin_stack)
  15775             continue;
  15776         char buf[20];
  15777         ImFormatString(buf, IM_ARRAYSIZE(buf), "[%04d] Window", window->BeginOrderWithinContext);
  15778         //BulletText("[%04d] Window '%s'", window->BeginOrderWithinContext, window->Name);
  15779         DebugNodeWindow(window, buf);
  15780         Indent();
  15781         DebugNodeWindowsListByBeginStackParent(windows + i + 1, windows_size - i - 1, window);
  15782         Unindent();
  15783     }
  15784 }
  15785 
  15786 //-----------------------------------------------------------------------------
  15787 // [SECTION] DEBUG LOG WINDOW
  15788 //-----------------------------------------------------------------------------
  15789 
  15790 void ImGui::DebugLog(const char* fmt, ...)
  15791 {
  15792     va_list args;
  15793     va_start(args, fmt);
  15794     DebugLogV(fmt, args);
  15795     va_end(args);
  15796 }
  15797 
  15798 void ImGui::DebugLogV(const char* fmt, va_list args)
  15799 {
  15800     ImGuiContext& g = *GImGui;
  15801     const int old_size = g.DebugLogBuf.size();
  15802     if (g.ContextName[0] != 0)
  15803         g.DebugLogBuf.appendf("[%s] [%05d] ", g.ContextName, g.FrameCount);
  15804     else
  15805         g.DebugLogBuf.appendf("[%05d] ", g.FrameCount);
  15806     g.DebugLogBuf.appendfv(fmt, args);
  15807     g.DebugLogIndex.append(g.DebugLogBuf.c_str(), old_size, g.DebugLogBuf.size());
  15808     if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTTY)
  15809         IMGUI_DEBUG_PRINTF("%s", g.DebugLogBuf.begin() + old_size);
  15810 #ifdef IMGUI_ENABLE_TEST_ENGINE
  15811     // IMGUI_TEST_ENGINE_LOG() adds a trailing \n automatically
  15812     const int new_size = g.DebugLogBuf.size();
  15813     const bool trailing_carriage_return = (g.DebugLogBuf[new_size - 1] == '\n');
  15814     if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTestEngine)
  15815         IMGUI_TEST_ENGINE_LOG("%.*s", new_size - old_size - (trailing_carriage_return ? 1 : 0), g.DebugLogBuf.begin() + old_size);
  15816 #endif
  15817 }
  15818 
  15819 // FIXME-LAYOUT: To be done automatically via layout mode once we rework ItemSize/ItemAdd into ItemLayout.
  15820 static void SameLineOrWrap(const ImVec2& size)
  15821 {
  15822     ImGuiContext& g = *GImGui;
  15823     ImGuiWindow* window = g.CurrentWindow;
  15824     ImVec2 pos(window->DC.CursorPosPrevLine.x + g.Style.ItemSpacing.x, window->DC.CursorPosPrevLine.y);
  15825     if (window->WorkRect.Contains(ImRect(pos, pos + size)))
  15826         ImGui::SameLine();
  15827 }
  15828 
  15829 static void ShowDebugLogFlag(const char* name, ImGuiDebugLogFlags flags)
  15830 {
  15831     ImGuiContext& g = *GImGui;
  15832     ImVec2 size(ImGui::GetFrameHeight() + g.Style.ItemInnerSpacing.x + ImGui::CalcTextSize(name).x, ImGui::GetFrameHeight());
  15833     SameLineOrWrap(size); // FIXME-LAYOUT: To be done automatically once we rework ItemSize/ItemAdd into ItemLayout.
  15834     if (ImGui::CheckboxFlags(name, &g.DebugLogFlags, flags) && g.IO.KeyShift && (g.DebugLogFlags & flags) != 0)
  15835     {
  15836         g.DebugLogAutoDisableFrames = 2;
  15837         g.DebugLogAutoDisableFlags |= flags;
  15838     }
  15839     ImGui::SetItemTooltip("Hold SHIFT when clicking to enable for 2 frames only (useful for spammy log entries)");
  15840 }
  15841 
  15842 void ImGui::ShowDebugLogWindow(bool* p_open)
  15843 {
  15844     ImGuiContext& g = *GImGui;
  15845     if (!(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize))
  15846         SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 12.0f), ImGuiCond_FirstUseEver);
  15847     if (!Begin("Dear ImGui Debug Log", p_open) || GetCurrentWindow()->BeginCount > 1)
  15848     {
  15849         End();
  15850         return;
  15851     }
  15852 
  15853     ImGuiDebugLogFlags all_enable_flags = ImGuiDebugLogFlags_EventMask_ & ~ImGuiDebugLogFlags_EventInputRouting;
  15854     CheckboxFlags("All", &g.DebugLogFlags, all_enable_flags);
  15855     SetItemTooltip("(except InputRouting which is spammy)");
  15856 
  15857     ShowDebugLogFlag("ActiveId", ImGuiDebugLogFlags_EventActiveId);
  15858     ShowDebugLogFlag("Clipper", ImGuiDebugLogFlags_EventClipper);
  15859     ShowDebugLogFlag("Focus", ImGuiDebugLogFlags_EventFocus);
  15860     ShowDebugLogFlag("IO", ImGuiDebugLogFlags_EventIO);
  15861     ShowDebugLogFlag("Nav", ImGuiDebugLogFlags_EventNav);
  15862     ShowDebugLogFlag("Popup", ImGuiDebugLogFlags_EventPopup);
  15863     ShowDebugLogFlag("Selection", ImGuiDebugLogFlags_EventSelection);
  15864     ShowDebugLogFlag("InputRouting", ImGuiDebugLogFlags_EventInputRouting);
  15865 
  15866     if (SmallButton("Clear"))
  15867     {
  15868         g.DebugLogBuf.clear();
  15869         g.DebugLogIndex.clear();
  15870     }
  15871     SameLine();
  15872     if (SmallButton("Copy"))
  15873         SetClipboardText(g.DebugLogBuf.c_str());
  15874     SameLine();
  15875     if (SmallButton("Configure Outputs.."))
  15876         OpenPopup("Outputs");
  15877     if (BeginPopup("Outputs"))
  15878     {
  15879         CheckboxFlags("OutputToTTY", &g.DebugLogFlags, ImGuiDebugLogFlags_OutputToTTY);
  15880 #ifndef IMGUI_ENABLE_TEST_ENGINE
  15881         BeginDisabled();
  15882 #endif
  15883         CheckboxFlags("OutputToTestEngine", &g.DebugLogFlags, ImGuiDebugLogFlags_OutputToTestEngine);
  15884 #ifndef IMGUI_ENABLE_TEST_ENGINE
  15885         EndDisabled();
  15886 #endif
  15887         EndPopup();
  15888     }
  15889 
  15890     BeginChild("##log", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Border, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar);
  15891 
  15892     const ImGuiDebugLogFlags backup_log_flags = g.DebugLogFlags;
  15893     g.DebugLogFlags &= ~ImGuiDebugLogFlags_EventClipper;
  15894 
  15895     ImGuiListClipper clipper;
  15896     clipper.Begin(g.DebugLogIndex.size());
  15897     while (clipper.Step())
  15898         for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
  15899             DebugTextUnformattedWithLocateItem(g.DebugLogIndex.get_line_begin(g.DebugLogBuf.c_str(), line_no), g.DebugLogIndex.get_line_end(g.DebugLogBuf.c_str(), line_no));
  15900     g.DebugLogFlags = backup_log_flags;
  15901     if (GetScrollY() >= GetScrollMaxY())
  15902         SetScrollHereY(1.0f);
  15903     EndChild();
  15904 
  15905     End();
  15906 }
  15907 
  15908 // Display line, search for 0xXXXXXXXX identifiers and call DebugLocateItemOnHover() when hovered.
  15909 void ImGui::DebugTextUnformattedWithLocateItem(const char* line_begin, const char* line_end)
  15910 {
  15911     TextUnformatted(line_begin, line_end);
  15912     if (!IsItemHovered())
  15913         return;
  15914     ImGuiContext& g = *GImGui;
  15915     ImRect text_rect = g.LastItemData.Rect;
  15916     for (const char* p = line_begin; p <= line_end - 10; p++)
  15917     {
  15918         ImGuiID id = 0;
  15919         if (p[0] != '0' || (p[1] != 'x' && p[1] != 'X') || sscanf(p + 2, "%X", &id) != 1 || ImCharIsXdigitA(p[10]))
  15920             continue;
  15921         ImVec2 p0 = CalcTextSize(line_begin, p);
  15922         ImVec2 p1 = CalcTextSize(p, p + 10);
  15923         g.LastItemData.Rect = ImRect(text_rect.Min + ImVec2(p0.x, 0.0f), text_rect.Min + ImVec2(p0.x + p1.x, p1.y));
  15924         if (IsMouseHoveringRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, true))
  15925             DebugLocateItemOnHover(id);
  15926         p += 10;
  15927     }
  15928 }
  15929 
  15930 //-----------------------------------------------------------------------------
  15931 // [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL)
  15932 //-----------------------------------------------------------------------------
  15933 
  15934 // Draw a small cross at current CursorPos in current window's DrawList
  15935 void ImGui::DebugDrawCursorPos(ImU32 col)
  15936 {
  15937     ImGuiContext& g = *GImGui;
  15938     ImGuiWindow* window = g.CurrentWindow;
  15939     ImVec2 pos = window->DC.CursorPos;
  15940     window->DrawList->AddLine(ImVec2(pos.x, pos.y - 3.0f), ImVec2(pos.x, pos.y + 4.0f), col, 1.0f);
  15941     window->DrawList->AddLine(ImVec2(pos.x - 3.0f, pos.y), ImVec2(pos.x + 4.0f, pos.y), col, 1.0f);
  15942 }
  15943 
  15944 // Draw a 10px wide rectangle around CurposPos.x using Line Y1/Y2 in current window's DrawList
  15945 void ImGui::DebugDrawLineExtents(ImU32 col)
  15946 {
  15947     ImGuiContext& g = *GImGui;
  15948     ImGuiWindow* window = g.CurrentWindow;
  15949     float curr_x = window->DC.CursorPos.x;
  15950     float line_y1 = (window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y);
  15951     float line_y2 = line_y1 + (window->DC.IsSameLine ? window->DC.PrevLineSize.y : window->DC.CurrLineSize.y);
  15952     window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y1), ImVec2(curr_x + 5.0f, line_y1), col, 1.0f);
  15953     window->DrawList->AddLine(ImVec2(curr_x - 0.5f, line_y1), ImVec2(curr_x - 0.5f, line_y2), col, 1.0f);
  15954     window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y2), ImVec2(curr_x + 5.0f, line_y2), col, 1.0f);
  15955 }
  15956 
  15957 // Draw last item rect in ForegroundDrawList (so it is always visible)
  15958 void ImGui::DebugDrawItemRect(ImU32 col)
  15959 {
  15960     ImGuiContext& g = *GImGui;
  15961     ImGuiWindow* window = g.CurrentWindow;
  15962     GetForegroundDrawList(window)->AddRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, col);
  15963 }
  15964 
  15965 // [DEBUG] Locate item position/rectangle given an ID.
  15966 static const ImU32 DEBUG_LOCATE_ITEM_COLOR = IM_COL32(0, 255, 0, 255);  // Green
  15967 
  15968 void ImGui::DebugLocateItem(ImGuiID target_id)
  15969 {
  15970     ImGuiContext& g = *GImGui;
  15971     g.DebugLocateId = target_id;
  15972     g.DebugLocateFrames = 2;
  15973     g.DebugBreakInLocateId = false;
  15974 }
  15975 
  15976 // FIXME: Doesn't work over through a modal window, because they clear HoveredWindow.
  15977 void ImGui::DebugLocateItemOnHover(ImGuiID target_id)
  15978 {
  15979     if (target_id == 0 || !IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenBlockedByPopup))
  15980         return;
  15981     ImGuiContext& g = *GImGui;
  15982     DebugLocateItem(target_id);
  15983     GetForegroundDrawList(g.CurrentWindow)->AddRect(g.LastItemData.Rect.Min - ImVec2(3.0f, 3.0f), g.LastItemData.Rect.Max + ImVec2(3.0f, 3.0f), DEBUG_LOCATE_ITEM_COLOR);
  15984 
  15985     // Can't easily use a context menu here because it will mess with focus, active id etc.
  15986     if (g.IO.ConfigDebugIsDebuggerPresent && g.MouseStationaryTimer > 1.0f)
  15987     {
  15988         DebugBreakButtonTooltip(false, "in ItemAdd()");
  15989         if (IsKeyChordPressed(g.DebugBreakKeyChord))
  15990             g.DebugBreakInLocateId = true;
  15991     }
  15992 }
  15993 
  15994 void ImGui::DebugLocateItemResolveWithLastItem()
  15995 {
  15996     ImGuiContext& g = *GImGui;
  15997 
  15998     // [DEBUG] Debug break requested by user
  15999     if (g.DebugBreakInLocateId)
  16000         IM_DEBUG_BREAK();
  16001 
  16002     ImGuiLastItemData item_data = g.LastItemData;
  16003     g.DebugLocateId = 0;
  16004     ImDrawList* draw_list = GetForegroundDrawList(g.CurrentWindow);
  16005     ImRect r = item_data.Rect;
  16006     r.Expand(3.0f);
  16007     ImVec2 p1 = g.IO.MousePos;
  16008     ImVec2 p2 = ImVec2((p1.x < r.Min.x) ? r.Min.x : (p1.x > r.Max.x) ? r.Max.x : p1.x, (p1.y < r.Min.y) ? r.Min.y : (p1.y > r.Max.y) ? r.Max.y : p1.y);
  16009     draw_list->AddRect(r.Min, r.Max, DEBUG_LOCATE_ITEM_COLOR);
  16010     draw_list->AddLine(p1, p2, DEBUG_LOCATE_ITEM_COLOR);
  16011 }
  16012 
  16013 void ImGui::DebugStartItemPicker()
  16014 {
  16015     ImGuiContext& g = *GImGui;
  16016     g.DebugItemPickerActive = true;
  16017 }
  16018 
  16019 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
  16020 void ImGui::UpdateDebugToolItemPicker()
  16021 {
  16022     ImGuiContext& g = *GImGui;
  16023     g.DebugItemPickerBreakId = 0;
  16024     if (!g.DebugItemPickerActive)
  16025         return;
  16026 
  16027     const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
  16028     SetMouseCursor(ImGuiMouseCursor_Hand);
  16029     if (IsKeyPressed(ImGuiKey_Escape))
  16030         g.DebugItemPickerActive = false;
  16031     const bool change_mapping = g.IO.KeyMods == (ImGuiMod_Ctrl | ImGuiMod_Shift);
  16032     if (!change_mapping && IsMouseClicked(g.DebugItemPickerMouseButton) && hovered_id)
  16033     {
  16034         g.DebugItemPickerBreakId = hovered_id;
  16035         g.DebugItemPickerActive = false;
  16036     }
  16037     for (int mouse_button = 0; mouse_button < 3; mouse_button++)
  16038         if (change_mapping && IsMouseClicked(mouse_button))
  16039             g.DebugItemPickerMouseButton = (ImU8)mouse_button;
  16040     SetNextWindowBgAlpha(0.70f);
  16041     if (!BeginTooltip())
  16042         return;
  16043     Text("HoveredId: 0x%08X", hovered_id);
  16044     Text("Press ESC to abort picking.");
  16045     const char* mouse_button_names[] = { "Left", "Right", "Middle" };
  16046     if (change_mapping)
  16047         Text("Remap w/ Ctrl+Shift: click anywhere to select new mouse button.");
  16048     else
  16049         TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click %s Button to break in debugger! (remap w/ Ctrl+Shift)", mouse_button_names[g.DebugItemPickerMouseButton]);
  16050     EndTooltip();
  16051 }
  16052 
  16053 // [DEBUG] ID Stack Tool: update queries. Called by NewFrame()
  16054 void ImGui::UpdateDebugToolStackQueries()
  16055 {
  16056     ImGuiContext& g = *GImGui;
  16057     ImGuiIDStackTool* tool = &g.DebugIDStackTool;
  16058 
  16059     // Clear hook when id stack tool is not visible
  16060     g.DebugHookIdInfo = 0;
  16061     if (g.FrameCount != tool->LastActiveFrame + 1)
  16062         return;
  16063 
  16064     // Update queries. The steps are: -1: query Stack, >= 0: query each stack item
  16065     // We can only perform 1 ID Info query every frame. This is designed so the GetID() tests are cheap and constant-time
  16066     const ImGuiID query_id = g.HoveredIdPreviousFrame ? g.HoveredIdPreviousFrame : g.ActiveId;
  16067     if (tool->QueryId != query_id)
  16068     {
  16069         tool->QueryId = query_id;
  16070         tool->StackLevel = -1;
  16071         tool->Results.resize(0);
  16072     }
  16073     if (query_id == 0)
  16074         return;
  16075 
  16076     // Advance to next stack level when we got our result, or after 2 frames (in case we never get a result)
  16077     int stack_level = tool->StackLevel;
  16078     if (stack_level >= 0 && stack_level < tool->Results.Size)
  16079         if (tool->Results[stack_level].QuerySuccess || tool->Results[stack_level].QueryFrameCount > 2)
  16080             tool->StackLevel++;
  16081 
  16082     // Update hook
  16083     stack_level = tool->StackLevel;
  16084     if (stack_level == -1)
  16085         g.DebugHookIdInfo = query_id;
  16086     if (stack_level >= 0 && stack_level < tool->Results.Size)
  16087     {
  16088         g.DebugHookIdInfo = tool->Results[stack_level].ID;
  16089         tool->Results[stack_level].QueryFrameCount++;
  16090     }
  16091 }
  16092 
  16093 // [DEBUG] ID Stack tool: hooks called by GetID() family functions
  16094 void ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end)
  16095 {
  16096     ImGuiContext& g = *GImGui;
  16097     ImGuiWindow* window = g.CurrentWindow;
  16098     ImGuiIDStackTool* tool = &g.DebugIDStackTool;
  16099 
  16100     // Step 0: stack query
  16101     // This assumes that the ID was computed with the current ID stack, which tends to be the case for our widget.
  16102     if (tool->StackLevel == -1)
  16103     {
  16104         tool->StackLevel++;
  16105         tool->Results.resize(window->IDStack.Size + 1, ImGuiStackLevelInfo());
  16106         for (int n = 0; n < window->IDStack.Size + 1; n++)
  16107             tool->Results[n].ID = (n < window->IDStack.Size) ? window->IDStack[n] : id;
  16108         return;
  16109     }
  16110 
  16111     // Step 1+: query for individual level
  16112     IM_ASSERT(tool->StackLevel >= 0);
  16113     if (tool->StackLevel != window->IDStack.Size)
  16114         return;
  16115     ImGuiStackLevelInfo* info = &tool->Results[tool->StackLevel];
  16116     IM_ASSERT(info->ID == id && info->QueryFrameCount > 0);
  16117 
  16118     switch (data_type)
  16119     {
  16120     case ImGuiDataType_S32:
  16121         ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "%d", (int)(intptr_t)data_id);
  16122         break;
  16123     case ImGuiDataType_String:
  16124         ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "%.*s", data_id_end ? (int)((const char*)data_id_end - (const char*)data_id) : (int)strlen((const char*)data_id), (const char*)data_id);
  16125         break;
  16126     case ImGuiDataType_Pointer:
  16127         ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "(void*)0x%p", data_id);
  16128         break;
  16129     case ImGuiDataType_ID:
  16130         if (info->Desc[0] != 0) // PushOverrideID() is often used to avoid hashing twice, which would lead to 2 calls to DebugHookIdInfo(). We prioritize the first one.
  16131             return;
  16132         ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "0x%08X [override]", id);
  16133         break;
  16134     default:
  16135         IM_ASSERT(0);
  16136     }
  16137     info->QuerySuccess = true;
  16138     info->DataType = data_type;
  16139 }
  16140 
  16141 static int StackToolFormatLevelInfo(ImGuiIDStackTool* tool, int n, bool format_for_ui, char* buf, size_t buf_size)
  16142 {
  16143     ImGuiStackLevelInfo* info = &tool->Results[n];
  16144     ImGuiWindow* window = (info->Desc[0] == 0 && n == 0) ? ImGui::FindWindowByID(info->ID) : NULL;
  16145     if (window)                                                                 // Source: window name (because the root ID don't call GetID() and so doesn't get hooked)
  16146         return ImFormatString(buf, buf_size, format_for_ui ? "\"%s\" [window]" : "%s", window->Name);
  16147     if (info->QuerySuccess)                                                     // Source: GetID() hooks (prioritize over ItemInfo() because we frequently use patterns like: PushID(str), Button("") where they both have same id)
  16148         return ImFormatString(buf, buf_size, (format_for_ui && info->DataType == ImGuiDataType_String) ? "\"%s\"" : "%s", info->Desc);
  16149     if (tool->StackLevel < tool->Results.Size)                                  // Only start using fallback below when all queries are done, so during queries we don't flickering ??? markers.
  16150         return (*buf = 0);
  16151 #ifdef IMGUI_ENABLE_TEST_ENGINE
  16152     if (const char* label = ImGuiTestEngine_FindItemDebugLabel(GImGui, info->ID))   // Source: ImGuiTestEngine's ItemInfo()
  16153         return ImFormatString(buf, buf_size, format_for_ui ? "??? \"%s\"" : "%s", label);
  16154 #endif
  16155     return ImFormatString(buf, buf_size, "???");
  16156 }
  16157 
  16158 // ID Stack Tool: Display UI
  16159 void ImGui::ShowIDStackToolWindow(bool* p_open)
  16160 {
  16161     ImGuiContext& g = *GImGui;
  16162     if (!(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize))
  16163         SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 8.0f), ImGuiCond_FirstUseEver);
  16164     if (!Begin("Dear ImGui ID Stack Tool", p_open) || GetCurrentWindow()->BeginCount > 1)
  16165     {
  16166         End();
  16167         return;
  16168     }
  16169 
  16170     // Display hovered/active status
  16171     ImGuiIDStackTool* tool = &g.DebugIDStackTool;
  16172     const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
  16173     const ImGuiID active_id = g.ActiveId;
  16174 #ifdef IMGUI_ENABLE_TEST_ENGINE
  16175     Text("HoveredId: 0x%08X (\"%s\"), ActiveId:  0x%08X (\"%s\")", hovered_id, hovered_id ? ImGuiTestEngine_FindItemDebugLabel(&g, hovered_id) : "", active_id, active_id ? ImGuiTestEngine_FindItemDebugLabel(&g, active_id) : "");
  16176 #else
  16177     Text("HoveredId: 0x%08X, ActiveId:  0x%08X", hovered_id, active_id);
  16178 #endif
  16179     SameLine();
  16180     MetricsHelpMarker("Hover an item with the mouse to display elements of the ID Stack leading to the item's final ID.\nEach level of the stack correspond to a PushID() call.\nAll levels of the stack are hashed together to make the final ID of a widget (ID displayed at the bottom level of the stack).\nRead FAQ entry about the ID stack for details.");
  16181 
  16182     // CTRL+C to copy path
  16183     const float time_since_copy = (float)g.Time - tool->CopyToClipboardLastTime;
  16184     Checkbox("Ctrl+C: copy path to clipboard", &tool->CopyToClipboardOnCtrlC);
  16185     SameLine();
  16186     TextColored((time_since_copy >= 0.0f && time_since_copy < 0.75f && ImFmod(time_since_copy, 0.25f) < 0.25f * 0.5f) ? ImVec4(1.f, 1.f, 0.3f, 1.f) : ImVec4(), "*COPIED*");
  16187     if (tool->CopyToClipboardOnCtrlC && Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused))
  16188     {
  16189         tool->CopyToClipboardLastTime = (float)g.Time;
  16190         char* p = g.TempBuffer.Data;
  16191         char* p_end = p + g.TempBuffer.Size;
  16192         for (int stack_n = 0; stack_n < tool->Results.Size && p + 3 < p_end; stack_n++)
  16193         {
  16194             *p++ = '/';
  16195             char level_desc[256];
  16196             StackToolFormatLevelInfo(tool, stack_n, false, level_desc, IM_ARRAYSIZE(level_desc));
  16197             for (int n = 0; level_desc[n] && p + 2 < p_end; n++)
  16198             {
  16199                 if (level_desc[n] == '/')
  16200                     *p++ = '\\';
  16201                 *p++ = level_desc[n];
  16202             }
  16203         }
  16204         *p = '\0';
  16205         SetClipboardText(g.TempBuffer.Data);
  16206     }
  16207 
  16208     // Display decorated stack
  16209     tool->LastActiveFrame = g.FrameCount;
  16210     if (tool->Results.Size > 0 && BeginTable("##table", 3, ImGuiTableFlags_Borders))
  16211     {
  16212         const float id_width = CalcTextSize("0xDDDDDDDD").x;
  16213         TableSetupColumn("Seed", ImGuiTableColumnFlags_WidthFixed, id_width);
  16214         TableSetupColumn("PushID", ImGuiTableColumnFlags_WidthStretch);
  16215         TableSetupColumn("Result", ImGuiTableColumnFlags_WidthFixed, id_width);
  16216         TableHeadersRow();
  16217         for (int n = 0; n < tool->Results.Size; n++)
  16218         {
  16219             ImGuiStackLevelInfo* info = &tool->Results[n];
  16220             TableNextColumn();
  16221             Text("0x%08X", (n > 0) ? tool->Results[n - 1].ID : 0);
  16222             TableNextColumn();
  16223             StackToolFormatLevelInfo(tool, n, true, g.TempBuffer.Data, g.TempBuffer.Size);
  16224             TextUnformatted(g.TempBuffer.Data);
  16225             TableNextColumn();
  16226             Text("0x%08X", info->ID);
  16227             if (n == tool->Results.Size - 1)
  16228                 TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_Header));
  16229         }
  16230         EndTable();
  16231     }
  16232     End();
  16233 }
  16234 
  16235 #else
  16236 
  16237 void ImGui::ShowMetricsWindow(bool*) {}
  16238 void ImGui::ShowFontAtlas(ImFontAtlas*) {}
  16239 void ImGui::DebugNodeColumns(ImGuiOldColumns*) {}
  16240 void ImGui::DebugNodeDrawList(ImGuiWindow*, ImGuiViewportP*, const ImDrawList*, const char*) {}
  16241 void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList*, const ImDrawList*, const ImDrawCmd*, bool, bool) {}
  16242 void ImGui::DebugNodeFont(ImFont*) {}
  16243 void ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {}
  16244 void ImGui::DebugNodeTabBar(ImGuiTabBar*, const char*) {}
  16245 void ImGui::DebugNodeWindow(ImGuiWindow*, const char*) {}
  16246 void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {}
  16247 void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>*, const char*) {}
  16248 void ImGui::DebugNodeViewport(ImGuiViewportP*) {}
  16249 
  16250 void ImGui::ShowDebugLogWindow(bool*) {}
  16251 void ImGui::ShowIDStackToolWindow(bool*) {}
  16252 void ImGui::DebugStartItemPicker() {}
  16253 void ImGui::DebugHookIdInfo(ImGuiID, ImGuiDataType, const void*, const void*) {}
  16254 
  16255 #endif // #ifndef IMGUI_DISABLE_DEBUG_TOOLS
  16256 
  16257 //-----------------------------------------------------------------------------
  16258 
  16259 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
  16260 // 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.
  16261 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
  16262 #include "imgui_user.inl"
  16263 #endif
  16264 
  16265 //-----------------------------------------------------------------------------
  16266 
  16267 #endif // #ifndef IMGUI_DISABLE