medfall

A super great game engine
Log | Files | Refs

imgui.cc (436179B)


      1 // dear imgui, v1.50
      2 // (main code and documentation)
      3 
      4 // See ImGui::ShowTestWindow() in imgui_demo.cpp for demo code.
      5 // Newcomers, read 'Programmer guide' below for notes on how to setup ImGui in your codebase.
      6 // Get latest version at https://github.com/ocornut/imgui
      7 // Releases change-log at https://github.com/ocornut/imgui/releases
      8 // Gallery (please post your screenshots/video there!): https://github.com/ocornut/imgui/issues/772
      9 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
     10 // This library is free but I need your support to sustain development and maintenance.
     11 // If you work for a company, please consider financial support, e.g: https://www.patreon.com/imgui
     12 
     13 /*
     14 
     15  Index
     16  - MISSION STATEMENT
     17  - END-USER GUIDE
     18  - PROGRAMMER GUIDE (read me!)
     19  - API BREAKING CHANGES (read me when you update!)
     20  - FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
     21    - How can I help?
     22    - How do I update to a newer version of ImGui?
     23    - What is ImTextureID and how do I display an image?
     24    - I integrated ImGui in my engine and the text or lines are blurry..
     25    - I integrated ImGui in my engine and some elements are clipping or disappearing when I move windows around..
     26    - How can I have multiple widgets with the same label? Can I have widget without a label? (Yes). A primer on the purpose of labels/IDs.
     27    - How can I tell when ImGui wants my mouse/keyboard inputs and when I can pass them to my application?
     28    - How can I load a different font than the default?
     29    - How can I easily use icons in my application?
     30    - How can I load multiple fonts?
     31    - How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic?
     32    - How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
     33  - ISSUES & TODO-LIST
     34  - CODE
     35 
     36 
     37  MISSION STATEMENT
     38  =================
     39 
     40  - easy to use to create code-driven and data-driven tools
     41  - easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools
     42  - easy to hack and improve
     43  - minimize screen real-estate usage
     44  - minimize setup and maintenance
     45  - minimize state storage on user side
     46  - portable, minimize dependencies, run on target (consoles, phones, etc.)
     47  - efficient runtime (NB- we do allocate when "growing" content - creating a window / opening a tree node for the first time, etc. - but a typical frame won't allocate anything)
     48  - read about immediate-mode gui principles @ http://mollyrocket.com/861, http://mollyrocket.com/forums/index.html
     49 
     50  Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
     51  - doesn't look fancy, doesn't animate
     52  - limited layout features, intricate layouts are typically crafted in code
     53  - occasionally uses statically sized buffers for string manipulations - won't crash, but some very long pieces of text may be clipped. functions like ImGui::TextUnformatted() don't have such restriction.
     54 
     55 
     56  END-USER GUIDE
     57  ==============
     58 
     59  - double-click title bar to collapse window
     60  - click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin()
     61  - click and drag on lower right corner to resize window
     62  - click and drag on any empty space to move window
     63  - double-click/double-tap on lower right corner grip to auto-fit to content
     64  - TAB/SHIFT+TAB to cycle through keyboard editable fields
     65  - use mouse wheel to scroll
     66  - use CTRL+mouse wheel to zoom window contents (if IO.FontAllowScaling is true)
     67  - CTRL+Click on a slider or drag box to input value as text
     68  - text editor:
     69    - Hold SHIFT or use mouse to select text.
     70    - CTRL+Left/Right to word jump
     71    - CTRL+Shift+Left/Right to select words
     72    - CTRL+A our Double-Click to select all
     73    - CTRL+X,CTRL+C,CTRL+V to use OS clipboard
     74    - CTRL+Z,CTRL+Y to undo/redo
     75    - ESCAPE to revert text to its original value
     76    - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
     77 
     78 
     79  PROGRAMMER GUIDE
     80  ================
     81 
     82  - read the FAQ below this section!
     83  - your code creates the UI, if your code doesn't run the UI is gone! == very dynamic UI, no construction/destructions steps, less data retention on your side, no state duplication, less sync, less bugs.
     84  - call and read ImGui::ShowTestWindow() for demo code demonstrating most features.
     85  - see examples/ folder for standalone sample applications. Prefer reading examples/opengl2_example/ first as it is the simplest.
     86    you may be able to grab and copy a ready made imgui_impl_*** file from the examples/.
     87  - customization: PushStyleColor()/PushStyleVar() or the style editor to tweak the look of the interface (e.g. if you want a more compact UI or a different color scheme).
     88 
     89  - getting started:
     90    - init: call ImGui::GetIO() to retrieve the ImGuiIO structure and fill the fields marked 'Settings'.
     91    - init: call io.Fonts->GetTexDataAsRGBA32(...) and load the font texture pixels into graphics memory.
     92    - every frame:
     93       1/ in your mainloop or right after you got your keyboard/mouse info, call ImGui::GetIO() and fill the fields marked 'Input'
     94       2/ call ImGui::NewFrame() as early as you can!
     95       3/ use any ImGui function you want between NewFrame() and Render()
     96       4/ call ImGui::Render() as late as you can to end the frame and finalize render data. it will call your RenderDrawListFn handler that you set in the IO structure.
     97          (if you don't need to render, you still need to call Render() and ignore the callback, or call EndFrame() instead. if you call neither some aspects of windows focusing/moving will appear broken.)
     98    - all rendering information are stored into command-lists until ImGui::Render() is called.
     99    - ImGui never touches or know about your GPU state. the only function that knows about GPU is the RenderDrawListFn handler that you provide.
    100    - effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" phases of your own application.
    101    - refer to the examples applications in the examples/ folder for instruction on how to setup your code.
    102    - a typical application skeleton may be:
    103 
    104         // Application init
    105         ImGuiIO& io = ImGui::GetIO();
    106         io.DisplaySize.x = 1920.0f;
    107         io.DisplaySize.y = 1280.0f;
    108         io.IniFilename = "imgui.ini";
    109         io.RenderDrawListsFn = my_render_function;  // Setup a render function, or set to NULL and call GetDrawData() after Render() to access the render data.
    110         // TODO: Fill others settings of the io structure
    111 
    112         // Load texture atlas
    113         // There is a default font so you don't need to care about choosing a font yet
    114         unsigned char* pixels;
    115         int width, height;
    116         io.Fonts->GetTexDataAsRGBA32(pixels, &width, &height);
    117         // TODO: At this points you've got a texture pointed to by 'pixels' and you need to upload that your your graphic system
    118         // TODO: Store your texture pointer/identifier (whatever your engine uses) in 'io.Fonts->TexID'
    119 
    120         // Application main loop
    121         while (true)
    122         {
    123             // 1) get low-level inputs (e.g. on Win32, GetKeyboardState(), or poll your events, etc.)
    124             // TODO: fill all fields of IO structure and call NewFrame
    125             ImGuiIO& io = ImGui::GetIO();
    126             io.DeltaTime = 1.0f/60.0f;
    127             io.MousePos = mouse_pos;
    128             io.MouseDown[0] = mouse_button_0;
    129             io.MouseDown[1] = mouse_button_1;
    130             io.KeysDown[i] = ...
    131 
    132             // 2) call NewFrame(), after this point you can use ImGui::* functions anytime
    133             ImGui::NewFrame();
    134 
    135             // 3) most of your application code here
    136             MyGameUpdate(); // may use any ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
    137             MyGameRender(); // may use any ImGui functions
    138 
    139             // 4) render & swap video buffers
    140             ImGui::Render();
    141             SwapBuffers();
    142         }
    143 
    144    - You can read back 'io.WantCaptureMouse', 'io.WantCaptureKeybord' etc. flags from the IO structure to tell how ImGui intends to use your
    145      inputs and to know if you should share them or hide them from the rest of your application. Read the FAQ below for more information.
    146 
    147 
    148  API BREAKING CHANGES
    149  ====================
    150 
    151  Occasionally introducing changes that are breaking the API. The breakage are generally minor and easy to fix.
    152  Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code.
    153  Also read releases logs https://github.com/ocornut/imgui/releases for more details.
    154 
    155  - 2017/05/26 (1.50) - Removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
    156  - 2017/05/01 (1.50) - Renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
    157  - 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().
    158  - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
    159  - 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.
    160  - 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.
    161  - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore. 
    162                        If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you. 
    163                        However if your TitleBg/TitleBgActive alpha was <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
    164                        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.
    165                            ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col)
    166                            {
    167                                float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a;
    168                                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);
    169                            }
    170                        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.
    171  - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
    172  - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
    173  - 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).
    174  - 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDraw::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.
    175  - 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).
    176  - 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)
    177  - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
    178  - 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.
    179  - 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.
    180  - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
    181  - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
    182  - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
    183                        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.
    184                        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!
    185  - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
    186  - 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.
    187  - 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
    188  - 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.
    189                        you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
    190  - 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.
    191                        this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
    192                      - if you are using a vanilla copy of one of the imgui_impl_XXXX.cpp provided in the example, you just need to update your copy and you can ignore the rest.
    193                      - the signature of the io.RenderDrawListsFn handler has changed!
    194                             ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
    195                        became:
    196                             ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
    197                               argument   'cmd_lists'        -> 'draw_data->CmdLists'
    198                               argument   'cmd_lists_count'  -> 'draw_data->CmdListsCount'
    199                               ImDrawList 'commands'         -> 'CmdBuffer'
    200                               ImDrawList 'vtx_buffer'       -> 'VtxBuffer'
    201                               ImDrawList  n/a               -> 'IdxBuffer' (new)
    202                               ImDrawCmd  'vtx_count'        -> 'ElemCount'
    203                               ImDrawCmd  'clip_rect'        -> 'ClipRect'
    204                               ImDrawCmd  'user_callback'    -> 'UserCallback'
    205                               ImDrawCmd  'texture_id'       -> 'TextureId'
    206                      - 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.
    207                      - 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!
    208                      - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
    209  - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
    210  - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
    211  - 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.
    212  - 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
    213  - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely be used. Sorry!
    214  - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
    215  - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
    216  - 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.
    217  - 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.
    218  - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
    219  - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
    220  - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
    221  - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
    222  - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
    223  - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
    224  - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
    225  - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
    226  - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
    227  - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
    228  - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
    229  - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
    230  - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
    231  - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
    232  - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
    233  - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
    234  - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
    235               (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
    236                        this sequence:
    237                            const void* png_data;
    238                            unsigned int png_size;
    239                            ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size);
    240                            // <Copy to GPU>
    241                        became:
    242                            unsigned char* pixels;
    243                            int width, height;
    244                            io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
    245                            // <Copy to GPU>
    246                            io.Fonts->TexID = (your_texture_identifier);
    247                        you now have much more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs.
    248                        it is now recommended that you sample the font texture with bilinear interpolation.
    249               (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
    250               (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
    251               (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
    252  - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
    253  - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
    254  - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
    255  - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
    256  - 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)
    257  - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
    258  - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
    259  - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
    260  - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
    261  - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
    262  - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
    263 
    264 
    265  FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
    266  ======================================
    267 
    268  Q: How can I help?
    269  A: - If you are experienced enough with ImGui and with C/C++, look at the todo list and see how you want/can help!
    270     - Become a Patron/donate. Convince your company to become a Patron or provide serious funding for development time.
    271 
    272  Q: How do I update to a newer version of ImGui?
    273  A: Overwrite the following files:
    274       imgui.cpp
    275       imgui.h
    276       imgui_demo.cpp
    277       imgui_draw.cpp
    278       imgui_internal.h
    279       stb_rect_pack.h
    280       stb_textedit.h
    281       stb_truetype.h
    282     Don't overwrite imconfig.h if you have made modification to your copy.
    283     If you have a problem with a missing function/symbols, search for its name in the code, there will likely be a comment about it. 
    284     Check the "API BREAKING CHANGES" sections for a list of occasional API breaking changes. 
    285     Please report any issue to the GitHub page!
    286 
    287  Q: What is ImTextureID and how do I display an image?
    288  A: ImTextureID is a void* used to pass renderer-agnostic texture references around until it hits your render function.
    289     ImGui knows nothing about what those bits represent, it just passes them around. It is up to you to decide what you want the void* to carry!
    290     It could be an identifier to your OpenGL texture (cast GLuint to void*), a pointer to your custom engine material (cast MyMaterial* to void*), etc.
    291     At the end of the chain, your renderer takes this void* to cast it back into whatever it needs to select a current texture to render.
    292     Refer to examples applications, where each renderer (in a imgui_impl_xxxx.cpp file) is treating ImTextureID as a different thing.
    293     (c++ tip: OpenGL uses integers to identify textures. You can safely store an integer into a void*, just cast it to void*, don't take it's address!)
    294     To display a custom image/texture within an ImGui window, you may use ImGui::Image(), ImGui::ImageButton(), ImDrawList::AddImage() functions.
    295     ImGui will generate the geometry and draw calls using the ImTextureID that you passed and which your renderer can use.
    296     It is your responsibility to get textures uploaded to your GPU.
    297 
    298  Q: I integrated ImGui in my engine and the text or lines are blurry..
    299  A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f).
    300     Also make sure your orthographic projection matrix and io.DisplaySize matches your actual framebuffer dimension.
    301 
    302  Q: I integrated ImGui in my engine and some elements are clipping or disappearing when I move windows around..
    303  A: Most likely you are mishandling the clipping rectangles in your render function. Rectangles provided by ImGui are defined as (x1=left,y1=top,x2=right,y2=bottom) and NOT as (x1,y1,width,height).
    304 
    305  Q: Can I have multiple widgets with the same label? Can I have widget without a label? (Yes)
    306  A: Yes. A primer on the use of labels/IDs in ImGui..
    307 
    308    - Elements that are not clickable, such as Text() items don't need an ID.
    309 
    310    - Interactive widgets require state to be carried over multiple frames (most typically ImGui often needs to remember what is the "active" widget).
    311      to do so they need a unique ID. unique ID are typically derived from a string label, an integer index or a pointer.
    312 
    313        Button("OK");        // Label = "OK",     ID = hash of "OK"
    314        Button("Cancel");    // Label = "Cancel", ID = hash of "Cancel"
    315 
    316    - ID are uniquely scoped within windows, tree nodes, etc. so no conflict can happen if you have two buttons called "OK" in two different windows
    317      or in two different locations of a tree.
    318 
    319    - If you have a same ID twice in the same location, you'll have a conflict:
    320 
    321        Button("OK");
    322        Button("OK");           // ID collision! Both buttons will be treated as the same.
    323 
    324      Fear not! this is easy to solve and there are many ways to solve it!
    325 
    326    - When passing a label you can optionally specify extra unique ID information within string itself. This helps solving the simpler collision cases.
    327      use "##" to pass a complement to the ID that won't be visible to the end-user:
    328 
    329        Button("Play");         // Label = "Play",   ID = hash of "Play"
    330        Button("Play##foo1");   // Label = "Play",   ID = hash of "Play##foo1" (different from above)
    331        Button("Play##foo2");   // Label = "Play",   ID = hash of "Play##foo2" (different from above)
    332 
    333    - If you want to completely hide the label, but still need an ID:
    334 
    335        Checkbox("##On", &b);   // Label = "",       ID = hash of "##On" (no label!)
    336 
    337    - Occasionally/rarely you might want change a label while preserving a constant ID. This allows you to animate labels.
    338      For example you may want to include varying information in a window title bar (and windows are uniquely identified by their ID.. obviously)
    339      Use "###" to pass a label that isn't part of ID:
    340 
    341        Button("Hello###ID";   // Label = "Hello",  ID = hash of "ID"
    342        Button("World###ID";   // Label = "World",  ID = hash of "ID" (same as above)
    343 
    344        sprintf(buf, "My game (%f FPS)###MyGame");
    345        Begin(buf);            // Variable label,   ID = hash of "MyGame"
    346 
    347    - Use PushID() / PopID() to create scopes and avoid ID conflicts within the same Window.
    348      This is the most convenient way of distinguishing ID if you are iterating and creating many UI elements.
    349      You can push a pointer, a string or an integer value. Remember that ID are formed from the concatenation of everything in the ID stack!
    350 
    351        for (int i = 0; i < 100; i++)
    352        {
    353          PushID(i);
    354          Button("Click");   // Label = "Click",  ID = hash of integer + "label" (unique)
    355          PopID();
    356        }
    357 
    358        for (int i = 0; i < 100; i++)
    359        {
    360          MyObject* obj = Objects[i];
    361          PushID(obj);
    362          Button("Click");   // Label = "Click",  ID = hash of pointer + "label" (unique)
    363          PopID();
    364        }
    365 
    366        for (int i = 0; i < 100; i++)
    367        {
    368          MyObject* obj = Objects[i];
    369          PushID(obj->Name);
    370          Button("Click");   // Label = "Click",  ID = hash of string + "label" (unique)
    371          PopID();
    372        }
    373 
    374    - More example showing that you can stack multiple prefixes into the ID stack:
    375 
    376        Button("Click");     // Label = "Click",  ID = hash of "Click"
    377        PushID("node");
    378        Button("Click");     // Label = "Click",  ID = hash of "node" + "Click"
    379          PushID(my_ptr);
    380            Button("Click"); // Label = "Click",  ID = hash of "node" + ptr + "Click"
    381          PopID();
    382        PopID();
    383 
    384    - Tree nodes implicitly creates a scope for you by calling PushID().
    385 
    386        Button("Click");     // Label = "Click",  ID = hash of "Click"
    387        if (TreeNode("node"))
    388        {
    389          Button("Click");   // Label = "Click",  ID = hash of "node" + "Click"
    390          TreePop();
    391        }
    392 
    393    - When working with trees, ID are used to preserve the open/close state of each tree node.
    394      Depending on your use cases you may want to use strings, indices or pointers as ID.
    395       e.g. when displaying a single object that may change over time (1-1 relationship), using a static string as ID will preserve your node open/closed state when the targeted object change.
    396       e.g. when displaying a list of objects, using indices or pointers as ID will preserve the node open/closed state differently. experiment and see what makes more sense!
    397 
    398  Q: How can I tell when ImGui wants my mouse/keyboard inputs and when I can pass them to my application?
    399  A: You can read the 'io.WantCaptureXXX' flags in the ImGuiIO structure. Preferably read them after calling ImGui::NewFrame() to avoid those flags lagging by one frame, but either should be fine.
    400     When 'io.WantCaptureMouse' or 'io.WantCaptureKeyboard' flags are set you may want to discard/hide the inputs from the rest of your application.
    401     When 'io.WantInputsCharacters' is set to may want to notify your OS to popup an on-screen keyboard, if available.
    402     ImGui is tracking dragging and widget activity that may occur outside the boundary of a window, so 'io.WantCaptureMouse' is a more accurate and complete than testing for ImGui::IsMouseHoveringAnyWindow().
    403     (Advanced note: text input releases focus on Return 'KeyDown', so the following Return 'KeyUp' event that your application receive will typically have 'io.WantcaptureKeyboard=false'. 
    404      Depending on your application logic it may or not be inconvenient. You might want to track which key-downs were for ImGui (e.g. with an array of bool) and filter out the corresponding key-ups.)
    405 
    406  Q: How can I load a different font than the default? (default is an embedded version of ProggyClean.ttf, rendered at size 13)
    407  A: Use the font atlas to load the TTF file you want:
    408 
    409       ImGuiIO& io = ImGui::GetIO();
    410       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
    411       io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
    412 
    413  Q: How can I easily use icons in my application?
    414  A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you main font. Then you can refer to icons within your strings.
    415     Read 'How can I load multiple fonts?' and the file 'extra_fonts/README.txt' for instructions.
    416 
    417  Q: How can I load multiple fonts?
    418  A: Use the font atlas to pack them into a single texture:
    419     (Read extra_fonts/README.txt and the code in ImFontAtlas for more details.)
    420 
    421       ImGuiIO& io = ImGui::GetIO();
    422       ImFont* font0 = io.Fonts->AddFontDefault();
    423       ImFont* font1 = io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
    424       ImFont* font2 = io.Fonts->AddFontFromFileTTF("myfontfile2.ttf", size_in_pixels);
    425       io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
    426       // the first loaded font gets used by default
    427       // use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime
    428 
    429       // Options
    430       ImFontConfig config;
    431       config.OversampleH = 3;
    432       config.OversampleV = 1;
    433       config.GlyphExtraSpacing.x = 1.0f;
    434       io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, &config);
    435 
    436       // Combine multiple fonts into one (e.g. for icon fonts)
    437       ImWchar ranges[] = { 0xf000, 0xf3ff, 0 };
    438       ImFontConfig config;
    439       config.MergeMode = true;
    440       io.Fonts->AddFontDefault();
    441       io.Fonts->LoadFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); // Merge icon font
    442       io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge japanese glyphs
    443 
    444  Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
    445  A: When loading a font, pass custom Unicode ranges to specify the glyphs to load. 
    446     All your strings needs to use UTF-8 encoding. Specifying literal in your source code using a local code page (such as CP-923 for Japanese or CP-1251 for Cyrillic) will not work.
    447     In C++11 you can encode a string literal in UTF-8 by using the u8"hello" syntax. Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8.
    448     You can also try to remap your local codepage characters to their Unicode codepoint using font->AddRemapChar(), but international users may have problems reading/editing your source code.
    449 
    450       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese());  // Load Japanese characters
    451       io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
    452       io.ImeWindowHandle = MY_HWND;      // To input using Microsoft IME, give ImGui the hwnd of your application
    453 
    454     As for text input, depends on you passing the right character code to io.AddInputCharacter(). The example applications do that.
    455 
    456  Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
    457  A: The easiest way is to create a dummy window. Call Begin() with NoTitleBar|NoResize|NoMove|NoScrollbar|NoSavedSettings|NoInputs flag, zero background alpha, 
    458     then retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like.
    459 
    460  - tip: the construct 'IMGUI_ONCE_UPON_A_FRAME { ... }' will run the block of code only once a frame. You can use it to quickly add custom UI in the middle of a deep nested inner loop in your code.
    461  - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug"
    462  - tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window. this is also useful to set yourself in the context of another window (to get/set other settings)
    463  - tip: you can call Render() multiple times (e.g for VR renders).
    464  - tip: call and read the ShowTestWindow() code in imgui_demo.cpp for more example of how to use ImGui!
    465 
    466 
    467  ISSUES & TODO-LIST
    468  ==================
    469  Issue numbers (#) refer to github issues listed at https://github.com/ocornut/imgui/issues
    470  The list below consist mostly of ideas noted down before they are requested/discussed by users (at which point it usually moves to the github)
    471 
    472  - doc: add a proper documentation+regression testing system (#435)
    473  - window: add a way for very transient windows (non-saved, temporary overlay over hundreds of objects) to "clean" up from the global window list. perhaps a lightweight explicit cleanup pass.
    474  - window: calling SetNextWindowSize() every frame with <= 0 doesn't do anything, may be useful to allow (particularly when used for a single axis) (#690)
    475  - window: auto-fit feedback loop when user relies on any dynamic layout (window width multiplier, column) appears weird to end-user. clarify.
    476  - window: allow resizing of child windows (possibly given min/max for each axis?)
    477  - window: background options for child windows, border option (disable rounding)
    478  - window: add a way to clear an existing window instead of appending (e.g. for tooltip override using a consistent api rather than the deferred tooltip)
    479  - window: resizing from any sides? + mouse cursor directives for app.
    480 !- window: begin with *p_open == false should return false.
    481  - window: get size/pos helpers given names (see discussion in #249)
    482  - window: a collapsed window can be stuck behind the main menu bar?
    483  - window: when window is small, prioritize resize button over close button.
    484  - window: detect extra End() call that pop the "Debug" window out and assert at call site instead of later.
    485  - window/tooltip: allow to set the width of a tooltip to allow TextWrapped() etc. while keeping the height automatic.
    486  - window: increase minimum size of a window with menus or fix the menu rendering so that it doesn't look odd.
    487  - draw-list: maintaining bounding box per command would allow to merge draw command when clipping isn't relied on (typical non-scrolling window or non-overflowing column would merge with previous command).
    488 !- scrolling: allow immediately effective change of scroll if we haven't appended items yet
    489  - splitter/separator: formalize the splitter idiom into an official api (we want to handle n-way split) (#319)
    490  - widgets: display mode: widget-label, label-widget (aligned on column or using fixed size), label-newline-tab-widget etc.
    491  - widgets: clean up widgets internal toward exposing everything.
    492  - widgets: add disabled and read-only modes (#211)
    493  - main: considering adding an Init() function? some constructs are awkward in the implementation because of the lack of them.
    494 !- main: make it so that a frame with no window registered won't refocus every window on subsequent frames (~bump LastFrameActive of all windows).
    495  - main: IsItemHovered() make it more consistent for various type of widgets, widgets with multiple components, etc. also effectively IsHovered() region sometimes differs from hot region, e.g tree nodes
    496  - main: IsItemHovered() info stored in a stack? so that 'if TreeNode() { Text; TreePop; } if IsHovered' return the hover state of the TreeNode?
    497  - input text: clean up the mess caused by converting UTF-8 <> wchar. the code is rather inefficient right now and super fragile.
    498  - input text: reorganize event handling, allow CharFilter to modify buffers, allow multiple events? (#541)
    499  - input text: expose CursorPos in char filter event (#816)
    500  - input text: flag to disable live update of the user buffer (also applies to float/int text input) 
    501  - input text: resize behavior - field could stretch when being edited? hover tooltip shows more text?
    502  - input text: add ImGuiInputTextFlags_EnterToApply? (off #218)
    503  - input text: add discard flag (e.g. ImGuiInputTextFlags_DiscardActiveBuffer) or make it easier to clear active focus for text replacement during edition (#725)
    504  - input text multi-line: don't directly call AddText() which does an unnecessary vertex reserve for character count prior to clipping. and/or more line-based clipping to AddText(). and/or reorganize TextUnformatted/RenderText for more efficiency for large text (e.g TextUnformatted could clip and log separately, etc).
    505  - input text multi-line: way to dynamically grow the buffer without forcing the user to initially allocate for worse case (follow up on #200)
    506  - input text multi-line: line numbers? status bar? (follow up on #200)
    507  - input text multi-line: behave better when user changes input buffer while editing is active (even though it is illegal behavior). namely, the change of buffer can create a scrollbar glitch (#725)
    508  - input text: allow centering/positioning text so that ctrl+clicking Drag or Slider keeps the textual value at the same pixel position.
    509  - input number: optional range min/max for Input*() functions
    510  - input number: holding [-]/[+] buttons could increase the step speed non-linearly (or user-controlled)
    511  - input number: use mouse wheel to step up/down
    512  - input number: applying arithmetics ops (+,-,*,/) messes up with text edit undo stack.
    513  - button: provide a button that looks framed.
    514  - text: proper alignment options
    515  - image/image button: misalignment on padded/bordered button?
    516  - image/image button: parameters are confusing, image() has tint_col,border_col whereas imagebutton() has bg_col/tint_col. Even thou they are different parameters ordering could be more consistent. can we fix that?
    517  - layout: horizontal layout helper (#97)
    518  - layout: horizontal flow until no space left (#404)
    519  - layout: more generic alignment state (left/right/centered) for single items?
    520  - layout: clean up the InputFloatN/SliderFloatN/ColorEdit4 layout code. item width should include frame padding.
    521  - layout: BeginGroup() needs a border option.
    522  - columns: declare column set (each column: fixed size, %, fill, distribute default size among fills) (#513, #125)
    523  - columns: add a conditional parameter to SetColumnOffset() (#513, #125)
    524  - columns: separator function or parameter that works within the column (currently Separator() bypass all columns) (#125)
    525  - columns: columns header to act as button (~sort op) and allow resize/reorder (#513, #125)
    526  - columns: user specify columns size (#513, #125)
    527  - columns: flag to add horizontal separator above/below?
    528  - columns/layout: setup minimum line height (equivalent of automatically calling AlignFirstTextHeightToWidgets)
    529  - combo: sparse combo boxes (via function call?) / iterators
    530  - combo: contents should extends to fit label if combo widget is small
    531  - combo/listbox: keyboard control. need InputText-like non-active focus + key handling. considering keyboard for custom listbox (pr #203)
    532  - listbox: multiple selection
    533  - listbox: user may want to initial scroll to focus on the one selected value?
    534  - listbox: keyboard navigation.
    535  - listbox: scrolling should track modified selection.
    536 !- popups/menus: clarify usage of popups id, how MenuItem/Selectable closing parent popups affects the ID, etc. this is quite fishy needs improvement! (#331, #402)
    537  - popups: add variant using global identifier similar to Begin/End (#402)
    538  - popups: border options. richer api like BeginChild() perhaps? (#197)
    539  - tooltip: tooltip that doesn't fit in entire screen seems to lose their "last preferred button" and may teleport when moving mouse
    540  - menus: local shortcuts, global shortcuts (#456, #126)
    541  - menus: icons
    542  - menus: menubars: some sort of priority / effect of main menu-bar on desktop size?
    543  - menus: calling BeginMenu() twice with a same name doesn't seem to append nicely
    544  - statusbar: add a per-window status bar helper similar to what menubar does.
    545  - tabs (#261, #351)
    546  - separator: separator on the initial position of a window is not visible (cursorpos.y <= clippos.y)
    547 !- color: the color helpers/typing is a mess and needs sorting out.
    548  - color: add a better color picker (#346)
    549  - node/graph editor (#306)
    550  - pie menus patterns (#434)
    551  - drag'n drop, dragging helpers (carry dragging info, visualize drag source before clicking, drop target, etc.) (#143, #479)
    552  - plot: PlotLines() should use the polygon-stroke facilities (currently issues with averaging normals)
    553  - plot: make it easier for user to draw extra stuff into the graph (e.g: draw basis, highlight certain points, 2d plots, multiple plots)
    554  - plot: "smooth" automatic scale over time, user give an input 0.0(full user scale) 1.0(full derived from value)
    555  - plot: add a helper e.g. Plot(char* label, float value, float time_span=2.0f) that stores values and Plot them for you - probably another function name. and/or automatically allow to plot ANY displayed value (more reliance on stable ID)
    556  - slider: allow using the [-]/[+] buttons used by InputFloat()/InputInt()
    557  - slider: initial absolute click is imprecise. change to relative movement slider (same as scrollbar).
    558  - slider: add dragging-based widgets to edit values with mouse (on 2 axises), saving screen real-estate.
    559  - slider: tint background based on value (e.g. v_min -> v_max, or use 0.0f either side of the sign)
    560  - slider & drag: int data passing through a float
    561  - drag float: up/down axis
    562  - drag float: added leeway on edge (e.g. a few invisible steps past the clamp limits)
    563  - tree node / optimization: avoid formatting when clipped.
    564  - tree node: tree-node/header right-most side doesn't take account of horizontal scrolling.
    565  - tree node: add treenode/treepush int variants? not there because (void*) cast from int warns on some platforms/settings?
    566  - tree node: try to apply scrolling at time of TreePop() if node was just opened and end of node is past scrolling limits?
    567  - tree node / selectable render mismatch which is visible if you use them both next to each other (e.g. cf. property viewer)
    568  - tree node: tweak color scheme to distinguish headers from selected tree node (#581)
    569  - textwrapped: figure out better way to use TextWrapped() in an always auto-resize context (tooltip, etc.) (#249)
    570  - settings: write more decent code to allow saving/loading new fields
    571  - settings: api for per-tool simple persistent data (bool,int,float,columns sizes,etc.) in .ini file
    572  - style: add window shadows.
    573  - style/optimization: store rounded corners in texture to use 1 quad per corner (filled and wireframe) to lower the cost of rounding.
    574  - style: color-box not always square?
    575  - style: a concept of "compact style" that the end-user can easily rely on (e.g. PushStyleCompact()?) that maps to other settings? avoid implementing duplicate helpers such as SmallCheckbox(), etc.
    576  - style: try to make PushStyleVar() more robust to incorrect parameters (to be more friendly to edit & continues situation).
    577  - style: global scale setting.
    578  - style: WindowPadding needs to be EVEN needs the 0.5 multiplier probably have a subtle effect on clip rectangle
    579  - text: simple markup language for color change?
    580  - font: dynamic font atlas to avoid baking huge ranges into bitmap and make scaling easier.
    581  - font: small opt: for monospace font (like the defalt one) we can trim IndexXAdvance as long as trailing value is == FallbackXAdvance
    582  - font: add support for kerning, probably optional. perhaps default to (32..128)^2 matrix ~ 36KB then hash fallback.
    583  - font: add a simpler CalcTextSizeA() api? current one ok but not welcome if user needs to call it directly (without going through ImGui::CalcTextSize)
    584  - font: fix AddRemapChar() to work before font has been built.
    585  - log: LogButtons() options for specifying depth and/or hiding depth slider
    586  - log: have more control over the log scope (e.g. stop logging when leaving current tree node scope)
    587  - log: be able to log anything (e.g. right-click on a window/tree-node, shows context menu? log into tty/file/clipboard)
    588  - log: let user copy any window content to clipboard easily (CTRL+C on windows? while moving it? context menu?). code is commented because it fails with multiple Begin/End pairs.
    589  - filters: set a current filter that tree node can automatically query to hide themselves
    590  - filters: handle wildcards (with implicit leading/trailing *), regexps
    591  - shortcuts: add a shortcut api, e.g. parse "&Save" and/or "Save (CTRL+S)", pass in to widgets or provide simple ways to use (button=activate, input=focus)
    592 !- keyboard: tooltip & combo boxes are messing up / not honoring keyboard tabbing
    593  - keyboard: full keyboard navigation and focus. (#323)
    594  - focus: preserve ActiveId/focus stack state, e.g. when opening a menu and close it, previously selected InputText() focus gets restored (#622)
    595  - focus: SetKeyboardFocusHere() on with >= 0 offset could be done on same frame (else latch and modulate on beginning of next frame)
    596  - input: rework IO system to be able to pass actual ordered/timestamped events. (~#335, #71)
    597  - input: allow to decide and pass explicit double-clicks (e.g. for windows by the CS_DBLCLKS style).
    598  - input: support track pad style scrolling & slider edit.
    599  - misc: provide a way to compile out the entire implementation while providing a dummy API (e.g. #define IMGUI_DUMMY_IMPL)
    600  - misc: double-clicking on title bar to minimize isn't consistent, perhaps move to single-click on left-most collapse icon?
    601  - misc: provide HoveredTime and ActivatedTime to ease the creation of animations.
    602  - style editor: have a more global HSV setter (e.g. alter hue on all elements). consider replacing active/hovered by offset in HSV space? (#438)
    603  - style editor: color child window height expressed in multiple of line height.
    604  - remote: make a system like RemoteImGui first-class citizen/project (#75)
    605  - drawlist: move Font, FontSize, FontTexUvWhitePixel inside ImDrawList and make it self-contained (apart from drawing settings?)
    606  - drawlist: end-user probably can't call Clear() directly because we expect a texture to be pushed in the stack.
    607  - examples: directx9: save/restore device state more thoroughly.
    608  - examples: window minimize, maximize (#583)
    609  - optimization: add a flag to disable most of rendering, for the case where the user expect to skip it (#335)
    610  - optimization: use another hash function than crc32, e.g. FNV1a
    611  - optimization/render: merge command-lists with same clip-rect into one even if they aren't sequential? (as long as in-between clip rectangle don't overlap)?
    612  - optimization: turn some the various stack vectors into statically-sized arrays
    613  - optimization: better clipping for multi-component widgets
    614 */
    615 
    616 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
    617 #define _CRT_SECURE_NO_WARNINGS
    618 #endif
    619 
    620 #include "imgui.h"
    621 #define IMGUI_DEFINE_MATH_OPERATORS
    622 #define IMGUI_DEFINE_PLACEMENT_NEW
    623 #include "imgui_internal.h"
    624 
    625 #include <ctype.h>      // toupper, isprint
    626 #include <stdlib.h>     // NULL, malloc, free, qsort, atoi
    627 #include <stdio.h>      // vsnprintf, sscanf, printf
    628 #include <limits.h>     // INT_MIN, INT_MAX
    629 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
    630 #include <stddef.h>     // intptr_t
    631 #else
    632 #include <stdint.h>     // intptr_t
    633 #endif
    634 
    635 #ifdef _MSC_VER
    636 #pragma warning (disable: 4127) // condition expression is constant
    637 #pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
    638 #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
    639 #endif
    640 
    641 // Clang warnings with -Weverything
    642 #ifdef __clang__
    643 #pragma clang diagnostic ignored "-Wold-style-cast"         // warning : use of old-style cast                              // yes, they are more terse.
    644 #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.
    645 #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.
    646 #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.
    647 #pragma clang diagnostic ignored "-Wglobal-constructors"    // warning : declaration requires a global destructor           // similar to above, not sure what the exact difference it.
    648 #pragma clang diagnostic ignored "-Wsign-conversion"        // warning : implicit conversion changes signedness             //
    649 #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. 
    650 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int' //
    651 #elif defined(__GNUC__)
    652 #pragma GCC diagnostic ignored "-Wunused-function"          // warning: 'xxxx' defined but not used
    653 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"      // warning: cast to pointer from integer of different size
    654 #pragma GCC diagnostic ignored "-Wformat"                   // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
    655 #pragma GCC diagnostic ignored "-Wdouble-promotion"         // warning: implicit conversion from 'float' to 'double' when passing argument to function
    656 #pragma GCC diagnostic ignored "-Wconversion"               // warning: conversion to 'xxxx' from 'xxxx' may alter its value
    657 #pragma GCC diagnostic ignored "-Wcast-qual"                // warning: cast from type 'xxxx' to type 'xxxx' casts away qualifiers
    658 #endif
    659 
    660 //-------------------------------------------------------------------------
    661 // Forward Declarations
    662 //-------------------------------------------------------------------------
    663 
    664 static void             LogRenderedText(const ImVec2& ref_pos, const char* text, const char* text_end = NULL);
    665 
    666 static void             PushMultiItemsWidths(int components, float w_full = 0.0f);
    667 static float            GetDraggedColumnOffset(int column_index);
    668 
    669 static bool             IsKeyPressedMap(ImGuiKey key, bool repeat = true);
    670 
    671 static ImFont*          GetDefaultFont();
    672 static void             SetCurrentFont(ImFont* font);
    673 static void             SetCurrentWindow(ImGuiWindow* window);
    674 static void             SetWindowScrollY(ImGuiWindow* window, float new_scroll_y);
    675 static void             SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiSetCond cond);
    676 static void             SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiSetCond cond);
    677 static void             SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiSetCond cond);
    678 static ImGuiWindow*     FindHoveredWindow(ImVec2 pos, bool excluding_childs);
    679 static ImGuiWindow*     CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
    680 static inline bool      IsWindowContentHoverable(ImGuiWindow* window);
    681 static void             ClearSetNextWindowData();
    682 static void             CheckStacksSize(ImGuiWindow* window, bool write);
    683 static void             Scrollbar(ImGuiWindow* window, bool horizontal);
    684 
    685 static void             AddDrawListToRenderList(ImVector<ImDrawList*>& out_render_list, ImDrawList* draw_list);
    686 static void             AddWindowToRenderList(ImVector<ImDrawList*>& out_render_list, ImGuiWindow* window);
    687 static void             AddWindowToSortedBuffer(ImVector<ImGuiWindow*>& out_sorted_windows, ImGuiWindow* window);
    688 
    689 static ImGuiIniData*    FindWindowSettings(const char* name);
    690 static ImGuiIniData*    AddWindowSettings(const char* name);
    691 static void             LoadIniSettingsFromDisk(const char* ini_filename);
    692 static void             SaveIniSettingsToDisk(const char* ini_filename);
    693 static void             MarkIniSettingsDirty();
    694 
    695 static void             PushColumnClipRect(int column_index = -1);
    696 static ImRect           GetVisibleRect();
    697 
    698 static bool             BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_flags);
    699 static void             CloseInactivePopups();
    700 static void             ClosePopupToLevel(int remaining);
    701 static void             ClosePopup(ImGuiID id);
    702 static bool             IsPopupOpen(ImGuiID id);
    703 static ImGuiWindow*     GetFrontMostModalRootWindow();
    704 static ImVec2           FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& rect_to_avoid);
    705 
    706 static bool             InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data);
    707 static int              InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end);
    708 static ImVec2           InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false);
    709 
    710 static inline void      DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size);
    711 static inline void      DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size);
    712 static void             DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2);
    713 static bool             DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format);
    714 
    715 //-----------------------------------------------------------------------------
    716 // Platform dependent default implementations
    717 //-----------------------------------------------------------------------------
    718 
    719 static const char*      GetClipboardTextFn_DefaultImpl(void* user_data);
    720 static void             SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
    721 static void             ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
    722 
    723 //-----------------------------------------------------------------------------
    724 // Context
    725 //-----------------------------------------------------------------------------
    726 
    727 // Default font atlas storage .
    728 // New contexts always point by default to this font atlas. It can be changed by reassigning the GetIO().Fonts variable.
    729 static ImFontAtlas      GImDefaultFontAtlas;
    730 
    731 // Default context storage + current context pointer.
    732 // Implicitely used by all ImGui functions. Always assumed to be != NULL. Change to a different context by calling ImGui::SetCurrentContext()
    733 // ImGui is currently not thread-safe because of this variable. If you want thread-safety to allow N threads to access N different contexts, you might work around it by:
    734 // - Having multiple instances of the ImGui code compiled inside different namespace (easiest/safest, if you have a finite number of contexts)
    735 // - or: Changing this variable to be TLS. You may #define GImGui in imconfig.h for further custom hackery. Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
    736 #ifndef GImGui
    737 static ImGuiContext     GImDefaultContext;
    738 ImGuiContext*           GImGui = &GImDefaultContext;
    739 #endif
    740 
    741 //-----------------------------------------------------------------------------
    742 // User facing structures
    743 //-----------------------------------------------------------------------------
    744 
    745 ImGuiStyle::ImGuiStyle()
    746 {
    747     Alpha                   = 1.0f;             // Global alpha applies to everything in ImGui
    748     WindowPadding           = ImVec2(8,8);      // Padding within a window
    749     WindowMinSize           = ImVec2(32,32);    // Minimum window size
    750     WindowRounding          = 9.0f;             // Radius of window corners rounding. Set to 0.0f to have rectangular windows
    751     WindowTitleAlign        = ImVec2(0.0f,0.5f);// Alignment for title bar text
    752     ChildWindowRounding     = 0.0f;             // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
    753     FramePadding            = ImVec2(4,3);      // Padding within a framed rectangle (used by most widgets)
    754     FrameRounding           = 0.0f;             // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
    755     ItemSpacing             = ImVec2(8,4);      // Horizontal and vertical spacing between widgets/lines
    756     ItemInnerSpacing        = ImVec2(4,4);      // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
    757     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!
    758     IndentSpacing           = 21.0f;            // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
    759     ColumnsMinSpacing       = 6.0f;             // Minimum horizontal spacing between two columns
    760     ScrollbarSize           = 16.0f;            // Width of the vertical scrollbar, Height of the horizontal scrollbar
    761     ScrollbarRounding       = 9.0f;             // Radius of grab corners rounding for scrollbar
    762     GrabMinSize             = 10.0f;            // Minimum width/height of a grab box for slider/scrollbar
    763     GrabRounding            = 0.0f;             // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
    764     ButtonTextAlign         = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
    765     DisplayWindowPadding    = ImVec2(22,22);    // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows.
    766     DisplaySafeAreaPadding  = ImVec2(4,4);      // 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.
    767     AntiAliasedLines        = true;             // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
    768     AntiAliasedShapes       = true;             // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
    769     CurveTessellationTol    = 1.25f;            // Tessellation tolerance. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
    770 
    771     Colors[ImGuiCol_Text]                   = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
    772     Colors[ImGuiCol_TextDisabled]           = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
    773     Colors[ImGuiCol_WindowBg]               = ImVec4(0.00f, 0.00f, 0.00f, 0.70f);
    774     Colors[ImGuiCol_ChildWindowBg]          = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
    775     Colors[ImGuiCol_PopupBg]                = ImVec4(0.05f, 0.05f, 0.10f, 0.90f);
    776     Colors[ImGuiCol_Border]                 = ImVec4(0.70f, 0.70f, 0.70f, 0.65f);
    777     Colors[ImGuiCol_BorderShadow]           = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
    778     Colors[ImGuiCol_FrameBg]                = ImVec4(0.80f, 0.80f, 0.80f, 0.30f);   // Background of checkbox, radio button, plot, slider, text input
    779     Colors[ImGuiCol_FrameBgHovered]         = ImVec4(0.90f, 0.80f, 0.80f, 0.40f);
    780     Colors[ImGuiCol_FrameBgActive]          = ImVec4(0.90f, 0.65f, 0.65f, 0.45f);
    781     Colors[ImGuiCol_TitleBg]                = ImVec4(0.27f, 0.27f, 0.54f, 0.83f);
    782     Colors[ImGuiCol_TitleBgCollapsed]       = ImVec4(0.40f, 0.40f, 0.80f, 0.20f);
    783     Colors[ImGuiCol_TitleBgActive]          = ImVec4(0.32f, 0.32f, 0.63f, 0.87f);
    784     Colors[ImGuiCol_MenuBarBg]              = ImVec4(0.40f, 0.40f, 0.55f, 0.80f);
    785     Colors[ImGuiCol_ScrollbarBg]            = ImVec4(0.20f, 0.25f, 0.30f, 0.60f);
    786     Colors[ImGuiCol_ScrollbarGrab]          = ImVec4(0.40f, 0.40f, 0.80f, 0.30f);
    787     Colors[ImGuiCol_ScrollbarGrabHovered]   = ImVec4(0.40f, 0.40f, 0.80f, 0.40f);
    788     Colors[ImGuiCol_ScrollbarGrabActive]    = ImVec4(0.80f, 0.50f, 0.50f, 0.40f);
    789     Colors[ImGuiCol_ComboBg]                = ImVec4(0.20f, 0.20f, 0.20f, 0.99f);
    790     Colors[ImGuiCol_CheckMark]              = ImVec4(0.90f, 0.90f, 0.90f, 0.50f);
    791     Colors[ImGuiCol_SliderGrab]             = ImVec4(1.00f, 1.00f, 1.00f, 0.30f);
    792     Colors[ImGuiCol_SliderGrabActive]       = ImVec4(0.80f, 0.50f, 0.50f, 1.00f);
    793     Colors[ImGuiCol_Button]                 = ImVec4(0.67f, 0.40f, 0.40f, 0.60f);
    794     Colors[ImGuiCol_ButtonHovered]          = ImVec4(0.67f, 0.40f, 0.40f, 1.00f);
    795     Colors[ImGuiCol_ButtonActive]           = ImVec4(0.80f, 0.50f, 0.50f, 1.00f);
    796     Colors[ImGuiCol_Header]                 = ImVec4(0.40f, 0.40f, 0.90f, 0.45f);
    797     Colors[ImGuiCol_HeaderHovered]          = ImVec4(0.45f, 0.45f, 0.90f, 0.80f);
    798     Colors[ImGuiCol_HeaderActive]           = ImVec4(0.53f, 0.53f, 0.87f, 0.80f);
    799     Colors[ImGuiCol_Column]                 = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
    800     Colors[ImGuiCol_ColumnHovered]          = ImVec4(0.70f, 0.60f, 0.60f, 1.00f);
    801     Colors[ImGuiCol_ColumnActive]           = ImVec4(0.90f, 0.70f, 0.70f, 1.00f);
    802     Colors[ImGuiCol_ResizeGrip]             = ImVec4(1.00f, 1.00f, 1.00f, 0.30f);
    803     Colors[ImGuiCol_ResizeGripHovered]      = ImVec4(1.00f, 1.00f, 1.00f, 0.60f);
    804     Colors[ImGuiCol_ResizeGripActive]       = ImVec4(1.00f, 1.00f, 1.00f, 0.90f);
    805     Colors[ImGuiCol_CloseButton]            = ImVec4(0.50f, 0.50f, 0.90f, 0.50f);
    806     Colors[ImGuiCol_CloseButtonHovered]     = ImVec4(0.70f, 0.70f, 0.90f, 0.60f);
    807     Colors[ImGuiCol_CloseButtonActive]      = ImVec4(0.70f, 0.70f, 0.70f, 1.00f);
    808     Colors[ImGuiCol_PlotLines]              = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
    809     Colors[ImGuiCol_PlotLinesHovered]       = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
    810     Colors[ImGuiCol_PlotHistogram]          = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
    811     Colors[ImGuiCol_PlotHistogramHovered]   = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
    812     Colors[ImGuiCol_TextSelectedBg]         = ImVec4(0.00f, 0.00f, 1.00f, 0.35f);
    813     Colors[ImGuiCol_ModalWindowDarkening]   = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
    814 }
    815 
    816 ImGuiIO::ImGuiIO()
    817 {
    818     // Most fields are initialized with zero
    819     memset(this, 0, sizeof(*this));
    820 
    821     DisplaySize = ImVec2(-1.0f, -1.0f);
    822     DeltaTime = 1.0f/60.0f;
    823     IniSavingRate = 5.0f;
    824     IniFilename = "imgui.ini";
    825     LogFilename = "imgui_log.txt";
    826     Fonts = &GImDefaultFontAtlas;
    827     FontGlobalScale = 1.0f;
    828     FontDefault = NULL;
    829     DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
    830     MousePos = ImVec2(-1,-1);
    831     MousePosPrev = ImVec2(-1,-1);
    832     MouseDoubleClickTime = 0.30f;
    833     MouseDoubleClickMaxDist = 6.0f;
    834     MouseDragThreshold = 6.0f;
    835     for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++)
    836         MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
    837     for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++)
    838         KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f;
    839     for (int i = 0; i < ImGuiKey_COUNT; i++)
    840         KeyMap[i] = -1;
    841     KeyRepeatDelay = 0.250f;
    842     KeyRepeatRate = 0.050f;
    843     UserData = NULL;
    844 
    845     // User functions
    846     RenderDrawListsFn = NULL;
    847     MemAllocFn = malloc;
    848     MemFreeFn = free;
    849     GetClipboardTextFn = GetClipboardTextFn_DefaultImpl;   // Platform dependent default implementations
    850     SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
    851     ClipboardUserData = NULL;
    852     ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
    853 
    854     // Set OS X style defaults based on __APPLE__ compile time flag
    855 #ifdef __APPLE__
    856     OSXBehaviors = true;
    857 #endif
    858 }
    859 
    860 // Pass in translated ASCII characters for text input.
    861 // - with glfw you can get those from the callback set in glfwSetCharCallback()
    862 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
    863 void ImGuiIO::AddInputCharacter(ImWchar c)
    864 {
    865     const int n = ImStrlenW(InputCharacters);
    866     if (n + 1 < IM_ARRAYSIZE(InputCharacters))
    867     {
    868         InputCharacters[n] = c;
    869         InputCharacters[n+1] = '\0';
    870     }
    871 }
    872 
    873 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
    874 {
    875     // We can't pass more wchars than ImGuiIO::InputCharacters[] can hold so don't convert more
    876     const int wchars_buf_len = sizeof(ImGuiIO::InputCharacters) / sizeof(ImWchar);
    877     ImWchar wchars[wchars_buf_len];
    878     ImTextStrFromUtf8(wchars, wchars_buf_len, utf8_chars, NULL);
    879     for (int i = 0; i < wchars_buf_len && wchars[i] != 0; i++)
    880         AddInputCharacter(wchars[i]);
    881 }
    882 
    883 //-----------------------------------------------------------------------------
    884 // HELPERS
    885 //-----------------------------------------------------------------------------
    886 
    887 #define IM_F32_TO_INT8_UNBOUND(_VAL)    ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f)))   // Unsaturated, for display purpose 
    888 #define IM_F32_TO_INT8_SAT(_VAL)        ((int)(ImSaturate(_VAL) * 255.0f + 0.5f))               // Saturated, always output 0..255
    889 
    890 // Play it nice with Windows users. Notepad in 2015 still doesn't display text data with Unix-style \n.
    891 #ifdef _WIN32
    892 #define IM_NEWLINE "\r\n"
    893 #else
    894 #define IM_NEWLINE "\n"
    895 #endif
    896 
    897 bool ImIsPointInTriangle(const ImVec2& p, const ImVec2& a, const ImVec2& b, const ImVec2& c)
    898 {
    899     bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
    900     bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
    901     bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
    902     return ((b1 == b2) && (b2 == b3));
    903 }
    904 
    905 int ImStricmp(const char* str1, const char* str2)
    906 {
    907     int d;
    908     while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
    909     return d;
    910 }
    911 
    912 int ImStrnicmp(const char* str1, const char* str2, int count)
    913 {
    914     int d = 0;
    915     while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
    916     return d;
    917 }
    918 
    919 void ImStrncpy(char* dst, const char* src, int count)
    920 {
    921     if (count < 1) return;
    922     strncpy(dst, src, (size_t)count);
    923     dst[count-1] = 0;
    924 }
    925 
    926 char* ImStrdup(const char *str)
    927 {
    928     size_t len = strlen(str) + 1;
    929     void* buff = ImGui::MemAlloc(len);
    930     return (char*)memcpy(buff, (const void*)str, len);
    931 }
    932 
    933 int ImStrlenW(const ImWchar* str)
    934 {
    935     int n = 0;
    936     while (*str++) n++;
    937     return n;
    938 }
    939 
    940 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
    941 {
    942     while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
    943         buf_mid_line--;
    944     return buf_mid_line;
    945 }
    946 
    947 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
    948 {
    949     if (!needle_end)
    950         needle_end = needle + strlen(needle);
    951 
    952     const char un0 = (char)toupper(*needle);
    953     while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
    954     {
    955         if (toupper(*haystack) == un0)
    956         {
    957             const char* b = needle + 1;
    958             for (const char* a = haystack + 1; b < needle_end; a++, b++)
    959                 if (toupper(*a) != toupper(*b))
    960                     break;
    961             if (b == needle_end)
    962                 return haystack;
    963         }
    964         haystack++;
    965     }
    966     return NULL;
    967 }
    968 
    969 
    970 // MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size). 
    971 // 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.
    972 int ImFormatString(char* buf, int buf_size, const char* fmt, ...)
    973 {
    974     IM_ASSERT(buf_size > 0);
    975     va_list args;
    976     va_start(args, fmt);
    977     int w = vsnprintf(buf, buf_size, fmt, args);
    978     va_end(args);
    979     if (w == -1 || w >= buf_size)
    980         w = buf_size - 1;
    981     buf[w] = 0;
    982     return w;
    983 }
    984 
    985 int ImFormatStringV(char* buf, int buf_size, const char* fmt, va_list args)
    986 {
    987     IM_ASSERT(buf_size > 0);
    988     int w = vsnprintf(buf, buf_size, fmt, args);
    989     if (w == -1 || w >= buf_size)
    990         w = buf_size - 1;
    991     buf[w] = 0;
    992     return w;
    993 }
    994 
    995 // Pass data_size==0 for zero-terminated strings
    996 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
    997 ImU32 ImHash(const void* data, int data_size, ImU32 seed)
    998 {
    999     static ImU32 crc32_lut[256] = { 0 };
   1000     if (!crc32_lut[1])
   1001     {
   1002         const ImU32 polynomial = 0xEDB88320;
   1003         for (ImU32 i = 0; i < 256; i++)
   1004         {
   1005             ImU32 crc = i;
   1006             for (ImU32 j = 0; j < 8; j++)
   1007                 crc = (crc >> 1) ^ (ImU32(-int(crc & 1)) & polynomial);
   1008             crc32_lut[i] = crc;
   1009         }
   1010     }
   1011 
   1012     seed = ~seed;
   1013     ImU32 crc = seed;
   1014     const unsigned char* current = (const unsigned char*)data;
   1015 
   1016     if (data_size > 0)
   1017     {
   1018         // Known size
   1019         while (data_size--)
   1020             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *current++];
   1021     }
   1022     else
   1023     {
   1024         // Zero-terminated string
   1025         while (unsigned char c = *current++)
   1026         {
   1027             // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
   1028             // Because this syntax is rarely used we are optimizing for the common case.
   1029             // - If we reach ### in the string we discard the hash so far and reset to the seed.
   1030             // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller.
   1031             if (c == '#' && current[0] == '#' && current[1] == '#')
   1032                 crc = seed;
   1033             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
   1034         }
   1035     }
   1036     return ~crc;
   1037 }
   1038 
   1039 //-----------------------------------------------------------------------------
   1040 // ImText* helpers
   1041 //-----------------------------------------------------------------------------
   1042 
   1043 // Convert UTF-8 to 32-bits character, process single character input.
   1044 // Based on stb_from_utf8() from github.com/nothings/stb/
   1045 // We handle UTF-8 decoding error by skipping forward.
   1046 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
   1047 {
   1048     unsigned int c = (unsigned int)-1;
   1049     const unsigned char* str = (const unsigned char*)in_text;
   1050     if (!(*str & 0x80))
   1051     {
   1052         c = (unsigned int)(*str++);
   1053         *out_char = c;
   1054         return 1;
   1055     }
   1056     if ((*str & 0xe0) == 0xc0)
   1057     {
   1058         *out_char = 0xFFFD; // will be invalid but not end of string
   1059         if (in_text_end && in_text_end - (const char*)str < 2) return 1;
   1060         if (*str < 0xc2) return 2;
   1061         c = (unsigned int)((*str++ & 0x1f) << 6);
   1062         if ((*str & 0xc0) != 0x80) return 2;
   1063         c += (*str++ & 0x3f);
   1064         *out_char = c;
   1065         return 2;
   1066     }
   1067     if ((*str & 0xf0) == 0xe0)
   1068     {
   1069         *out_char = 0xFFFD; // will be invalid but not end of string
   1070         if (in_text_end && in_text_end - (const char*)str < 3) return 1;
   1071         if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3;
   1072         if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below
   1073         c = (unsigned int)((*str++ & 0x0f) << 12);
   1074         if ((*str & 0xc0) != 0x80) return 3;
   1075         c += (unsigned int)((*str++ & 0x3f) << 6);
   1076         if ((*str & 0xc0) != 0x80) return 3;
   1077         c += (*str++ & 0x3f);
   1078         *out_char = c;
   1079         return 3;
   1080     }
   1081     if ((*str & 0xf8) == 0xf0)
   1082     {
   1083         *out_char = 0xFFFD; // will be invalid but not end of string
   1084         if (in_text_end && in_text_end - (const char*)str < 4) return 1;
   1085         if (*str > 0xf4) return 4;
   1086         if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4;
   1087         if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below
   1088         c = (unsigned int)((*str++ & 0x07) << 18);
   1089         if ((*str & 0xc0) != 0x80) return 4;
   1090         c += (unsigned int)((*str++ & 0x3f) << 12);
   1091         if ((*str & 0xc0) != 0x80) return 4;
   1092         c += (unsigned int)((*str++ & 0x3f) << 6);
   1093         if ((*str & 0xc0) != 0x80) return 4;
   1094         c += (*str++ & 0x3f);
   1095         // utf-8 encodings of values used in surrogate pairs are invalid
   1096         if ((c & 0xFFFFF800) == 0xD800) return 4;
   1097         *out_char = c;
   1098         return 4;
   1099     }
   1100     *out_char = 0;
   1101     return 0;
   1102 }
   1103 
   1104 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
   1105 {
   1106     ImWchar* buf_out = buf;
   1107     ImWchar* buf_end = buf + buf_size;
   1108     while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
   1109     {
   1110         unsigned int c;
   1111         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
   1112         if (c == 0)
   1113             break;
   1114         if (c < 0x10000)    // FIXME: Losing characters that don't fit in 2 bytes
   1115             *buf_out++ = (ImWchar)c;
   1116     }
   1117     *buf_out = 0;
   1118     if (in_text_remaining)
   1119         *in_text_remaining = in_text;
   1120     return (int)(buf_out - buf);
   1121 }
   1122 
   1123 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
   1124 {
   1125     int char_count = 0;
   1126     while ((!in_text_end || in_text < in_text_end) && *in_text)
   1127     {
   1128         unsigned int c;
   1129         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
   1130         if (c == 0)
   1131             break;
   1132         if (c < 0x10000)
   1133             char_count++;
   1134     }
   1135     return char_count;
   1136 }
   1137 
   1138 // Based on stb_to_utf8() from github.com/nothings/stb/
   1139 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
   1140 {
   1141     if (c < 0x80)
   1142     {
   1143         buf[0] = (char)c;
   1144         return 1;
   1145     }
   1146     if (c < 0x800)
   1147     {
   1148         if (buf_size < 2) return 0;
   1149         buf[0] = (char)(0xc0 + (c >> 6));
   1150         buf[1] = (char)(0x80 + (c & 0x3f));
   1151         return 2;
   1152     }
   1153     if (c >= 0xdc00 && c < 0xe000)
   1154     {
   1155         return 0;
   1156     }
   1157     if (c >= 0xd800 && c < 0xdc00)
   1158     {
   1159         if (buf_size < 4) return 0;
   1160         buf[0] = (char)(0xf0 + (c >> 18));
   1161         buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
   1162         buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
   1163         buf[3] = (char)(0x80 + ((c ) & 0x3f));
   1164         return 4;
   1165     }
   1166     //else if (c < 0x10000)
   1167     {
   1168         if (buf_size < 3) return 0;
   1169         buf[0] = (char)(0xe0 + (c >> 12));
   1170         buf[1] = (char)(0x80 + ((c>> 6) & 0x3f));
   1171         buf[2] = (char)(0x80 + ((c ) & 0x3f));
   1172         return 3;
   1173     }
   1174 }
   1175 
   1176 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
   1177 {
   1178     if (c < 0x80) return 1;
   1179     if (c < 0x800) return 2;
   1180     if (c >= 0xdc00 && c < 0xe000) return 0;
   1181     if (c >= 0xd800 && c < 0xdc00) return 4;
   1182     return 3;
   1183 }
   1184 
   1185 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
   1186 {
   1187     char* buf_out = buf;
   1188     const char* buf_end = buf + buf_size;
   1189     while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
   1190     {
   1191         unsigned int c = (unsigned int)(*in_text++);
   1192         if (c < 0x80)
   1193             *buf_out++ = (char)c;
   1194         else
   1195             buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c);
   1196     }
   1197     *buf_out = 0;
   1198     return (int)(buf_out - buf);
   1199 }
   1200 
   1201 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
   1202 {
   1203     int bytes_count = 0;
   1204     while ((!in_text_end || in_text < in_text_end) && *in_text)
   1205     {
   1206         unsigned int c = (unsigned int)(*in_text++);
   1207         if (c < 0x80)
   1208             bytes_count++;
   1209         else
   1210             bytes_count += ImTextCountUtf8BytesFromChar(c);
   1211     }
   1212     return bytes_count;
   1213 }
   1214 
   1215 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
   1216 {
   1217     float s = 1.0f/255.0f;
   1218     return ImVec4(
   1219         ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
   1220         ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
   1221         ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
   1222         ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
   1223 }
   1224 
   1225 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
   1226 {
   1227     ImU32 out;
   1228     out  = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
   1229     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
   1230     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
   1231     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
   1232     return out;
   1233 }
   1234 
   1235 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)  
   1236 { 
   1237     ImVec4 c = GImGui->Style.Colors[idx]; 
   1238     c.w *= GImGui->Style.Alpha * alpha_mul; 
   1239     return ColorConvertFloat4ToU32(c); 
   1240 }
   1241 
   1242 ImU32 ImGui::GetColorU32(const ImVec4& col)
   1243 { 
   1244     ImVec4 c = col; 
   1245     c.w *= GImGui->Style.Alpha; 
   1246     return ColorConvertFloat4ToU32(c); 
   1247 }
   1248 
   1249 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
   1250 // Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
   1251 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
   1252 {
   1253     float K = 0.f;
   1254     if (g < b)
   1255     {
   1256         const float tmp = g; g = b; b = tmp;
   1257         K = -1.f;
   1258     }
   1259     if (r < g)
   1260     {
   1261         const float tmp = r; r = g; g = tmp;
   1262         K = -2.f / 6.f - K;
   1263     }
   1264 
   1265     const float chroma = r - (g < b ? g : b);
   1266     out_h = fabsf(K + (g - b) / (6.f * chroma + 1e-20f));
   1267     out_s = chroma / (r + 1e-20f);
   1268     out_v = r;
   1269 }
   1270 
   1271 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
   1272 // also http://en.wikipedia.org/wiki/HSL_and_HSV
   1273 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
   1274 {
   1275     if (s == 0.0f)
   1276     {
   1277         // gray
   1278         out_r = out_g = out_b = v;
   1279         return;
   1280     }
   1281 
   1282     h = fmodf(h, 1.0f) / (60.0f/360.0f);
   1283     int   i = (int)h;
   1284     float f = h - (float)i;
   1285     float p = v * (1.0f - s);
   1286     float q = v * (1.0f - s * f);
   1287     float t = v * (1.0f - s * (1.0f - f));
   1288 
   1289     switch (i)
   1290     {
   1291     case 0: out_r = v; out_g = t; out_b = p; break;
   1292     case 1: out_r = q; out_g = v; out_b = p; break;
   1293     case 2: out_r = p; out_g = v; out_b = t; break;
   1294     case 3: out_r = p; out_g = q; out_b = v; break;
   1295     case 4: out_r = t; out_g = p; out_b = v; break;
   1296     case 5: default: out_r = v; out_g = p; out_b = q; break;
   1297     }
   1298 }
   1299 
   1300 FILE* ImFileOpen(const char* filename, const char* mode)
   1301 {
   1302 #if defined(_WIN32) && !defined(__CYGWIN__)
   1303     // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. Converting both strings from UTF-8 to wchar format (using a single allocation, because we can)
   1304     const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1;
   1305     const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1;
   1306     ImVector<ImWchar> buf;
   1307     buf.resize(filename_wsize + mode_wsize);
   1308     ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL);
   1309     ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL);
   1310     return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]);
   1311 #else
   1312     return fopen(filename, mode);
   1313 #endif
   1314 }
   1315 
   1316 // Load file content into memory
   1317 // Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree()
   1318 void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, int* out_file_size, int padding_bytes)
   1319 {
   1320     IM_ASSERT(filename && file_open_mode);
   1321     if (out_file_size)
   1322         *out_file_size = 0;
   1323 
   1324     FILE* f;
   1325     if ((f = ImFileOpen(filename, file_open_mode)) == NULL)
   1326         return NULL;
   1327 
   1328     long file_size_signed;
   1329     if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET))
   1330     {
   1331         fclose(f);
   1332         return NULL;
   1333     }
   1334 
   1335     int file_size = (int)file_size_signed;
   1336     void* file_data = ImGui::MemAlloc(file_size + padding_bytes);
   1337     if (file_data == NULL)
   1338     {
   1339         fclose(f);
   1340         return NULL;
   1341     }
   1342     if (fread(file_data, 1, (size_t)file_size, f) != (size_t)file_size)
   1343     {
   1344         fclose(f);
   1345         ImGui::MemFree(file_data);
   1346         return NULL;
   1347     }
   1348     if (padding_bytes > 0)
   1349         memset((void *)(((char*)file_data) + file_size), 0, padding_bytes);
   1350 
   1351     fclose(f);
   1352     if (out_file_size)
   1353         *out_file_size = file_size;
   1354 
   1355     return file_data;
   1356 }
   1357 
   1358 //-----------------------------------------------------------------------------
   1359 // ImGuiStorage
   1360 //-----------------------------------------------------------------------------
   1361 
   1362 // Helper: Key->value storage
   1363 void ImGuiStorage::Clear()
   1364 {
   1365     Data.clear();
   1366 }
   1367 
   1368 // std::lower_bound but without the bullshit
   1369 static ImVector<ImGuiStorage::Pair>::iterator LowerBound(ImVector<ImGuiStorage::Pair>& data, ImGuiID key)
   1370 {
   1371     ImVector<ImGuiStorage::Pair>::iterator first = data.begin();
   1372     ImVector<ImGuiStorage::Pair>::iterator last = data.end();
   1373     int count = (int)(last - first);
   1374     while (count > 0)
   1375     {
   1376         int count2 = count / 2;
   1377         ImVector<ImGuiStorage::Pair>::iterator mid = first + count2;
   1378         if (mid->key < key)
   1379         {
   1380             first = ++mid;
   1381             count -= count2 + 1;
   1382         }
   1383         else
   1384         {
   1385             count = count2;
   1386         }
   1387     }
   1388     return first;
   1389 }
   1390 
   1391 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
   1392 {
   1393     ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
   1394     if (it == Data.end() || it->key != key)
   1395         return default_val;
   1396     return it->val_i;
   1397 }
   1398 
   1399 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
   1400 {
   1401     return GetInt(key, default_val ? 1 : 0) != 0;
   1402 }
   1403 
   1404 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
   1405 {
   1406     ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
   1407     if (it == Data.end() || it->key != key)
   1408         return default_val;
   1409     return it->val_f;
   1410 }
   1411 
   1412 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
   1413 {
   1414     ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
   1415     if (it == Data.end() || it->key != key)
   1416         return NULL;
   1417     return it->val_p;
   1418 }
   1419 
   1420 // 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.
   1421 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
   1422 {
   1423     ImVector<Pair>::iterator it = LowerBound(Data, key);
   1424     if (it == Data.end() || it->key != key)
   1425         it = Data.insert(it, Pair(key, default_val));
   1426     return &it->val_i;
   1427 }
   1428 
   1429 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
   1430 {
   1431     return (bool*)GetIntRef(key, default_val ? 1 : 0);
   1432 }
   1433 
   1434 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
   1435 {
   1436     ImVector<Pair>::iterator it = LowerBound(Data, key);
   1437     if (it == Data.end() || it->key != key)
   1438         it = Data.insert(it, Pair(key, default_val));
   1439     return &it->val_f;
   1440 }
   1441 
   1442 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
   1443 {
   1444     ImVector<Pair>::iterator it = LowerBound(Data, key);
   1445     if (it == Data.end() || it->key != key)
   1446         it = Data.insert(it, Pair(key, default_val));
   1447     return &it->val_p;
   1448 }
   1449 
   1450 // 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)
   1451 void ImGuiStorage::SetInt(ImGuiID key, int val)
   1452 {
   1453     ImVector<Pair>::iterator it = LowerBound(Data, key);
   1454     if (it == Data.end() || it->key != key)
   1455     {
   1456         Data.insert(it, Pair(key, val));
   1457         return;
   1458     }
   1459     it->val_i = val;
   1460 }
   1461 
   1462 void ImGuiStorage::SetBool(ImGuiID key, bool val)
   1463 {
   1464     SetInt(key, val ? 1 : 0);
   1465 }
   1466 
   1467 void ImGuiStorage::SetFloat(ImGuiID key, float val)
   1468 {
   1469     ImVector<Pair>::iterator it = LowerBound(Data, key);
   1470     if (it == Data.end() || it->key != key)
   1471     {
   1472         Data.insert(it, Pair(key, val));
   1473         return;
   1474     }
   1475     it->val_f = val;
   1476 }
   1477 
   1478 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
   1479 {
   1480     ImVector<Pair>::iterator it = LowerBound(Data, key);
   1481     if (it == Data.end() || it->key != key)
   1482     {
   1483         Data.insert(it, Pair(key, val));
   1484         return;
   1485     }
   1486     it->val_p = val;
   1487 }
   1488 
   1489 void ImGuiStorage::SetAllInt(int v)
   1490 {
   1491     for (int i = 0; i < Data.Size; i++)
   1492         Data[i].val_i = v;
   1493 }
   1494 
   1495 //-----------------------------------------------------------------------------
   1496 // ImGuiTextFilter
   1497 //-----------------------------------------------------------------------------
   1498 
   1499 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
   1500 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
   1501 {
   1502     if (default_filter)
   1503     {
   1504         ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
   1505         Build();
   1506     }
   1507     else
   1508     {
   1509         InputBuf[0] = 0;
   1510         CountGrep = 0;
   1511     }
   1512 }
   1513 
   1514 bool ImGuiTextFilter::Draw(const char* label, float width)
   1515 {
   1516     if (width != 0.0f)
   1517         ImGui::PushItemWidth(width);
   1518     bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
   1519     if (width != 0.0f)
   1520         ImGui::PopItemWidth();
   1521     if (value_changed)
   1522         Build();
   1523     return value_changed;
   1524 }
   1525 
   1526 void ImGuiTextFilter::TextRange::split(char separator, ImVector<TextRange>& out)
   1527 {
   1528     out.resize(0);
   1529     const char* wb = b;
   1530     const char* we = wb;
   1531     while (we < e)
   1532     {
   1533         if (*we == separator)
   1534         {
   1535             out.push_back(TextRange(wb, we));
   1536             wb = we + 1;
   1537         }
   1538         we++;
   1539     }
   1540     if (wb != we)
   1541         out.push_back(TextRange(wb, we));
   1542 }
   1543 
   1544 void ImGuiTextFilter::Build()
   1545 {
   1546     Filters.resize(0);
   1547     TextRange input_range(InputBuf, InputBuf+strlen(InputBuf));
   1548     input_range.split(',', Filters);
   1549 
   1550     CountGrep = 0;
   1551     for (int i = 0; i != Filters.Size; i++)
   1552     {
   1553         Filters[i].trim_blanks();
   1554         if (Filters[i].empty())
   1555             continue;
   1556         if (Filters[i].front() != '-')
   1557             CountGrep += 1;
   1558     }
   1559 }
   1560 
   1561 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
   1562 {
   1563     if (Filters.empty())
   1564         return true;
   1565 
   1566     if (text == NULL)
   1567         text = "";
   1568 
   1569     for (int i = 0; i != Filters.Size; i++)
   1570     {
   1571         const TextRange& f = Filters[i];
   1572         if (f.empty())
   1573             continue;
   1574         if (f.front() == '-')
   1575         {
   1576             // Subtract
   1577             if (ImStristr(text, text_end, f.begin()+1, f.end()) != NULL)
   1578                 return false;
   1579         }
   1580         else
   1581         {
   1582             // Grep
   1583             if (ImStristr(text, text_end, f.begin(), f.end()) != NULL)
   1584                 return true;
   1585         }
   1586     }
   1587 
   1588     // Implicit * grep
   1589     if (CountGrep == 0)
   1590         return true;
   1591 
   1592     return false;
   1593 }
   1594 
   1595 //-----------------------------------------------------------------------------
   1596 // ImGuiTextBuffer
   1597 //-----------------------------------------------------------------------------
   1598 
   1599 // On some platform vsnprintf() takes va_list by reference and modifies it.
   1600 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
   1601 #ifndef va_copy
   1602 #define va_copy(dest, src) (dest = src)
   1603 #endif
   1604 
   1605 // Helper: Text buffer for logging/accumulating text
   1606 void ImGuiTextBuffer::appendv(const char* fmt, va_list args)
   1607 {
   1608     va_list args_copy;
   1609     va_copy(args_copy, args);
   1610 
   1611     int len = vsnprintf(NULL, 0, fmt, args);         // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
   1612     if (len <= 0)
   1613         return;
   1614 
   1615     const int write_off = Buf.Size;
   1616     const int needed_sz = write_off + len;
   1617     if (write_off + len >= Buf.Capacity)
   1618     {
   1619         int double_capacity = Buf.Capacity * 2;
   1620         Buf.reserve(needed_sz > double_capacity ? needed_sz : double_capacity);
   1621     }
   1622 
   1623     Buf.resize(needed_sz);
   1624     ImFormatStringV(&Buf[write_off] - 1, len+1, fmt, args_copy);
   1625 }
   1626 
   1627 void ImGuiTextBuffer::append(const char* fmt, ...)
   1628 {
   1629     va_list args;
   1630     va_start(args, fmt);
   1631     appendv(fmt, args);
   1632     va_end(args);
   1633 }
   1634 
   1635 //-----------------------------------------------------------------------------
   1636 // ImGuiSimpleColumns
   1637 //-----------------------------------------------------------------------------
   1638 
   1639 ImGuiSimpleColumns::ImGuiSimpleColumns()
   1640 {
   1641     Count = 0;
   1642     Spacing = Width = NextWidth = 0.0f;
   1643     memset(Pos, 0, sizeof(Pos));
   1644     memset(NextWidths, 0, sizeof(NextWidths));
   1645 }
   1646 
   1647 void ImGuiSimpleColumns::Update(int count, float spacing, bool clear)
   1648 {
   1649     IM_ASSERT(Count <= IM_ARRAYSIZE(Pos));
   1650     Count = count;
   1651     Width = NextWidth = 0.0f;
   1652     Spacing = spacing;
   1653     if (clear) memset(NextWidths, 0, sizeof(NextWidths));
   1654     for (int i = 0; i < Count; i++)
   1655     {
   1656         if (i > 0 && NextWidths[i] > 0.0f)
   1657             Width += Spacing;
   1658         Pos[i] = (float)(int)Width;
   1659         Width += NextWidths[i];
   1660         NextWidths[i] = 0.0f;
   1661     }
   1662 }
   1663 
   1664 float ImGuiSimpleColumns::DeclColumns(float w0, float w1, float w2) // not using va_arg because they promote float to double
   1665 {
   1666     NextWidth = 0.0f;
   1667     NextWidths[0] = ImMax(NextWidths[0], w0);
   1668     NextWidths[1] = ImMax(NextWidths[1], w1);
   1669     NextWidths[2] = ImMax(NextWidths[2], w2);
   1670     for (int i = 0; i < 3; i++)
   1671         NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f);
   1672     return ImMax(Width, NextWidth);
   1673 }
   1674 
   1675 float ImGuiSimpleColumns::CalcExtraSpace(float avail_w)
   1676 {
   1677     return ImMax(0.0f, avail_w - Width);
   1678 }
   1679 
   1680 //-----------------------------------------------------------------------------
   1681 // ImGuiListClipper
   1682 //-----------------------------------------------------------------------------
   1683 
   1684 static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height)
   1685 {
   1686     // Set cursor position and a few other things so that SetScrollHere() and Columns() can work when seeking cursor. 
   1687     // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue. Consider moving within SetCursorXXX functions?
   1688     ImGui::SetCursorPosY(pos_y);
   1689     ImGuiWindow* window = ImGui::GetCurrentWindow();
   1690     window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height;      // Setting those fields so that SetScrollHere() can properly function after the end of our clipper usage.
   1691     window->DC.PrevLineHeight = (line_height - GImGui->Style.ItemSpacing.y);    // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list.
   1692     if (window->DC.ColumnsCount > 1)
   1693         window->DC.ColumnsCellMinY = window->DC.CursorPos.y;                    // Setting this so that cell Y position are set properly
   1694 }
   1695 
   1696 // Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
   1697 // Use case B: Begin() called from constructor with items_height>0
   1698 // FIXME-LEGACY: Ideally we should remove the Begin/End functions but they are part of the legacy API we still support. This is why some of the code in Step() calling Begin() and reassign some fields, spaghetti style.
   1699 void ImGuiListClipper::Begin(int count, float items_height)
   1700 {
   1701     StartPosY = ImGui::GetCursorPosY();
   1702     ItemsHeight = items_height;
   1703     ItemsCount = count;
   1704     StepNo = 0;
   1705     DisplayEnd = DisplayStart = -1;
   1706     if (ItemsHeight > 0.0f)
   1707     {
   1708         ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display
   1709         if (DisplayStart > 0)
   1710             SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor
   1711         StepNo = 2;
   1712     }
   1713 }
   1714 
   1715 void ImGuiListClipper::End()
   1716 {
   1717     if (ItemsCount < 0)
   1718         return;
   1719     // In theory here we should assert that ImGui::GetCursorPosY() == StartPosY + DisplayEnd * ItemsHeight, but it feels saner to just seek at the end and not assert/crash the user.
   1720     if (ItemsCount < INT_MAX)
   1721         SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor
   1722     ItemsCount = -1;
   1723     StepNo = 3;
   1724 }
   1725 
   1726 bool ImGuiListClipper::Step()
   1727 {
   1728     if (ItemsCount == 0 || ImGui::GetCurrentWindowRead()->SkipItems)
   1729     {
   1730         ItemsCount = -1; 
   1731         return false; 
   1732     }
   1733     if (StepNo == 0) // Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height.
   1734     {
   1735         DisplayStart = 0;
   1736         DisplayEnd = 1;
   1737         StartPosY = ImGui::GetCursorPosY();
   1738         StepNo = 1;
   1739         return true;
   1740     }
   1741     if (StepNo == 1) // Step 1: the clipper infer height from first element, calculate the actual range of elements to display, and position the cursor before the first element.
   1742     {
   1743         if (ItemsCount == 1) { ItemsCount = -1; return false; }
   1744         float items_height = ImGui::GetCursorPosY() - StartPosY;
   1745         IM_ASSERT(items_height > 0.0f);   // If this triggers, it means Item 0 hasn't moved the cursor vertically
   1746         Begin(ItemsCount-1, items_height);
   1747         DisplayStart++;
   1748         DisplayEnd++;
   1749         StepNo = 3;
   1750         return true;
   1751     }
   1752     if (StepNo == 2) // Step 2: dummy step only required if an explicit items_height was passed to constructor or Begin() and user still call Step(). Does nothing and switch to Step 3.
   1753     {
   1754         IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0);
   1755         StepNo = 3;
   1756         return true;
   1757     }
   1758     if (StepNo == 3) // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), advance the cursor to the end of the list and then returns 'false' to end the loop.
   1759         End();
   1760     return false;
   1761 }
   1762 
   1763 //-----------------------------------------------------------------------------
   1764 // ImGuiWindow
   1765 //-----------------------------------------------------------------------------
   1766 
   1767 ImGuiWindow::ImGuiWindow(const char* name)
   1768 {
   1769     Name = ImStrdup(name);
   1770     ID = ImHash(name, 0);
   1771     IDStack.push_back(ID);
   1772     MoveId = GetID("#MOVE");
   1773 
   1774     Flags = 0;
   1775     IndexWithinParent = 0;
   1776     PosFloat = Pos = ImVec2(0.0f, 0.0f);
   1777     Size = SizeFull = ImVec2(0.0f, 0.0f);
   1778     SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f);
   1779     WindowPadding = ImVec2(0.0f, 0.0f);
   1780     Scroll = ImVec2(0.0f, 0.0f);
   1781     ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
   1782     ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
   1783     ScrollbarX = ScrollbarY = false;
   1784     ScrollbarSizes = ImVec2(0.0f, 0.0f);
   1785     BorderSize = 0.0f;
   1786     Active = WasActive = false;
   1787     Accessed = false;
   1788     Collapsed = false;
   1789     SkipItems = false;
   1790     BeginCount = 0;
   1791     PopupId = 0;
   1792     AutoFitFramesX = AutoFitFramesY = -1;
   1793     AutoFitOnlyGrows = false;
   1794     AutoPosLastDirection = -1;
   1795     HiddenFrames = 0;
   1796     SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiSetCond_Always | ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing;
   1797     SetWindowPosCenterWanted = false;
   1798 
   1799     LastFrameActive = -1;
   1800     ItemWidthDefault = 0.0f;
   1801     FontWindowScale = 1.0f;
   1802 
   1803     DrawList = (ImDrawList*)ImGui::MemAlloc(sizeof(ImDrawList));
   1804     IM_PLACEMENT_NEW(DrawList) ImDrawList();
   1805     DrawList->_OwnerName = Name;
   1806     RootWindow = NULL;
   1807     RootNonPopupWindow = NULL;
   1808     ParentWindow = NULL;
   1809 
   1810     FocusIdxAllCounter = FocusIdxTabCounter = -1;
   1811     FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX;
   1812     FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX;
   1813 }
   1814 
   1815 ImGuiWindow::~ImGuiWindow()
   1816 {
   1817     DrawList->~ImDrawList();
   1818     ImGui::MemFree(DrawList);
   1819     DrawList = NULL;
   1820     ImGui::MemFree(Name);
   1821     Name = NULL;
   1822 }
   1823 
   1824 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
   1825 {
   1826     ImGuiID seed = IDStack.back();
   1827     ImGuiID id = ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
   1828     ImGui::KeepAliveID(id);
   1829     return id;
   1830 }
   1831 
   1832 ImGuiID ImGuiWindow::GetID(const void* ptr)
   1833 {
   1834     ImGuiID seed = IDStack.back();
   1835     ImGuiID id = ImHash(&ptr, sizeof(void*), seed);
   1836     ImGui::KeepAliveID(id);
   1837     return id;
   1838 }
   1839 
   1840 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
   1841 {
   1842     ImGuiID seed = IDStack.back();
   1843     return ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
   1844 }
   1845 
   1846 //-----------------------------------------------------------------------------
   1847 // Internal API exposed in imgui_internal.h
   1848 //-----------------------------------------------------------------------------
   1849 
   1850 static void SetCurrentWindow(ImGuiWindow* window)
   1851 {
   1852     ImGuiContext& g = *GImGui;
   1853     g.CurrentWindow = window;
   1854     if (window)
   1855         g.FontSize = window->CalcFontSize();
   1856 }
   1857 
   1858 ImGuiWindow* ImGui::GetParentWindow()
   1859 {
   1860     ImGuiContext& g = *GImGui;
   1861     IM_ASSERT(g.CurrentWindowStack.Size >= 2);
   1862     return g.CurrentWindowStack[(unsigned int)g.CurrentWindowStack.Size - 2];
   1863 }
   1864 
   1865 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
   1866 {
   1867     ImGuiContext& g = *GImGui;
   1868     g.ActiveId = id;
   1869     g.ActiveIdAllowOverlap = false;
   1870     g.ActiveIdIsJustActivated = true;
   1871     if (id)
   1872         g.ActiveIdIsAlive = true;
   1873     g.ActiveIdWindow = window;
   1874 }
   1875 
   1876 void ImGui::ClearActiveID()
   1877 {
   1878     SetActiveID(0, NULL);
   1879 }
   1880 
   1881 void ImGui::SetHoveredID(ImGuiID id)
   1882 {
   1883     ImGuiContext& g = *GImGui;
   1884     g.HoveredId = id;
   1885     g.HoveredIdAllowOverlap = false;
   1886 }
   1887 
   1888 void ImGui::KeepAliveID(ImGuiID id)
   1889 {
   1890     ImGuiContext& g = *GImGui;
   1891     if (g.ActiveId == id)
   1892         g.ActiveIdIsAlive = true;
   1893 }
   1894 
   1895 // Advance cursor given item size for layout.
   1896 void ImGui::ItemSize(const ImVec2& size, float text_offset_y)
   1897 {
   1898     ImGuiWindow* window = GetCurrentWindow();
   1899     if (window->SkipItems)
   1900         return;
   1901 
   1902     // Always align ourselves on pixel boundaries
   1903     ImGuiContext& g = *GImGui;
   1904     const float line_height = ImMax(window->DC.CurrentLineHeight, size.y);
   1905     const float text_base_offset = ImMax(window->DC.CurrentLineTextBaseOffset, text_offset_y);
   1906     window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y);
   1907     window->DC.CursorPos = ImVec2((float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX), (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y));
   1908     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
   1909     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
   1910 
   1911     //window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // Debug
   1912 
   1913     window->DC.PrevLineHeight = line_height;
   1914     window->DC.PrevLineTextBaseOffset = text_base_offset;
   1915     window->DC.CurrentLineHeight = window->DC.CurrentLineTextBaseOffset = 0.0f;
   1916 }
   1917 
   1918 void ImGui::ItemSize(const ImRect& bb, float text_offset_y)
   1919 {
   1920     ItemSize(bb.GetSize(), text_offset_y);
   1921 }
   1922 
   1923 // Declare item bounding box for clipping and interaction.
   1924 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
   1925 // declares their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd().
   1926 bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id)
   1927 {
   1928     ImGuiWindow* window = GetCurrentWindow();
   1929     window->DC.LastItemId = id ? *id : 0;
   1930     window->DC.LastItemRect = bb;
   1931     window->DC.LastItemHoveredAndUsable = window->DC.LastItemHoveredRect = false;
   1932     if (IsClippedEx(bb, id, false))
   1933         return false;
   1934 
   1935     // This is a sensible default, but widgets are free to override it after calling ItemAdd()
   1936     ImGuiContext& g = *GImGui;
   1937     if (IsMouseHoveringRect(bb.Min, bb.Max))
   1938     {
   1939         // Matching the behavior of IsHovered() but allow if ActiveId==window->MoveID (we clicked on the window background)
   1940         // So that clicking on items with no active id such as Text() still returns true with IsItemHovered()
   1941         window->DC.LastItemHoveredRect = true;
   1942         if (g.HoveredRootWindow == window->RootWindow)
   1943             if (g.ActiveId == 0 || (id && g.ActiveId == *id) || g.ActiveIdAllowOverlap || (g.ActiveId == window->MoveId))
   1944                 if (IsWindowContentHoverable(window))
   1945                     window->DC.LastItemHoveredAndUsable = true;
   1946     }
   1947 
   1948     return true;
   1949 }
   1950 
   1951 bool ImGui::IsClippedEx(const ImRect& bb, const ImGuiID* id, bool clip_even_when_logged)
   1952 {
   1953     ImGuiContext& g = *GImGui;
   1954     ImGuiWindow* window = GetCurrentWindowRead();
   1955 
   1956     if (!bb.Overlaps(window->ClipRect))
   1957         if (!id || *id != GImGui->ActiveId)
   1958             if (clip_even_when_logged || !g.LogEnabled)
   1959                 return true;
   1960     return false;
   1961 }
   1962 
   1963 // NB: This is an internal helper. The user-facing IsItemHovered() is using data emitted from ItemAdd(), with a slightly different logic.
   1964 bool ImGui::IsHovered(const ImRect& bb, ImGuiID id, bool flatten_childs)
   1965 {
   1966     ImGuiContext& g = *GImGui;
   1967     if (g.HoveredId == 0 || g.HoveredId == id || g.HoveredIdAllowOverlap)
   1968     {
   1969         ImGuiWindow* window = GetCurrentWindowRead();
   1970         if (g.HoveredWindow == window || (flatten_childs && g.HoveredRootWindow == window->RootWindow))
   1971             if ((g.ActiveId == 0 || g.ActiveId == id || g.ActiveIdAllowOverlap) && IsMouseHoveringRect(bb.Min, bb.Max))
   1972                 if (IsWindowContentHoverable(g.HoveredRootWindow))
   1973                     return true;
   1974     }
   1975     return false;
   1976 }
   1977 
   1978 bool ImGui::FocusableItemRegister(ImGuiWindow* window, bool is_active, bool tab_stop)
   1979 {
   1980     ImGuiContext& g = *GImGui;
   1981 
   1982     const bool allow_keyboard_focus = window->DC.AllowKeyboardFocus;
   1983     window->FocusIdxAllCounter++;
   1984     if (allow_keyboard_focus)
   1985         window->FocusIdxTabCounter++;
   1986 
   1987     // Process keyboard input at this point: TAB, Shift-TAB switch focus
   1988     // We can always TAB out of a widget that doesn't allow tabbing in.
   1989     if (tab_stop && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && is_active && IsKeyPressedMap(ImGuiKey_Tab))
   1990     {
   1991         // Modulo on index will be applied at the end of frame once we've got the total counter of items.
   1992         window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (allow_keyboard_focus ? -1 : 0) : +1);
   1993     }
   1994 
   1995     if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent)
   1996         return true;
   1997 
   1998     if (allow_keyboard_focus)
   1999         if (window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent)
   2000             return true;
   2001 
   2002     return false;
   2003 }
   2004 
   2005 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
   2006 {
   2007     window->FocusIdxAllCounter--;
   2008     window->FocusIdxTabCounter--;
   2009 }
   2010 
   2011 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_x, float default_y)
   2012 {
   2013     ImGuiContext& g = *GImGui;
   2014     ImVec2 content_max;
   2015     if (size.x < 0.0f || size.y < 0.0f)
   2016         content_max = g.CurrentWindow->Pos + GetContentRegionMax();
   2017     if (size.x <= 0.0f)
   2018         size.x = (size.x == 0.0f) ? default_x : ImMax(content_max.x - g.CurrentWindow->DC.CursorPos.x, 4.0f) + size.x;
   2019     if (size.y <= 0.0f)
   2020         size.y = (size.y == 0.0f) ? default_y : ImMax(content_max.y - g.CurrentWindow->DC.CursorPos.y, 4.0f) + size.y;
   2021     return size;
   2022 }
   2023 
   2024 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
   2025 {
   2026     if (wrap_pos_x < 0.0f)
   2027         return 0.0f;
   2028 
   2029     ImGuiWindow* window = GetCurrentWindowRead();
   2030     if (wrap_pos_x == 0.0f)
   2031         wrap_pos_x = GetContentRegionMax().x + window->Pos.x;
   2032     else if (wrap_pos_x > 0.0f)
   2033         wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
   2034 
   2035     return ImMax(wrap_pos_x - pos.x, 1.0f);
   2036 }
   2037 
   2038 //-----------------------------------------------------------------------------
   2039 
   2040 void* ImGui::MemAlloc(size_t sz)
   2041 {
   2042     GImGui->IO.MetricsAllocs++;
   2043     return GImGui->IO.MemAllocFn(sz);
   2044 }
   2045 
   2046 void ImGui::MemFree(void* ptr)
   2047 {
   2048     if (ptr) GImGui->IO.MetricsAllocs--;
   2049     return GImGui->IO.MemFreeFn(ptr);
   2050 }
   2051 
   2052 const char* ImGui::GetClipboardText()
   2053 {
   2054     return GImGui->IO.GetClipboardTextFn ? GImGui->IO.GetClipboardTextFn(GImGui->IO.ClipboardUserData) : "";
   2055 }
   2056 
   2057 void ImGui::SetClipboardText(const char* text)
   2058 {
   2059     if (GImGui->IO.SetClipboardTextFn)
   2060         GImGui->IO.SetClipboardTextFn(GImGui->IO.ClipboardUserData, text);
   2061 }
   2062 
   2063 const char* ImGui::GetVersion()
   2064 {
   2065     return IMGUI_VERSION;
   2066 }
   2067 
   2068 // Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself
   2069 // 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
   2070 ImGuiContext* ImGui::GetCurrentContext()
   2071 {
   2072     return GImGui;
   2073 }
   2074 
   2075 void ImGui::SetCurrentContext(ImGuiContext* ctx)
   2076 {
   2077 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
   2078     IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
   2079 #else
   2080     GImGui = ctx;
   2081 #endif
   2082 }
   2083 
   2084 ImGuiContext* ImGui::CreateContext(void* (*malloc_fn)(size_t), void (*free_fn)(void*))
   2085 {
   2086     if (!malloc_fn) malloc_fn = malloc;
   2087     ImGuiContext* ctx = (ImGuiContext*)malloc_fn(sizeof(ImGuiContext));
   2088     IM_PLACEMENT_NEW(ctx) ImGuiContext();
   2089     ctx->IO.MemAllocFn = malloc_fn;
   2090     ctx->IO.MemFreeFn = free_fn ? free_fn : free;
   2091     return ctx;
   2092 }
   2093 
   2094 void ImGui::DestroyContext(ImGuiContext* ctx)
   2095 {
   2096     void (*free_fn)(void*) = ctx->IO.MemFreeFn;
   2097     ctx->~ImGuiContext();
   2098     free_fn(ctx);
   2099     if (GImGui == ctx)
   2100         SetCurrentContext(NULL);
   2101 }
   2102 
   2103 ImGuiIO& ImGui::GetIO()
   2104 {
   2105     return GImGui->IO;
   2106 }
   2107 
   2108 ImGuiStyle& ImGui::GetStyle()
   2109 {
   2110     return GImGui->Style;
   2111 }
   2112 
   2113 // Same value as passed to your RenderDrawListsFn() function. valid after Render() and until the next call to NewFrame()
   2114 ImDrawData* ImGui::GetDrawData()
   2115 {
   2116     return GImGui->RenderDrawData.Valid ? &GImGui->RenderDrawData : NULL;
   2117 }
   2118 
   2119 float ImGui::GetTime()
   2120 {
   2121     return GImGui->Time;
   2122 }
   2123 
   2124 int ImGui::GetFrameCount()
   2125 {
   2126     return GImGui->FrameCount;
   2127 }
   2128 
   2129 void ImGui::NewFrame()
   2130 {
   2131     ImGuiContext& g = *GImGui;
   2132 
   2133     // Check user data
   2134     IM_ASSERT(g.IO.DeltaTime >= 0.0f);               // Need a positive DeltaTime (zero is tolerated but will cause some timing issues)
   2135     IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f);
   2136     IM_ASSERT(g.IO.Fonts->Fonts.Size > 0);           // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
   2137     IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded());     // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
   2138     IM_ASSERT(g.Style.CurveTessellationTol > 0.0f);  // Invalid style setting
   2139 
   2140     if (!g.Initialized)
   2141     {
   2142         // Initialize on first frame
   2143         g.LogClipboard = (ImGuiTextBuffer*)ImGui::MemAlloc(sizeof(ImGuiTextBuffer));
   2144         IM_PLACEMENT_NEW(g.LogClipboard) ImGuiTextBuffer();
   2145 
   2146         IM_ASSERT(g.Settings.empty());
   2147         LoadIniSettingsFromDisk(g.IO.IniFilename);
   2148         g.Initialized = true;
   2149     }
   2150 
   2151     SetCurrentFont(GetDefaultFont());
   2152     IM_ASSERT(g.Font->IsLoaded());
   2153 
   2154     g.Time += g.IO.DeltaTime;
   2155     g.FrameCount += 1;
   2156     g.Tooltip[0] = '\0';
   2157     g.OverlayDrawList.Clear();
   2158     g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID);
   2159     g.OverlayDrawList.PushClipRectFullScreen();
   2160 
   2161     // Mark rendering data as invalid to prevent user who may have a handle on it to use it
   2162     g.RenderDrawData.Valid = false;
   2163     g.RenderDrawData.CmdLists = NULL;
   2164     g.RenderDrawData.CmdListsCount = g.RenderDrawData.TotalVtxCount = g.RenderDrawData.TotalIdxCount = 0;
   2165 
   2166     // Update inputs state
   2167     if (g.IO.MousePos.x < 0 && g.IO.MousePos.y < 0)
   2168         g.IO.MousePos = ImVec2(-9999.0f, -9999.0f);
   2169     if ((g.IO.MousePos.x < 0 && g.IO.MousePos.y < 0) || (g.IO.MousePosPrev.x < 0 && g.IO.MousePosPrev.y < 0))   // if mouse just appeared or disappeared (negative coordinate) we cancel out movement in MouseDelta
   2170         g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
   2171     else
   2172         g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
   2173     g.IO.MousePosPrev = g.IO.MousePos;
   2174     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
   2175     {
   2176         g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
   2177         g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
   2178         g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
   2179         g.IO.MouseDownDuration[i] = g.IO.MouseDown[i] ? (g.IO.MouseDownDuration[i] < 0.0f ? 0.0f : g.IO.MouseDownDuration[i] + g.IO.DeltaTime) : -1.0f;
   2180         g.IO.MouseDoubleClicked[i] = false;
   2181         if (g.IO.MouseClicked[i])
   2182         {
   2183             if (g.Time - g.IO.MouseClickedTime[i] < g.IO.MouseDoubleClickTime)
   2184             {
   2185                 if (ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i]) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
   2186                     g.IO.MouseDoubleClicked[i] = true;
   2187                 g.IO.MouseClickedTime[i] = -FLT_MAX;    // so the third click isn't turned into a double-click
   2188             }
   2189             else
   2190             {
   2191                 g.IO.MouseClickedTime[i] = g.Time;
   2192             }
   2193             g.IO.MouseClickedPos[i] = g.IO.MousePos;
   2194             g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
   2195         }
   2196         else if (g.IO.MouseDown[i])
   2197         {
   2198             g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i]));
   2199         }
   2200     }
   2201     memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
   2202     for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
   2203         g.IO.KeysDownDuration[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownDuration[i] < 0.0f ? 0.0f : g.IO.KeysDownDuration[i] + g.IO.DeltaTime) : -1.0f;
   2204 
   2205     // Calculate frame-rate for the user, as a purely luxurious feature
   2206     g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
   2207     g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
   2208     g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
   2209     g.IO.Framerate = 1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame));
   2210 
   2211     // Clear reference to active widget if the widget isn't alive anymore
   2212     g.HoveredIdPreviousFrame = g.HoveredId;
   2213     g.HoveredId = 0;
   2214     g.HoveredIdAllowOverlap = false;
   2215     if (!g.ActiveIdIsAlive && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
   2216         ClearActiveID();
   2217     g.ActiveIdPreviousFrame = g.ActiveId;
   2218     g.ActiveIdIsAlive = false;
   2219     g.ActiveIdIsJustActivated = false;
   2220 
   2221     // Handle user moving window (at the beginning of the frame to avoid input lag or sheering). Only valid for root windows.
   2222     if (g.MovedWindowMoveId && g.MovedWindowMoveId == g.ActiveId)
   2223     {
   2224         KeepAliveID(g.MovedWindowMoveId);
   2225         IM_ASSERT(g.MovedWindow && g.MovedWindow->RootWindow);
   2226         IM_ASSERT(g.MovedWindow->RootWindow->MoveId == g.MovedWindowMoveId);
   2227         if (g.IO.MouseDown[0])
   2228         {
   2229             if (!(g.MovedWindow->Flags & ImGuiWindowFlags_NoMove))
   2230             {
   2231                 g.MovedWindow->PosFloat += g.IO.MouseDelta;
   2232                 if (!(g.MovedWindow->Flags & ImGuiWindowFlags_NoSavedSettings) && (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f))
   2233                     MarkIniSettingsDirty();
   2234             }
   2235             FocusWindow(g.MovedWindow);
   2236         }
   2237         else
   2238         {
   2239             ClearActiveID();
   2240             g.MovedWindow = NULL;
   2241             g.MovedWindowMoveId = 0;
   2242         }
   2243     }
   2244     else
   2245     {
   2246         g.MovedWindow = NULL;
   2247         g.MovedWindowMoveId = 0;
   2248     }
   2249 
   2250     // Delay saving settings so we don't spam disk too much
   2251     if (g.SettingsDirtyTimer > 0.0f)
   2252     {
   2253         g.SettingsDirtyTimer -= g.IO.DeltaTime;
   2254         if (g.SettingsDirtyTimer <= 0.0f)
   2255             SaveIniSettingsToDisk(g.IO.IniFilename);
   2256     }
   2257 
   2258     // Find the window we are hovering. Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow
   2259     g.HoveredWindow = g.MovedWindow ? g.MovedWindow : FindHoveredWindow(g.IO.MousePos, false);
   2260     if (g.HoveredWindow && (g.HoveredWindow->Flags & ImGuiWindowFlags_ChildWindow))
   2261         g.HoveredRootWindow = g.HoveredWindow->RootWindow;
   2262     else
   2263         g.HoveredRootWindow = g.MovedWindow ? g.MovedWindow->RootWindow : FindHoveredWindow(g.IO.MousePos, true);
   2264 
   2265     if (ImGuiWindow* modal_window = GetFrontMostModalRootWindow())
   2266     {
   2267         g.ModalWindowDarkeningRatio = ImMin(g.ModalWindowDarkeningRatio + g.IO.DeltaTime * 6.0f, 1.0f);
   2268         ImGuiWindow* window = g.HoveredRootWindow;
   2269         while (window && window != modal_window)
   2270             window = window->ParentWindow;
   2271         if (!window)
   2272             g.HoveredRootWindow = g.HoveredWindow = NULL;
   2273     }
   2274     else
   2275     {
   2276         g.ModalWindowDarkeningRatio = 0.0f;
   2277     }
   2278 
   2279     // Are we using inputs? Tell user so they can capture/discard the inputs away from the rest of their application.
   2280     // When clicking outside of a window we assume the click is owned by the application and won't request capture. We need to track click ownership.
   2281     int mouse_earliest_button_down = -1;
   2282     bool mouse_any_down = false;
   2283     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
   2284     {
   2285         if (g.IO.MouseClicked[i])
   2286             g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty());
   2287         mouse_any_down |= g.IO.MouseDown[i];
   2288         if (g.IO.MouseDown[i])
   2289             if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[mouse_earliest_button_down] > g.IO.MouseClickedTime[i])
   2290                 mouse_earliest_button_down = i;
   2291     }
   2292     bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
   2293     if (g.CaptureMouseNextFrame != -1)
   2294         g.IO.WantCaptureMouse = (g.CaptureMouseNextFrame != 0);
   2295     else
   2296         g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.ActiveId != 0) || (!g.OpenPopupStack.empty());
   2297     g.IO.WantCaptureKeyboard = (g.CaptureKeyboardNextFrame != -1) ? (g.CaptureKeyboardNextFrame != 0) : (g.ActiveId != 0);
   2298     g.IO.WantTextInput = (g.ActiveId != 0 && g.InputTextState.Id == g.ActiveId);
   2299     g.MouseCursor = ImGuiMouseCursor_Arrow;
   2300     g.CaptureMouseNextFrame = g.CaptureKeyboardNextFrame = -1;
   2301     g.OsImePosRequest = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
   2302 
   2303     // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
   2304     if (!mouse_avail_to_imgui)
   2305         g.HoveredWindow = g.HoveredRootWindow = NULL;
   2306 
   2307     // Scale & Scrolling
   2308     if (g.HoveredWindow && g.IO.MouseWheel != 0.0f && !g.HoveredWindow->Collapsed)
   2309     {
   2310         ImGuiWindow* window = g.HoveredWindow;
   2311         if (g.IO.KeyCtrl)
   2312         {
   2313             if (g.IO.FontAllowUserScaling)
   2314             {
   2315                 // Zoom / Scale window
   2316                 float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
   2317                 float scale = new_font_scale / window->FontWindowScale;
   2318                 window->FontWindowScale = new_font_scale;
   2319 
   2320                 const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
   2321                 window->Pos += offset;
   2322                 window->PosFloat += offset;
   2323                 window->Size *= scale;
   2324                 window->SizeFull *= scale;
   2325             }
   2326         }
   2327         else if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse))
   2328         {
   2329             // Scroll
   2330             const int scroll_lines = (window->Flags & ImGuiWindowFlags_ComboBox) ? 3 : 5;
   2331             SetWindowScrollY(window, window->Scroll.y - g.IO.MouseWheel * window->CalcFontSize() * scroll_lines);
   2332         }
   2333     }
   2334 
   2335     // Pressing TAB activate widget focus
   2336     // NB: Don't discard FocusedWindow if it isn't active, so that a window that go on/off programatically won't lose its keyboard focus.
   2337     if (g.ActiveId == 0 && g.FocusedWindow != NULL && g.FocusedWindow->Active && IsKeyPressedMap(ImGuiKey_Tab, false))
   2338         g.FocusedWindow->FocusIdxTabRequestNext = 0;
   2339 
   2340     // Mark all windows as not visible
   2341     for (int i = 0; i != g.Windows.Size; i++)
   2342     {
   2343         ImGuiWindow* window = g.Windows[i];
   2344         window->WasActive = window->Active;
   2345         window->Active = false;
   2346         window->Accessed = false;
   2347     }
   2348 
   2349     // Closing the focused window restore focus to the first active root window in descending z-order
   2350     if (g.FocusedWindow && !g.FocusedWindow->WasActive)
   2351         for (int i = g.Windows.Size-1; i >= 0; i--)
   2352             if (g.Windows[i]->WasActive && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow))
   2353             {
   2354                 FocusWindow(g.Windows[i]);
   2355                 break;
   2356             }
   2357 
   2358     // No window should be open at the beginning of the frame.
   2359     // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
   2360     g.CurrentWindowStack.resize(0);
   2361     g.CurrentPopupStack.resize(0);
   2362     CloseInactivePopups();
   2363 
   2364     // Create implicit window - we will only render it if the user has added something to it.
   2365     ImGui::SetNextWindowSize(ImVec2(400,400), ImGuiSetCond_FirstUseEver);
   2366     ImGui::Begin("Debug");
   2367 }
   2368 
   2369 // NB: behavior of ImGui after Shutdown() is not tested/guaranteed at the moment. This function is merely here to free heap allocations.
   2370 void ImGui::Shutdown()
   2371 {
   2372     ImGuiContext& g = *GImGui;
   2373 
   2374     // 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)
   2375     if (g.IO.Fonts) // Testing for NULL to allow user to NULLify in case of running Shutdown() on multiple contexts. Bit hacky.
   2376         g.IO.Fonts->Clear();
   2377 
   2378     // Cleanup of other data are conditional on actually having used ImGui.
   2379     if (!g.Initialized)
   2380         return;
   2381 
   2382     SaveIniSettingsToDisk(g.IO.IniFilename);
   2383 
   2384     for (int i = 0; i < g.Windows.Size; i++)
   2385     {
   2386         g.Windows[i]->~ImGuiWindow();
   2387         ImGui::MemFree(g.Windows[i]);
   2388     }
   2389     g.Windows.clear();
   2390     g.WindowsSortBuffer.clear();
   2391     g.CurrentWindow = NULL;
   2392     g.CurrentWindowStack.clear();
   2393     g.FocusedWindow = NULL;
   2394     g.HoveredWindow = NULL;
   2395     g.HoveredRootWindow = NULL;
   2396     g.ActiveIdWindow = NULL;
   2397     g.MovedWindow = NULL;
   2398     for (int i = 0; i < g.Settings.Size; i++)
   2399         ImGui::MemFree(g.Settings[i].Name);
   2400     g.Settings.clear();
   2401     g.ColorModifiers.clear();
   2402     g.StyleModifiers.clear();
   2403     g.FontStack.clear();
   2404     g.OpenPopupStack.clear();
   2405     g.CurrentPopupStack.clear();
   2406     g.SetNextWindowSizeConstraintCallback = NULL;
   2407     g.SetNextWindowSizeConstraintCallbackUserData = NULL;
   2408     for (int i = 0; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
   2409         g.RenderDrawLists[i].clear();
   2410     g.OverlayDrawList.ClearFreeMemory();
   2411     g.ColorEditModeStorage.Clear();
   2412     if (g.PrivateClipboard)
   2413     {
   2414         ImGui::MemFree(g.PrivateClipboard);
   2415         g.PrivateClipboard = NULL;
   2416     }
   2417     g.InputTextState.Text.clear();
   2418     g.InputTextState.InitialText.clear();
   2419     g.InputTextState.TempTextBuffer.clear();
   2420 
   2421     if (g.LogFile && g.LogFile != stdout)
   2422     {
   2423         fclose(g.LogFile);
   2424         g.LogFile = NULL;
   2425     }
   2426     if (g.LogClipboard)
   2427     {
   2428         g.LogClipboard->~ImGuiTextBuffer();
   2429         ImGui::MemFree(g.LogClipboard);
   2430     }
   2431 
   2432     g.Initialized = false;
   2433 }
   2434 
   2435 static ImGuiIniData* FindWindowSettings(const char* name)
   2436 {
   2437     ImGuiContext& g = *GImGui;
   2438     ImGuiID id = ImHash(name, 0);
   2439     for (int i = 0; i != g.Settings.Size; i++)
   2440     {
   2441         ImGuiIniData* ini = &g.Settings[i];
   2442         if (ini->Id == id)
   2443             return ini;
   2444     }
   2445     return NULL;
   2446 }
   2447 
   2448 static ImGuiIniData* AddWindowSettings(const char* name)
   2449 {
   2450     GImGui->Settings.resize(GImGui->Settings.Size + 1);
   2451     ImGuiIniData* ini = &GImGui->Settings.back();
   2452     ini->Name = ImStrdup(name);
   2453     ini->Id = ImHash(name, 0);
   2454     ini->Collapsed = false;
   2455     ini->Pos = ImVec2(FLT_MAX,FLT_MAX);
   2456     ini->Size = ImVec2(0,0);
   2457     return ini;
   2458 }
   2459 
   2460 // Zero-tolerance, poor-man .ini parsing
   2461 // FIXME: Write something less rubbish
   2462 static void LoadIniSettingsFromDisk(const char* ini_filename)
   2463 {
   2464     ImGuiContext& g = *GImGui;
   2465     if (!ini_filename)
   2466         return;
   2467 
   2468     int file_size;
   2469     char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_size, 1);
   2470     if (!file_data)
   2471         return;
   2472 
   2473     ImGuiIniData* settings = NULL;
   2474     const char* buf_end = file_data + file_size;
   2475     for (const char* line_start = file_data; line_start < buf_end; )
   2476     {
   2477         const char* line_end = line_start;
   2478         while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
   2479             line_end++;
   2480 
   2481         if (line_start[0] == '[' && line_end > line_start && line_end[-1] == ']')
   2482         {
   2483             char name[64];
   2484             ImFormatString(name, IM_ARRAYSIZE(name), "%.*s", (int)(line_end-line_start-2), line_start+1);
   2485             settings = FindWindowSettings(name);
   2486             if (!settings)
   2487                 settings = AddWindowSettings(name);
   2488         }
   2489         else if (settings)
   2490         {
   2491             float x, y;
   2492             int i;
   2493             if (sscanf(line_start, "Pos=%f,%f", &x, &y) == 2)
   2494                 settings->Pos = ImVec2(x, y);
   2495             else if (sscanf(line_start, "Size=%f,%f", &x, &y) == 2)
   2496                 settings->Size = ImMax(ImVec2(x, y), g.Style.WindowMinSize);
   2497             else if (sscanf(line_start, "Collapsed=%d", &i) == 1)
   2498                 settings->Collapsed = (i != 0);
   2499         }
   2500 
   2501         line_start = line_end+1;
   2502     }
   2503 
   2504     ImGui::MemFree(file_data);
   2505 }
   2506 
   2507 static void SaveIniSettingsToDisk(const char* ini_filename)
   2508 {
   2509     ImGuiContext& g = *GImGui;
   2510     g.SettingsDirtyTimer = 0.0f;
   2511     if (!ini_filename)
   2512         return;
   2513 
   2514     // Gather data from windows that were active during this session
   2515     for (int i = 0; i != g.Windows.Size; i++)
   2516     {
   2517         ImGuiWindow* window = g.Windows[i];
   2518         if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
   2519             continue;
   2520         ImGuiIniData* settings = FindWindowSettings(window->Name);
   2521         settings->Pos = window->Pos;
   2522         settings->Size = window->SizeFull;
   2523         settings->Collapsed = window->Collapsed;
   2524     }
   2525 
   2526     // Write .ini file
   2527     // If a window wasn't opened in this session we preserve its settings
   2528     FILE* f = ImFileOpen(ini_filename, "wt");
   2529     if (!f)
   2530         return;
   2531     for (int i = 0; i != g.Settings.Size; i++)
   2532     {
   2533         const ImGuiIniData* settings = &g.Settings[i];
   2534         if (settings->Pos.x == FLT_MAX)
   2535             continue;
   2536         const char* name = settings->Name;
   2537         if (const char* p = strstr(name, "###"))  // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
   2538             name = p;
   2539         fprintf(f, "[%s]\n", name);
   2540         fprintf(f, "Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y);
   2541         fprintf(f, "Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y);
   2542         fprintf(f, "Collapsed=%d\n", settings->Collapsed);
   2543         fprintf(f, "\n");
   2544     }
   2545 
   2546     fclose(f);
   2547 }
   2548 
   2549 static void MarkIniSettingsDirty()
   2550 {
   2551     ImGuiContext& g = *GImGui;
   2552     if (g.SettingsDirtyTimer <= 0.0f)
   2553         g.SettingsDirtyTimer = g.IO.IniSavingRate;
   2554 }
   2555 
   2556 // FIXME: Add a more explicit sort order in the window structure.
   2557 static int ChildWindowComparer(const void* lhs, const void* rhs)
   2558 {
   2559     const ImGuiWindow* a = *(const ImGuiWindow**)lhs;
   2560     const ImGuiWindow* b = *(const ImGuiWindow**)rhs;
   2561     if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
   2562         return d;
   2563     if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
   2564         return d;
   2565     if (int d = (a->Flags & ImGuiWindowFlags_ComboBox) - (b->Flags & ImGuiWindowFlags_ComboBox))
   2566         return d;
   2567     return (a->IndexWithinParent - b->IndexWithinParent);
   2568 }
   2569 
   2570 static void AddWindowToSortedBuffer(ImVector<ImGuiWindow*>& out_sorted_windows, ImGuiWindow* window)
   2571 {
   2572     out_sorted_windows.push_back(window);
   2573     if (window->Active)
   2574     {
   2575         int count = window->DC.ChildWindows.Size;
   2576         if (count > 1)
   2577             qsort(window->DC.ChildWindows.begin(), (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
   2578         for (int i = 0; i < count; i++)
   2579         {
   2580             ImGuiWindow* child = window->DC.ChildWindows[i];
   2581             if (child->Active)
   2582                 AddWindowToSortedBuffer(out_sorted_windows, child);
   2583         }
   2584     }
   2585 }
   2586 
   2587 static void AddDrawListToRenderList(ImVector<ImDrawList*>& out_render_list, ImDrawList* draw_list)
   2588 {
   2589     if (draw_list->CmdBuffer.empty())
   2590         return;
   2591 
   2592     // Remove trailing command if unused
   2593     ImDrawCmd& last_cmd = draw_list->CmdBuffer.back();
   2594     if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL)
   2595     {
   2596         draw_list->CmdBuffer.pop_back();
   2597         if (draw_list->CmdBuffer.empty())
   2598             return;
   2599     }
   2600 
   2601     // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
   2602     IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
   2603     IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
   2604     IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
   2605 
   2606     // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = 2 bytes = 64K vertices)
   2607     // If this assert triggers because you are drawing lots of stuff manually, A) workaround by calling BeginChild()/EndChild() to put your draw commands in multiple draw lists, B) #define ImDrawIdx to a 'unsigned int' in imconfig.h and render accordingly.
   2608     IM_ASSERT((int64_t)draw_list->_VtxCurrentIdx <= ((int64_t)1L << (sizeof(ImDrawIdx)*8)));  // Too many vertices in same ImDrawList. See comment above.
   2609     
   2610     out_render_list.push_back(draw_list);
   2611     GImGui->IO.MetricsRenderVertices += draw_list->VtxBuffer.Size;
   2612     GImGui->IO.MetricsRenderIndices += draw_list->IdxBuffer.Size;
   2613 }
   2614 
   2615 static void AddWindowToRenderList(ImVector<ImDrawList*>& out_render_list, ImGuiWindow* window)
   2616 {
   2617     AddDrawListToRenderList(out_render_list, window->DrawList);
   2618     for (int i = 0; i < window->DC.ChildWindows.Size; i++)
   2619     {
   2620         ImGuiWindow* child = window->DC.ChildWindows[i];
   2621         if (!child->Active) // clipped children may have been marked not active
   2622             continue;
   2623         if ((child->Flags & ImGuiWindowFlags_Popup) && child->HiddenFrames > 0)
   2624             continue;
   2625         AddWindowToRenderList(out_render_list, child);
   2626     }
   2627 }
   2628 
   2629 // When using this function it is sane to ensure that float are perfectly rounded to integer values, to that e.g. (int)(max.x-min.x) in user's render produce correct result.
   2630 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
   2631 {
   2632     ImGuiWindow* window = GetCurrentWindow();
   2633     window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
   2634     window->ClipRect = window->DrawList->_ClipRectStack.back();
   2635 }
   2636 
   2637 void ImGui::PopClipRect()
   2638 {
   2639     ImGuiWindow* window = GetCurrentWindow();
   2640     window->DrawList->PopClipRect();
   2641     window->ClipRect = window->DrawList->_ClipRectStack.back();
   2642 }
   2643 
   2644 // 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.
   2645 void ImGui::EndFrame()
   2646 {
   2647     ImGuiContext& g = *GImGui;
   2648     IM_ASSERT(g.Initialized);                       // Forgot to call ImGui::NewFrame()
   2649     IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // ImGui::EndFrame() called multiple times, or forgot to call ImGui::NewFrame() again
   2650 
   2651     // Render tooltip
   2652     if (g.Tooltip[0])
   2653     {
   2654         ImGui::BeginTooltip();
   2655         ImGui::TextUnformatted(g.Tooltip);
   2656         ImGui::EndTooltip();
   2657     }
   2658 
   2659     // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
   2660     if (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.OsImePosRequest - g.OsImePosSet) > 0.0001f)
   2661     {
   2662         g.IO.ImeSetInputScreenPosFn((int)g.OsImePosRequest.x, (int)g.OsImePosRequest.y);
   2663         g.OsImePosSet = g.OsImePosRequest;
   2664     }
   2665 
   2666     // Hide implicit "Debug" window if it hasn't been used
   2667     IM_ASSERT(g.CurrentWindowStack.Size == 1);    // Mismatched Begin()/End() calls
   2668     if (g.CurrentWindow && !g.CurrentWindow->Accessed)
   2669         g.CurrentWindow->Active = false;
   2670     ImGui::End();
   2671 
   2672     // Click to focus window and start moving (after we're done with all our widgets)
   2673     if (g.ActiveId == 0 && g.HoveredId == 0 && g.IO.MouseClicked[0])
   2674     {
   2675         if (!(g.FocusedWindow && !g.FocusedWindow->WasActive && g.FocusedWindow->Active)) // Unless we just made a popup appear
   2676         {
   2677             if (g.HoveredRootWindow != NULL)
   2678             {
   2679                 FocusWindow(g.HoveredWindow);
   2680                 if (!(g.HoveredWindow->Flags & ImGuiWindowFlags_NoMove))
   2681                 {
   2682                     g.MovedWindow = g.HoveredWindow;
   2683                     g.MovedWindowMoveId = g.HoveredRootWindow->MoveId;
   2684                     SetActiveID(g.MovedWindowMoveId, g.HoveredRootWindow);
   2685                 }
   2686             }
   2687             else if (g.FocusedWindow != NULL && GetFrontMostModalRootWindow() == NULL)
   2688             {
   2689                 // Clicking on void disable focus
   2690                 FocusWindow(NULL);
   2691             }
   2692         }
   2693     }
   2694 
   2695     // Sort the window list so that all child windows are after their parent
   2696     // We cannot do that on FocusWindow() because childs may not exist yet
   2697     g.WindowsSortBuffer.resize(0);
   2698     g.WindowsSortBuffer.reserve(g.Windows.Size);
   2699     for (int i = 0; i != g.Windows.Size; i++)
   2700     {
   2701         ImGuiWindow* window = g.Windows[i];
   2702         if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow))       // if a child is active its parent will add it
   2703             continue;
   2704         AddWindowToSortedBuffer(g.WindowsSortBuffer, window);
   2705     }
   2706     IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size);  // we done something wrong
   2707     g.Windows.swap(g.WindowsSortBuffer);
   2708 
   2709     // Clear Input data for next frame
   2710     g.IO.MouseWheel = 0.0f;
   2711     memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters));
   2712 
   2713     g.FrameCountEnded = g.FrameCount;
   2714 }
   2715 
   2716 void ImGui::Render()
   2717 {
   2718     ImGuiContext& g = *GImGui;
   2719     IM_ASSERT(g.Initialized);   // Forgot to call ImGui::NewFrame()
   2720 
   2721     if (g.FrameCountEnded != g.FrameCount)
   2722         ImGui::EndFrame();
   2723     g.FrameCountRendered = g.FrameCount;
   2724 
   2725     // Skip render altogether if alpha is 0.0
   2726     // Note that vertex buffers have been created and are wasted, so it is best practice that you don't create windows in the first place, or consistently respond to Begin() returning false.
   2727     if (g.Style.Alpha > 0.0f)
   2728     {
   2729         // Gather windows to render
   2730         g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsActiveWindows = 0;
   2731         for (int i = 0; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
   2732             g.RenderDrawLists[i].resize(0);
   2733         for (int i = 0; i != g.Windows.Size; i++)
   2734         {
   2735             ImGuiWindow* window = g.Windows[i];
   2736             if (window->Active && window->HiddenFrames <= 0 && (window->Flags & (ImGuiWindowFlags_ChildWindow)) == 0)
   2737             {
   2738                 // FIXME: Generalize this with a proper layering system so e.g. user can draw in specific layers, below text, ..
   2739                 g.IO.MetricsActiveWindows++;
   2740                 if (window->Flags & ImGuiWindowFlags_Popup)
   2741                     AddWindowToRenderList(g.RenderDrawLists[1], window);
   2742                 else if (window->Flags & ImGuiWindowFlags_Tooltip)
   2743                     AddWindowToRenderList(g.RenderDrawLists[2], window);
   2744                 else
   2745                     AddWindowToRenderList(g.RenderDrawLists[0], window);
   2746             }
   2747         }
   2748 
   2749         // Flatten layers
   2750         int n = g.RenderDrawLists[0].Size;
   2751         int flattened_size = n;
   2752         for (int i = 1; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
   2753             flattened_size += g.RenderDrawLists[i].Size;
   2754         g.RenderDrawLists[0].resize(flattened_size);
   2755         for (int i = 1; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
   2756         {
   2757             ImVector<ImDrawList*>& layer = g.RenderDrawLists[i];
   2758             if (layer.empty())
   2759                 continue;
   2760             memcpy(&g.RenderDrawLists[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
   2761             n += layer.Size;
   2762         }
   2763 
   2764         // Draw software mouse cursor if requested
   2765         if (g.IO.MouseDrawCursor)
   2766         {
   2767             const ImGuiMouseCursorData& cursor_data = g.MouseCursorData[g.MouseCursor];
   2768             const ImVec2 pos = g.IO.MousePos - cursor_data.HotOffset;
   2769             const ImVec2 size = cursor_data.Size;
   2770             const ImTextureID tex_id = g.IO.Fonts->TexID;
   2771             g.OverlayDrawList.PushTextureID(tex_id);
   2772             g.OverlayDrawList.AddImage(tex_id, pos+ImVec2(1,0), pos+ImVec2(1,0) + size, cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], IM_COL32(0,0,0,48));        // Shadow
   2773             g.OverlayDrawList.AddImage(tex_id, pos+ImVec2(2,0), pos+ImVec2(2,0) + size, cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], IM_COL32(0,0,0,48));        // Shadow
   2774             g.OverlayDrawList.AddImage(tex_id, pos,             pos + size,             cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], IM_COL32(0,0,0,255));       // Black border
   2775             g.OverlayDrawList.AddImage(tex_id, pos,             pos + size,             cursor_data.TexUvMin[0], cursor_data.TexUvMax[0], IM_COL32(255,255,255,255)); // White fill
   2776             g.OverlayDrawList.PopTextureID();
   2777         }
   2778         if (!g.OverlayDrawList.VtxBuffer.empty())
   2779             AddDrawListToRenderList(g.RenderDrawLists[0], &g.OverlayDrawList);
   2780 
   2781         // Setup draw data
   2782         g.RenderDrawData.Valid = true;
   2783         g.RenderDrawData.CmdLists = (g.RenderDrawLists[0].Size > 0) ? &g.RenderDrawLists[0][0] : NULL;
   2784         g.RenderDrawData.CmdListsCount = g.RenderDrawLists[0].Size;
   2785         g.RenderDrawData.TotalVtxCount = g.IO.MetricsRenderVertices;
   2786         g.RenderDrawData.TotalIdxCount = g.IO.MetricsRenderIndices;
   2787 
   2788         // Render. If user hasn't set a callback then they may retrieve the draw data via GetDrawData()
   2789         if (g.RenderDrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)
   2790             g.IO.RenderDrawListsFn(&g.RenderDrawData);
   2791     }
   2792 }
   2793 
   2794 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
   2795 {
   2796     const char* text_display_end = text;
   2797     if (!text_end)
   2798         text_end = (const char*)-1;
   2799 
   2800     while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
   2801         text_display_end++;
   2802     return text_display_end;
   2803 }
   2804 
   2805 // Pass text data straight to log (without being displayed)
   2806 void ImGui::LogText(const char* fmt, ...)
   2807 {
   2808     ImGuiContext& g = *GImGui;
   2809     if (!g.LogEnabled)
   2810         return;
   2811 
   2812     va_list args;
   2813     va_start(args, fmt);
   2814     if (g.LogFile)
   2815     {
   2816         vfprintf(g.LogFile, fmt, args);
   2817     }
   2818     else
   2819     {
   2820         g.LogClipboard->appendv(fmt, args);
   2821     }
   2822     va_end(args);
   2823 }
   2824 
   2825 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
   2826 // We split text into individual lines to add current tree level padding
   2827 static void LogRenderedText(const ImVec2& ref_pos, const char* text, const char* text_end)
   2828 {
   2829     ImGuiContext& g = *GImGui;
   2830     ImGuiWindow* window = ImGui::GetCurrentWindowRead();
   2831 
   2832     if (!text_end)
   2833         text_end = ImGui::FindRenderedTextEnd(text, text_end);
   2834 
   2835     const bool log_new_line = ref_pos.y > window->DC.LogLinePosY+1;
   2836     window->DC.LogLinePosY = ref_pos.y;
   2837 
   2838     const char* text_remaining = text;
   2839     if (g.LogStartDepth > window->DC.TreeDepth)  // Re-adjust padding if we have popped out of our starting depth
   2840         g.LogStartDepth = window->DC.TreeDepth;
   2841     const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth);
   2842     for (;;)
   2843     {
   2844         // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
   2845         const char* line_end = text_remaining;
   2846         while (line_end < text_end)
   2847             if (*line_end == '\n')
   2848                 break;
   2849             else
   2850                 line_end++;
   2851         if (line_end >= text_end)
   2852             line_end = NULL;
   2853 
   2854         const bool is_first_line = (text == text_remaining);
   2855         bool is_last_line = false;
   2856         if (line_end == NULL)
   2857         {
   2858             is_last_line = true;
   2859             line_end = text_end;
   2860         }
   2861         if (line_end != NULL && !(is_last_line && (line_end - text_remaining)==0))
   2862         {
   2863             const int char_count = (int)(line_end - text_remaining);
   2864             if (log_new_line || !is_first_line)
   2865                 ImGui::LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, text_remaining);
   2866             else
   2867                 ImGui::LogText(" %.*s", char_count, text_remaining);
   2868         }
   2869 
   2870         if (is_last_line)
   2871             break;
   2872         text_remaining = line_end + 1;
   2873     }
   2874 }
   2875 
   2876 // Internal ImGui functions to render text
   2877 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
   2878 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
   2879 {
   2880     ImGuiContext& g = *GImGui;
   2881     ImGuiWindow* window = GetCurrentWindow();
   2882 
   2883     // Hide anything after a '##' string
   2884     const char* text_display_end;
   2885     if (hide_text_after_hash)
   2886     {
   2887         text_display_end = FindRenderedTextEnd(text, text_end);
   2888     }
   2889     else
   2890     {
   2891         if (!text_end)
   2892             text_end = text + strlen(text); // FIXME-OPT
   2893         text_display_end = text_end;
   2894     }
   2895 
   2896     const int text_len = (int)(text_display_end - text);
   2897     if (text_len > 0)
   2898     {
   2899         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
   2900         if (g.LogEnabled)
   2901             LogRenderedText(pos, text, text_display_end);
   2902     }
   2903 }
   2904 
   2905 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
   2906 {
   2907     ImGuiContext& g = *GImGui;
   2908     ImGuiWindow* window = GetCurrentWindow();
   2909 
   2910     if (!text_end)
   2911         text_end = text + strlen(text); // FIXME-OPT
   2912 
   2913     const int text_len = (int)(text_end - text);
   2914     if (text_len > 0)
   2915     {
   2916         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
   2917         if (g.LogEnabled)
   2918             LogRenderedText(pos, text, text_end);
   2919     }
   2920 }
   2921 
   2922 // Default clip_rect uses (pos_min,pos_max)
   2923 // Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
   2924 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)
   2925 {
   2926     // Hide anything after a '##' string
   2927     const char* text_display_end = FindRenderedTextEnd(text, text_end);
   2928     const int text_len = (int)(text_display_end - text);
   2929     if (text_len == 0)
   2930         return;
   2931 
   2932     ImGuiContext& g = *GImGui;
   2933     ImGuiWindow* window = GetCurrentWindow();
   2934 
   2935     // Perform CPU side clipping for single clipped element to avoid using scissor state
   2936     ImVec2 pos = pos_min;
   2937     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
   2938 
   2939     const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
   2940     const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
   2941     bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
   2942     if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
   2943         need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
   2944 
   2945     // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
   2946     if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
   2947     if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
   2948 
   2949     // Render
   2950     if (need_clipping)
   2951     {
   2952         ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
   2953         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
   2954     }
   2955     else
   2956     {
   2957         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
   2958     }
   2959     if (g.LogEnabled)
   2960         LogRenderedText(pos, text, text_display_end);
   2961 }
   2962 
   2963 // Render a rectangle shaped with optional rounding and borders
   2964 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
   2965 {
   2966     ImGuiWindow* window = GetCurrentWindow();
   2967 
   2968     window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
   2969     if (border && (window->Flags & ImGuiWindowFlags_ShowBorders))
   2970     {
   2971         window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding);
   2972         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding);
   2973     }
   2974 }
   2975 
   2976 // Render a triangle to denote expanded/collapsed state
   2977 void ImGui::RenderCollapseTriangle(ImVec2 p_min, bool is_open, float scale)
   2978 {
   2979     ImGuiContext& g = *GImGui;
   2980     ImGuiWindow* window = GetCurrentWindow();
   2981 
   2982     const float h = g.FontSize * 1.00f;
   2983     const float r = h * 0.40f * scale;
   2984     ImVec2 center = p_min + ImVec2(h*0.50f, h*0.50f*scale);
   2985 
   2986     ImVec2 a, b, c;
   2987     if (is_open)
   2988     {
   2989         center.y -= r*0.25f;
   2990         a = center + ImVec2(0,1)*r;
   2991         b = center + ImVec2(-0.866f,-0.5f)*r;
   2992         c = center + ImVec2(0.866f,-0.5f)*r;
   2993     }
   2994     else
   2995     {
   2996         a = center + ImVec2(1,0)*r;
   2997         b = center + ImVec2(-0.500f,0.866f)*r;
   2998         c = center + ImVec2(-0.500f,-0.866f)*r;
   2999     }
   3000 
   3001     window->DrawList->AddTriangleFilled(a, b, c, GetColorU32(ImGuiCol_Text));
   3002 }
   3003 
   3004 void ImGui::RenderBullet(ImVec2 pos)
   3005 {
   3006     ImGuiWindow* window = GetCurrentWindow();
   3007     window->DrawList->AddCircleFilled(pos, GImGui->FontSize*0.20f, GetColorU32(ImGuiCol_Text), 8);
   3008 }
   3009 
   3010 void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col)
   3011 {
   3012     ImGuiContext& g = *GImGui;
   3013     ImGuiWindow* window = GetCurrentWindow();
   3014 
   3015     ImVec2 a, b, c;
   3016     float start_x = (float)(int)(g.FontSize * 0.307f + 0.5f);
   3017     float rem_third = (float)(int)((g.FontSize - start_x) / 3.0f);
   3018     a.x = pos.x + 0.5f + start_x;
   3019     b.x = a.x + rem_third;
   3020     c.x = a.x + rem_third * 3.0f;
   3021     b.y = pos.y - 1.0f + (float)(int)(g.Font->Ascent * (g.FontSize / g.Font->FontSize) + 0.5f) + (float)(int)(g.Font->DisplayOffset.y);
   3022     a.y = b.y - rem_third;
   3023     c.y = b.y - rem_third * 2.0f;
   3024 
   3025     window->DrawList->PathLineTo(a);
   3026     window->DrawList->PathLineTo(b);
   3027     window->DrawList->PathLineTo(c);
   3028     window->DrawList->PathStroke(col, false);
   3029 }
   3030 
   3031 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
   3032 // CalcTextSize("") should return ImVec2(0.0f, GImGui->FontSize)
   3033 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
   3034 {
   3035     ImGuiContext& g = *GImGui;
   3036 
   3037     const char* text_display_end;
   3038     if (hide_text_after_double_hash)
   3039         text_display_end = FindRenderedTextEnd(text, text_end);      // Hide anything after a '##' string
   3040     else
   3041         text_display_end = text_end;
   3042 
   3043     ImFont* font = g.Font;
   3044     const float font_size = g.FontSize;
   3045     if (text == text_display_end)
   3046         return ImVec2(0.0f, font_size);
   3047     ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
   3048 
   3049     // Cancel out character spacing for the last character of a line (it is baked into glyph->XAdvance field)
   3050     const float font_scale = font_size / font->FontSize;
   3051     const float character_spacing_x = 1.0f * font_scale;
   3052     if (text_size.x > 0.0f)
   3053         text_size.x -= character_spacing_x;
   3054     text_size.x = (float)(int)(text_size.x + 0.95f);
   3055 
   3056     return text_size;
   3057 }
   3058 
   3059 // Helper to calculate coarse clipping of large list of evenly sized items.
   3060 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
   3061 // NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX
   3062 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
   3063 {
   3064     ImGuiContext& g = *GImGui;
   3065     ImGuiWindow* window = GetCurrentWindowRead();
   3066     if (g.LogEnabled)
   3067     {
   3068         // If logging is active, do not perform any clipping
   3069         *out_items_display_start = 0;
   3070         *out_items_display_end = items_count;
   3071         return;
   3072     }
   3073     if (window->SkipItems)
   3074     {
   3075         *out_items_display_start = *out_items_display_end = 0;
   3076         return;
   3077     }
   3078 
   3079     const ImVec2 pos = window->DC.CursorPos;
   3080     int start = (int)((window->ClipRect.Min.y - pos.y) / items_height);
   3081     int end = (int)((window->ClipRect.Max.y - pos.y) / items_height);
   3082     start = ImClamp(start, 0, items_count);
   3083     end = ImClamp(end + 1, start, items_count);
   3084     *out_items_display_start = start;
   3085     *out_items_display_end = end;
   3086 }
   3087 
   3088 // Find window given position, search front-to-back
   3089 // FIXME: Note that we have a lag here because WindowRectClipped is updated in Begin() so windows moved by user via SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is called, aka before the next Begin(). Moving window thankfully isn't affected.
   3090 static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs)
   3091 {
   3092     ImGuiContext& g = *GImGui;
   3093     for (int i = g.Windows.Size-1; i >= 0; i--)
   3094     {
   3095         ImGuiWindow* window = g.Windows[i];
   3096         if (!window->Active)
   3097             continue;
   3098         if (window->Flags & ImGuiWindowFlags_NoInputs)
   3099             continue;
   3100         if (excluding_childs && (window->Flags & ImGuiWindowFlags_ChildWindow) != 0)
   3101             continue;
   3102 
   3103         // Using the clipped AABB so a child window will typically be clipped by its parent.
   3104         ImRect bb(window->WindowRectClipped.Min - g.Style.TouchExtraPadding, window->WindowRectClipped.Max + g.Style.TouchExtraPadding);
   3105         if (bb.Contains(pos))
   3106             return window;
   3107     }
   3108     return NULL;
   3109 }
   3110 
   3111 // Test if mouse cursor is hovering given rectangle
   3112 // NB- Rectangle is clipped by our current clip setting
   3113 // NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
   3114 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
   3115 {
   3116     ImGuiContext& g = *GImGui;
   3117     ImGuiWindow* window = GetCurrentWindowRead();
   3118 
   3119     // Clip
   3120     ImRect rect_clipped(r_min, r_max);
   3121     if (clip)
   3122         rect_clipped.Clip(window->ClipRect);
   3123 
   3124     // Expand for touch input
   3125     const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
   3126     return rect_for_touch.Contains(g.IO.MousePos);
   3127 }
   3128 
   3129 bool ImGui::IsMouseHoveringWindow()
   3130 {
   3131     ImGuiContext& g = *GImGui;
   3132     return g.HoveredWindow == g.CurrentWindow;
   3133 }
   3134 
   3135 bool ImGui::IsMouseHoveringAnyWindow()
   3136 {
   3137     ImGuiContext& g = *GImGui;
   3138     return g.HoveredWindow != NULL;
   3139 }
   3140 
   3141 bool ImGui::IsPosHoveringAnyWindow(const ImVec2& pos)
   3142 {
   3143     return FindHoveredWindow(pos, false) != NULL;
   3144 }
   3145 
   3146 static bool IsKeyPressedMap(ImGuiKey key, bool repeat)
   3147 {
   3148     const int key_index = GImGui->IO.KeyMap[key];
   3149     return ImGui::IsKeyPressed(key_index, repeat);
   3150 }
   3151 
   3152 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
   3153 {
   3154     IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
   3155     return GImGui->IO.KeyMap[imgui_key];
   3156 }
   3157 
   3158 // Note that imgui doesn't know the semantic of each entry of io.KeyDown[]. Use your own indices/enums according to how your backend/engine stored them into KeyDown[]!
   3159 bool ImGui::IsKeyDown(int user_key_index)
   3160 {
   3161     if (user_key_index < 0) return false;
   3162     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(GImGui->IO.KeysDown));
   3163     return GImGui->IO.KeysDown[user_key_index];
   3164 }
   3165 
   3166 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
   3167 {
   3168     ImGuiContext& g = *GImGui;
   3169     if (user_key_index < 0) return false;
   3170     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
   3171     const float t = g.IO.KeysDownDuration[user_key_index];
   3172     if (t == 0.0f)
   3173         return true;
   3174 
   3175     if (repeat && t > g.IO.KeyRepeatDelay)
   3176     {
   3177         float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate;
   3178         if ((fmodf(t - delay, rate) > rate*0.5f) != (fmodf(t - delay - g.IO.DeltaTime, rate) > rate*0.5f))
   3179             return true;
   3180     }
   3181     return false;
   3182 }
   3183 
   3184 bool ImGui::IsKeyReleased(int user_key_index)
   3185 {
   3186     ImGuiContext& g = *GImGui;
   3187     if (user_key_index < 0) return false;
   3188     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
   3189     if (g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index])
   3190         return true;
   3191     return false;
   3192 }
   3193 
   3194 bool ImGui::IsMouseDown(int button)
   3195 {
   3196     ImGuiContext& g = *GImGui;
   3197     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
   3198     return g.IO.MouseDown[button];
   3199 }
   3200 
   3201 bool ImGui::IsMouseClicked(int button, bool repeat)
   3202 {
   3203     ImGuiContext& g = *GImGui;
   3204     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
   3205     const float t = g.IO.MouseDownDuration[button];
   3206     if (t == 0.0f)
   3207         return true;
   3208 
   3209     if (repeat && t > g.IO.KeyRepeatDelay)
   3210     {
   3211         float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate;
   3212         if ((fmodf(t - delay, rate) > rate*0.5f) != (fmodf(t - delay - g.IO.DeltaTime, rate) > rate*0.5f))
   3213             return true;
   3214     }
   3215 
   3216     return false;
   3217 }
   3218 
   3219 bool ImGui::IsMouseReleased(int button)
   3220 {
   3221     ImGuiContext& g = *GImGui;
   3222     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
   3223     return g.IO.MouseReleased[button];
   3224 }
   3225 
   3226 bool ImGui::IsMouseDoubleClicked(int button)
   3227 {
   3228     ImGuiContext& g = *GImGui;
   3229     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
   3230     return g.IO.MouseDoubleClicked[button];
   3231 }
   3232 
   3233 bool ImGui::IsMouseDragging(int button, float lock_threshold)
   3234 {
   3235     ImGuiContext& g = *GImGui;
   3236     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
   3237     if (!g.IO.MouseDown[button])
   3238         return false;
   3239     if (lock_threshold < 0.0f)
   3240         lock_threshold = g.IO.MouseDragThreshold;
   3241     return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
   3242 }
   3243 
   3244 ImVec2 ImGui::GetMousePos()
   3245 {
   3246     return GImGui->IO.MousePos;
   3247 }
   3248 
   3249 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
   3250 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
   3251 {
   3252     ImGuiContext& g = *GImGui;
   3253     if (g.CurrentPopupStack.Size > 0)
   3254         return g.OpenPopupStack[g.CurrentPopupStack.Size-1].MousePosOnOpen;
   3255     return g.IO.MousePos;
   3256 }
   3257 
   3258 ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold)
   3259 {
   3260     ImGuiContext& g = *GImGui;
   3261     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
   3262     if (lock_threshold < 0.0f)
   3263         lock_threshold = g.IO.MouseDragThreshold;
   3264     if (g.IO.MouseDown[button])
   3265         if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
   3266             return g.IO.MousePos - g.IO.MouseClickedPos[button];     // Assume we can only get active with left-mouse button (at the moment).
   3267     return ImVec2(0.0f, 0.0f);
   3268 }
   3269 
   3270 void ImGui::ResetMouseDragDelta(int button)
   3271 {
   3272     ImGuiContext& g = *GImGui;
   3273     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
   3274     // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
   3275     g.IO.MouseClickedPos[button] = g.IO.MousePos;
   3276 }
   3277 
   3278 ImGuiMouseCursor ImGui::GetMouseCursor()
   3279 {
   3280     return GImGui->MouseCursor;
   3281 }
   3282 
   3283 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
   3284 {
   3285     GImGui->MouseCursor = cursor_type;
   3286 }
   3287 
   3288 void ImGui::CaptureKeyboardFromApp(bool capture)
   3289 {
   3290     GImGui->CaptureKeyboardNextFrame = capture ? 1 : 0;
   3291 }
   3292 
   3293 void ImGui::CaptureMouseFromApp(bool capture)
   3294 {
   3295     GImGui->CaptureMouseNextFrame = capture ? 1 : 0;
   3296 }
   3297 
   3298 bool ImGui::IsItemHovered()
   3299 {
   3300     ImGuiWindow* window = GetCurrentWindowRead();
   3301     return window->DC.LastItemHoveredAndUsable;
   3302 }
   3303 
   3304 bool ImGui::IsItemHoveredRect()
   3305 {
   3306     ImGuiWindow* window = GetCurrentWindowRead();
   3307     return window->DC.LastItemHoveredRect;
   3308 }
   3309 
   3310 bool ImGui::IsItemActive()
   3311 {
   3312     ImGuiContext& g = *GImGui;
   3313     if (g.ActiveId)
   3314     {
   3315         ImGuiWindow* window = GetCurrentWindowRead();
   3316         return g.ActiveId == window->DC.LastItemId;
   3317     }
   3318     return false;
   3319 }
   3320 
   3321 bool ImGui::IsItemClicked(int mouse_button)
   3322 {
   3323     return IsMouseClicked(mouse_button) && IsItemHovered();
   3324 }
   3325 
   3326 bool ImGui::IsAnyItemHovered()
   3327 {
   3328     return GImGui->HoveredId != 0 || GImGui->HoveredIdPreviousFrame != 0;
   3329 }
   3330 
   3331 bool ImGui::IsAnyItemActive()
   3332 {
   3333     return GImGui->ActiveId != 0;
   3334 }
   3335 
   3336 bool ImGui::IsItemVisible()
   3337 {
   3338     ImGuiWindow* window = GetCurrentWindowRead();
   3339     ImRect r(window->ClipRect);
   3340     return r.Overlaps(window->DC.LastItemRect);
   3341 }
   3342 
   3343 // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
   3344 void ImGui::SetItemAllowOverlap()
   3345 {
   3346     ImGuiContext& g = *GImGui;
   3347     if (g.HoveredId == g.CurrentWindow->DC.LastItemId)
   3348         g.HoveredIdAllowOverlap = true;
   3349     if (g.ActiveId == g.CurrentWindow->DC.LastItemId)
   3350         g.ActiveIdAllowOverlap = true;
   3351 }
   3352 
   3353 ImVec2 ImGui::GetItemRectMin()
   3354 {
   3355     ImGuiWindow* window = GetCurrentWindowRead();
   3356     return window->DC.LastItemRect.Min;
   3357 }
   3358 
   3359 ImVec2 ImGui::GetItemRectMax()
   3360 {
   3361     ImGuiWindow* window = GetCurrentWindowRead();
   3362     return window->DC.LastItemRect.Max;
   3363 }
   3364 
   3365 ImVec2 ImGui::GetItemRectSize()
   3366 {
   3367     ImGuiWindow* window = GetCurrentWindowRead();
   3368     return window->DC.LastItemRect.GetSize();
   3369 }
   3370 
   3371 ImVec2 ImGui::CalcItemRectClosestPoint(const ImVec2& pos, bool on_edge, float outward)
   3372 {
   3373     ImGuiWindow* window = GetCurrentWindowRead();
   3374     ImRect rect = window->DC.LastItemRect;
   3375     rect.Expand(outward);
   3376     return rect.GetClosestPoint(pos, on_edge);
   3377 }
   3378 
   3379 // Tooltip is stored and turned into a BeginTooltip()/EndTooltip() sequence at the end of the frame. Each call override previous value.
   3380 void ImGui::SetTooltipV(const char* fmt, va_list args)
   3381 {
   3382     ImGuiContext& g = *GImGui;
   3383     ImFormatStringV(g.Tooltip, IM_ARRAYSIZE(g.Tooltip), fmt, args);
   3384 }
   3385 
   3386 void ImGui::SetTooltip(const char* fmt, ...)
   3387 {
   3388     va_list args;
   3389     va_start(args, fmt);
   3390     SetTooltipV(fmt, args);
   3391     va_end(args);
   3392 }
   3393 
   3394 static ImRect GetVisibleRect()
   3395 {
   3396     ImGuiContext& g = *GImGui;
   3397     if (g.IO.DisplayVisibleMin.x != g.IO.DisplayVisibleMax.x && g.IO.DisplayVisibleMin.y != g.IO.DisplayVisibleMax.y)
   3398         return ImRect(g.IO.DisplayVisibleMin, g.IO.DisplayVisibleMax);
   3399     return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
   3400 }
   3401 
   3402 void ImGui::BeginTooltip()
   3403 {
   3404     ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
   3405     ImGui::Begin("##Tooltip", NULL, flags);
   3406 }
   3407 
   3408 void ImGui::EndTooltip()
   3409 {
   3410     IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip);   // Mismatched BeginTooltip()/EndTooltip() calls
   3411     ImGui::End();
   3412 }
   3413 
   3414 static bool IsPopupOpen(ImGuiID id)
   3415 {
   3416     ImGuiContext& g = *GImGui;
   3417     return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == id;
   3418 }
   3419 
   3420 // Mark popup as open (toggle toward open state).
   3421 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
   3422 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
   3423 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
   3424 void ImGui::OpenPopupEx(const char* str_id, bool reopen_existing)
   3425 {
   3426     ImGuiContext& g = *GImGui;
   3427     ImGuiWindow* window = g.CurrentWindow;
   3428     ImGuiID id = window->GetID(str_id);
   3429     int current_stack_size = g.CurrentPopupStack.Size;
   3430     ImGuiPopupRef popup_ref = ImGuiPopupRef(id, window, window->GetID("##menus"), g.IO.MousePos); // Tagged as new ref because constructor sets Window to NULL (we are passing the ParentWindow info here)
   3431     if (g.OpenPopupStack.Size < current_stack_size + 1)
   3432         g.OpenPopupStack.push_back(popup_ref);
   3433     else if (reopen_existing || g.OpenPopupStack[current_stack_size].PopupId != id)
   3434     {
   3435         g.OpenPopupStack.resize(current_stack_size+1);
   3436         g.OpenPopupStack[current_stack_size] = popup_ref;
   3437     }
   3438 }
   3439 
   3440 void ImGui::OpenPopup(const char* str_id)
   3441 {
   3442     ImGui::OpenPopupEx(str_id, false);
   3443 }
   3444 
   3445 static void CloseInactivePopups()
   3446 {
   3447     ImGuiContext& g = *GImGui;
   3448     if (g.OpenPopupStack.empty())
   3449         return;
   3450 
   3451     // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
   3452     // Don't close our own child popup windows
   3453     int n = 0;
   3454     if (g.FocusedWindow)
   3455     {
   3456         for (n = 0; n < g.OpenPopupStack.Size; n++)
   3457         {
   3458             ImGuiPopupRef& popup = g.OpenPopupStack[n];
   3459             if (!popup.Window)
   3460                 continue;
   3461             IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
   3462             if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
   3463                 continue;
   3464 
   3465             bool has_focus = false;
   3466             for (int m = n; m < g.OpenPopupStack.Size && !has_focus; m++)
   3467                 has_focus = (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == g.FocusedWindow->RootWindow);
   3468             if (!has_focus)
   3469                 break;
   3470         }
   3471     }
   3472     if (n < g.OpenPopupStack.Size)   // This test is not required but it allows to set a useful breakpoint on the line below
   3473         g.OpenPopupStack.resize(n);
   3474 }
   3475 
   3476 static ImGuiWindow* GetFrontMostModalRootWindow()
   3477 {
   3478     ImGuiContext& g = *GImGui;
   3479     for (int n = g.OpenPopupStack.Size-1; n >= 0; n--)
   3480         if (ImGuiWindow* front_most_popup = g.OpenPopupStack.Data[n].Window)
   3481             if (front_most_popup->Flags & ImGuiWindowFlags_Modal)
   3482                 return front_most_popup;
   3483     return NULL;
   3484 }
   3485 
   3486 static void ClosePopupToLevel(int remaining)
   3487 {
   3488     ImGuiContext& g = *GImGui;
   3489     if (remaining > 0)
   3490         ImGui::FocusWindow(g.OpenPopupStack[remaining-1].Window);
   3491     else
   3492         ImGui::FocusWindow(g.OpenPopupStack[0].ParentWindow);
   3493     g.OpenPopupStack.resize(remaining);
   3494 }
   3495 
   3496 static void ClosePopup(ImGuiID id)
   3497 {
   3498     if (!IsPopupOpen(id))
   3499         return;
   3500     ImGuiContext& g = *GImGui;
   3501     ClosePopupToLevel(g.OpenPopupStack.Size - 1);
   3502 }
   3503 
   3504 // Close the popup we have begin-ed into.
   3505 void ImGui::CloseCurrentPopup()
   3506 {
   3507     ImGuiContext& g = *GImGui;
   3508     int popup_idx = g.CurrentPopupStack.Size - 1;
   3509     if (popup_idx < 0 || popup_idx > g.OpenPopupStack.Size || g.CurrentPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
   3510         return;
   3511     while (popup_idx > 0 && g.OpenPopupStack[popup_idx].Window && (g.OpenPopupStack[popup_idx].Window->Flags & ImGuiWindowFlags_ChildMenu))
   3512         popup_idx--;
   3513     ClosePopupToLevel(popup_idx);
   3514 }
   3515 
   3516 static inline void ClearSetNextWindowData()
   3517 {
   3518     ImGuiContext& g = *GImGui;
   3519     g.SetNextWindowPosCond = g.SetNextWindowSizeCond = g.SetNextWindowContentSizeCond = g.SetNextWindowCollapsedCond = 0;
   3520     g.SetNextWindowSizeConstraint = g.SetNextWindowFocus = false;
   3521 }
   3522 
   3523 static bool BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_flags)
   3524 {
   3525     ImGuiContext& g = *GImGui;
   3526     ImGuiWindow* window = g.CurrentWindow;
   3527     const ImGuiID id = window->GetID(str_id);
   3528     if (!IsPopupOpen(id))
   3529     {
   3530         ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
   3531         return false;
   3532     }
   3533 
   3534     ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
   3535     ImGuiWindowFlags flags = extra_flags|ImGuiWindowFlags_Popup|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
   3536 
   3537     char name[20];
   3538     if (flags & ImGuiWindowFlags_ChildMenu)
   3539         ImFormatString(name, IM_ARRAYSIZE(name), "##menu_%d", g.CurrentPopupStack.Size);    // Recycle windows based on depth
   3540     else
   3541         ImFormatString(name, IM_ARRAYSIZE(name), "##popup_%08x", id); // Not recycling, so we can close/open during the same frame
   3542 
   3543     bool is_open = ImGui::Begin(name, NULL, flags);
   3544     if (!(window->Flags & ImGuiWindowFlags_ShowBorders))
   3545         g.CurrentWindow->Flags &= ~ImGuiWindowFlags_ShowBorders;
   3546     if (!is_open) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
   3547         ImGui::EndPopup();
   3548 
   3549     return is_open;
   3550 }
   3551 
   3552 bool ImGui::BeginPopup(const char* str_id)
   3553 {
   3554     if (GImGui->OpenPopupStack.Size <= GImGui->CurrentPopupStack.Size)	// Early out for performance
   3555     {
   3556         ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
   3557         return false;
   3558     }
   3559     return BeginPopupEx(str_id, ImGuiWindowFlags_ShowBorders);
   3560 }
   3561 
   3562 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags extra_flags)
   3563 {
   3564     ImGuiContext& g = *GImGui;
   3565     ImGuiWindow* window = g.CurrentWindow;
   3566     const ImGuiID id = window->GetID(name);
   3567     if (!IsPopupOpen(id))
   3568     {
   3569         ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
   3570         return false;
   3571     }
   3572 
   3573     ImGuiWindowFlags flags = extra_flags|ImGuiWindowFlags_Popup|ImGuiWindowFlags_Modal|ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoSavedSettings;
   3574     bool is_open = ImGui::Begin(name, p_open, flags);
   3575     if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
   3576     {
   3577         ImGui::EndPopup();
   3578         if (is_open)
   3579             ClosePopup(id);
   3580         return false;
   3581     }
   3582 
   3583     return is_open;
   3584 }
   3585 
   3586 void ImGui::EndPopup()
   3587 {
   3588     ImGuiWindow* window = GetCurrentWindow();
   3589     IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup);  // Mismatched BeginPopup()/EndPopup() calls
   3590     IM_ASSERT(GImGui->CurrentPopupStack.Size > 0);
   3591     ImGui::End();
   3592     if (!(window->Flags & ImGuiWindowFlags_Modal))
   3593         ImGui::PopStyleVar();
   3594 }
   3595 
   3596 // This is a helper to handle the most simple case of associating one named popup to one given widget.
   3597 // 1. If you have many possible popups (for different "instances" of a same widget, or for wholly different widgets), you may be better off handling
   3598 //    this yourself so you can store data relative to the widget that opened the popup instead of choosing different popup identifiers.
   3599 // 2. If you want right-clicking on the same item to reopen the popup at new location, use the same code replacing IsItemHovered() with IsItemHoveredRect()
   3600 //    and passing true to the OpenPopupEx().
   3601 //    Because: hovering an item in a window below the popup won't normally trigger is hovering behavior/coloring. The pattern of ignoring the fact that
   3602 //    the item isn't interactable (because it is blocked by the active popup) may useful in some situation when e.g. large canvas as one item, content of menu
   3603 //    driven by click position.
   3604 bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button)
   3605 {
   3606     if (IsItemHovered() && IsMouseClicked(mouse_button))
   3607         OpenPopupEx(str_id, false);
   3608     return BeginPopup(str_id);
   3609 }
   3610 
   3611 bool ImGui::BeginPopupContextWindow(bool also_over_items, const char* str_id, int mouse_button)
   3612 {
   3613     if (!str_id) str_id = "window_context_menu";
   3614     if (IsMouseHoveringWindow() && IsMouseClicked(mouse_button))
   3615         if (also_over_items || !IsAnyItemHovered())
   3616             OpenPopupEx(str_id, true);
   3617     return BeginPopup(str_id);
   3618 }
   3619 
   3620 bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
   3621 {
   3622     if (!str_id) str_id = "void_context_menu";
   3623     if (!IsMouseHoveringAnyWindow() && IsMouseClicked(mouse_button))
   3624         OpenPopupEx(str_id, true);
   3625     return BeginPopup(str_id);
   3626 }
   3627 
   3628 static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
   3629 {
   3630     ImGuiWindow* window = ImGui::GetCurrentWindow();
   3631     ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
   3632 
   3633     const ImVec2 content_avail = ImGui::GetContentRegionAvail();
   3634     ImVec2 size = ImFloor(size_arg);
   3635     if (size.x <= 0.0f)
   3636     {
   3637         if (size.x == 0.0f)
   3638             flags |= ImGuiWindowFlags_ChildWindowAutoFitX;
   3639         size.x = ImMax(content_avail.x, 4.0f) - fabsf(size.x); // Arbitrary minimum zero-ish child size of 4.0f (0.0f causing too much issues)
   3640     }
   3641     if (size.y <= 0.0f)
   3642     {
   3643         if (size.y == 0.0f)
   3644             flags |= ImGuiWindowFlags_ChildWindowAutoFitY;
   3645         size.y = ImMax(content_avail.y, 4.0f) - fabsf(size.y);
   3646     }
   3647     if (border)
   3648         flags |= ImGuiWindowFlags_ShowBorders;
   3649     flags |= extra_flags;
   3650 
   3651     char title[256];
   3652     if (name)
   3653         ImFormatString(title, IM_ARRAYSIZE(title), "%s.%s.%08X", window->Name, name, id);
   3654     else
   3655         ImFormatString(title, IM_ARRAYSIZE(title), "%s.%08X", window->Name, id);
   3656 
   3657     bool ret = ImGui::Begin(title, NULL, size, -1.0f, flags);
   3658 
   3659     if (!(window->Flags & ImGuiWindowFlags_ShowBorders))
   3660         ImGui::GetCurrentWindow()->Flags &= ~ImGuiWindowFlags_ShowBorders;
   3661 
   3662     return ret;
   3663 }
   3664 
   3665 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
   3666 {
   3667     ImGuiWindow* window = GetCurrentWindow();
   3668     return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
   3669 }
   3670 
   3671 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
   3672 {
   3673     return BeginChildEx(NULL, id, size_arg, border, extra_flags);
   3674 }
   3675 
   3676 void ImGui::EndChild()
   3677 {
   3678     ImGuiWindow* window = GetCurrentWindow();
   3679 
   3680     IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow);   // Mismatched BeginChild()/EndChild() callss
   3681     if ((window->Flags & ImGuiWindowFlags_ComboBox) || window->BeginCount > 1)
   3682     {
   3683         ImGui::End();
   3684     }
   3685     else
   3686     {
   3687         // When using auto-filling child window, we don't provide full width/height to ItemSize so that it doesn't feed back into automatic size-fitting.
   3688         ImVec2 sz = GetWindowSize();
   3689         if (window->Flags & ImGuiWindowFlags_ChildWindowAutoFitX) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
   3690             sz.x = ImMax(4.0f, sz.x);
   3691         if (window->Flags & ImGuiWindowFlags_ChildWindowAutoFitY)
   3692             sz.y = ImMax(4.0f, sz.y);
   3693 
   3694         ImGui::End();
   3695 
   3696         window = GetCurrentWindow();
   3697         ImRect bb(window->DC.CursorPos, window->DC.CursorPos + sz);
   3698         ItemSize(sz);
   3699         ItemAdd(bb, NULL);
   3700     }
   3701 }
   3702 
   3703 // Helper to create a child window / scrolling region that looks like a normal widget frame.
   3704 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
   3705 {
   3706     ImGuiContext& g = *GImGui;
   3707     const ImGuiStyle& style = g.Style;
   3708     ImGui::PushStyleColor(ImGuiCol_ChildWindowBg, style.Colors[ImGuiCol_FrameBg]);
   3709     ImGui::PushStyleVar(ImGuiStyleVar_ChildWindowRounding, style.FrameRounding);
   3710     ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
   3711     return ImGui::BeginChild(id, size, (g.CurrentWindow->Flags & ImGuiWindowFlags_ShowBorders) ? true : false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
   3712 }
   3713 
   3714 void ImGui::EndChildFrame()
   3715 {
   3716     ImGui::EndChild();
   3717     ImGui::PopStyleVar(2);
   3718     ImGui::PopStyleColor();
   3719 }
   3720 
   3721 // Save and compare stack sizes on Begin()/End() to detect usage errors
   3722 static void CheckStacksSize(ImGuiWindow* window, bool write)
   3723 {
   3724     // NOT checking: DC.ItemWidth, DC.AllowKeyboardFocus, DC.ButtonRepeat, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin)
   3725     ImGuiContext& g = *GImGui;
   3726     int* p_backup = &window->DC.StackSizesBackup[0];
   3727     { int current = window->IDStack.Size;       if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushID/PopID or TreeNode/TreePop Mismatch!");   p_backup++; }    // Too few or too many PopID()/TreePop()
   3728     { int current = window->DC.GroupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginGroup/EndGroup Mismatch!");                p_backup++; }    // Too few or too many EndGroup()
   3729     { int current = g.CurrentPopupStack.Size;   if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch"); p_backup++;}// Too few or too many EndMenu()/EndPopup()
   3730     { int current = g.ColorModifiers.Size;      if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushStyleColor/PopStyleColor Mismatch!");       p_backup++; }    // Too few or too many PopStyleColor()
   3731     { int current = g.StyleModifiers.Size;      if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushStyleVar/PopStyleVar Mismatch!");           p_backup++; }    // Too few or too many PopStyleVar()
   3732     { int current = g.FontStack.Size;           if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushFont/PopFont Mismatch!");                   p_backup++; }    // Too few or too many PopFont()
   3733     IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
   3734 }
   3735 
   3736 static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& r_inner)
   3737 {
   3738     const ImGuiStyle& style = GImGui->Style;
   3739 
   3740     // Clamp into visible area while not overlapping the cursor. Safety padding is optional if our popup size won't fit without it.
   3741     ImVec2 safe_padding = style.DisplaySafeAreaPadding;
   3742     ImRect r_outer(GetVisibleRect());
   3743     r_outer.Reduce(ImVec2((size.x - r_outer.GetWidth() > safe_padding.x*2) ? safe_padding.x : 0.0f, (size.y - r_outer.GetHeight() > safe_padding.y*2) ? safe_padding.y : 0.0f));
   3744     ImVec2 base_pos_clamped = ImClamp(base_pos, r_outer.Min, r_outer.Max - size);
   3745 
   3746     for (int n = (*last_dir != -1) ? -1 : 0; n < 4; n++)   // Last, Right, down, up, left. (Favor last used direction).
   3747     {
   3748         const int dir = (n == -1) ? *last_dir : n;
   3749         ImRect rect(dir == 0 ? r_inner.Max.x : r_outer.Min.x, dir == 1 ? r_inner.Max.y : r_outer.Min.y, dir == 3 ? r_inner.Min.x : r_outer.Max.x, dir == 2 ? r_inner.Min.y : r_outer.Max.y);
   3750         if (rect.GetWidth() < size.x || rect.GetHeight() < size.y)
   3751             continue;
   3752         *last_dir = dir;
   3753         return ImVec2(dir == 0 ? r_inner.Max.x : dir == 3 ? r_inner.Min.x - size.x : base_pos_clamped.x, dir == 1 ? r_inner.Max.y : dir == 2 ? r_inner.Min.y - size.y : base_pos_clamped.y);
   3754     }
   3755 
   3756     // Fallback, try to keep within display
   3757     *last_dir = -1;
   3758     ImVec2 pos = base_pos;
   3759     pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
   3760     pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
   3761     return pos;
   3762 }
   3763 
   3764 ImGuiWindow* ImGui::FindWindowByName(const char* name)
   3765 {
   3766     // FIXME-OPT: Store sorted hashes -> pointers so we can do a bissection in a contiguous block
   3767     ImGuiContext& g = *GImGui;
   3768     ImGuiID id = ImHash(name, 0);
   3769     for (int i = 0; i < g.Windows.Size; i++)
   3770         if (g.Windows[i]->ID == id)
   3771             return g.Windows[i];
   3772     return NULL;
   3773 }
   3774 
   3775 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
   3776 {
   3777     ImGuiContext& g = *GImGui;
   3778 
   3779     // Create window the first time
   3780     ImGuiWindow* window = (ImGuiWindow*)ImGui::MemAlloc(sizeof(ImGuiWindow));
   3781     IM_PLACEMENT_NEW(window) ImGuiWindow(name);
   3782     window->Flags = flags;
   3783 
   3784     if (flags & ImGuiWindowFlags_NoSavedSettings)
   3785     {
   3786         // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
   3787         window->Size = window->SizeFull = size;
   3788     }
   3789     else
   3790     {
   3791         // Retrieve settings from .ini file
   3792         // Use SetWindowPos() or SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
   3793         window->PosFloat = ImVec2(60, 60);
   3794         window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
   3795 
   3796         ImGuiIniData* settings = FindWindowSettings(name);
   3797         if (!settings)
   3798         {
   3799             settings = AddWindowSettings(name);
   3800         }
   3801         else
   3802         {
   3803             window->SetWindowPosAllowFlags &= ~ImGuiSetCond_FirstUseEver;
   3804             window->SetWindowSizeAllowFlags &= ~ImGuiSetCond_FirstUseEver;
   3805             window->SetWindowCollapsedAllowFlags &= ~ImGuiSetCond_FirstUseEver;
   3806         }
   3807 
   3808         if (settings->Pos.x != FLT_MAX)
   3809         {
   3810             window->PosFloat = settings->Pos;
   3811             window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
   3812             window->Collapsed = settings->Collapsed;
   3813         }
   3814 
   3815         if (ImLengthSqr(settings->Size) > 0.00001f && !(flags & ImGuiWindowFlags_NoResize))
   3816             size = settings->Size;
   3817         window->Size = window->SizeFull = size;
   3818     }
   3819 
   3820     if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
   3821     {
   3822         window->AutoFitFramesX = window->AutoFitFramesY = 2;
   3823         window->AutoFitOnlyGrows = false;
   3824     }
   3825     else
   3826     {
   3827         if (window->Size.x <= 0.0f)
   3828             window->AutoFitFramesX = 2;
   3829         if (window->Size.y <= 0.0f)
   3830             window->AutoFitFramesY = 2;
   3831         window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
   3832     }
   3833 
   3834     if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
   3835         g.Windows.insert(g.Windows.begin(), window); // Quite slow but rare and only once
   3836     else
   3837         g.Windows.push_back(window);
   3838     return window;
   3839 }
   3840 
   3841 static void ApplySizeFullWithConstraint(ImGuiWindow* window, ImVec2 new_size)
   3842 {
   3843     ImGuiContext& g = *GImGui;
   3844     if (g.SetNextWindowSizeConstraint)
   3845     {
   3846         // Using -1,-1 on either X/Y axis to preserve the current size.
   3847         ImRect cr = g.SetNextWindowSizeConstraintRect;
   3848         new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
   3849         new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
   3850         if (g.SetNextWindowSizeConstraintCallback)
   3851         {
   3852             ImGuiSizeConstraintCallbackData data;
   3853             data.UserData = g.SetNextWindowSizeConstraintCallbackUserData;
   3854             data.Pos = window->Pos;
   3855             data.CurrentSize = window->SizeFull;
   3856             data.DesiredSize = new_size;
   3857             g.SetNextWindowSizeConstraintCallback(&data);
   3858             new_size = data.DesiredSize;
   3859         }
   3860     }
   3861     if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
   3862         new_size = ImMax(new_size, g.Style.WindowMinSize);
   3863     window->SizeFull = new_size;
   3864 }
   3865 
   3866 // Push a new ImGui window to add widgets to.
   3867 // - 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.
   3868 // - Begin/End can be called multiple times during the frame with the same window name to append content.
   3869 // - 'size_on_first_use' for a regular window denote the initial size for first-time creation (no saved data) and isn't that useful. Use SetNextWindowSize() prior to calling Begin() for more flexible window manipulation.
   3870 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
   3871 //   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.
   3872 // - 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.
   3873 // - 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.
   3874 // - Passing non-zero 'size' is roughly equivalent to calling SetNextWindowSize(size, ImGuiSetCond_FirstUseEver) prior to calling Begin().
   3875 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
   3876 {
   3877     return ImGui::Begin(name, p_open, ImVec2(0.f, 0.f), -1.0f, flags);
   3878 }
   3879 
   3880 bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_use, float bg_alpha, ImGuiWindowFlags flags)
   3881 {
   3882     ImGuiContext& g = *GImGui;
   3883     const ImGuiStyle& style = g.Style;
   3884     IM_ASSERT(name != NULL);                        // Window name required
   3885     IM_ASSERT(g.Initialized);                       // Forgot to call ImGui::NewFrame()
   3886     IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
   3887 
   3888     if (flags & ImGuiWindowFlags_NoInputs)
   3889         flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
   3890 
   3891     // Find or create
   3892     bool window_is_new = false;
   3893     ImGuiWindow* window = FindWindowByName(name);
   3894     if (!window)
   3895     {
   3896         window = CreateNewWindow(name, size_on_first_use, flags);
   3897         window_is_new = true;
   3898     }
   3899 
   3900     const int current_frame = ImGui::GetFrameCount();
   3901     const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
   3902     if (first_begin_of_the_frame)
   3903         window->Flags = (ImGuiWindowFlags)flags;
   3904     else
   3905         flags = window->Flags;
   3906 
   3907     // Add to stack
   3908     ImGuiWindow* parent_window = !g.CurrentWindowStack.empty() ? g.CurrentWindowStack.back() : NULL;
   3909     g.CurrentWindowStack.push_back(window);
   3910     SetCurrentWindow(window);
   3911     CheckStacksSize(window, true);
   3912     IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
   3913 
   3914     bool window_was_active = (window->LastFrameActive == current_frame - 1);   // Not using !WasActive because the implicit "Debug" window would always toggle off->on
   3915     if (flags & ImGuiWindowFlags_Popup)
   3916     {
   3917         ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size];
   3918         window_was_active &= (window->PopupId == popup_ref.PopupId);
   3919         window_was_active &= (window == popup_ref.Window);
   3920         popup_ref.Window = window;
   3921         g.CurrentPopupStack.push_back(popup_ref);
   3922         window->PopupId = popup_ref.PopupId;
   3923     }
   3924 
   3925     const bool window_appearing_after_being_hidden = (window->HiddenFrames == 1);
   3926 
   3927     // Process SetNextWindow***() calls
   3928     bool window_pos_set_by_api = false, window_size_set_by_api = false;
   3929     if (g.SetNextWindowPosCond)
   3930     {
   3931         const ImVec2 backup_cursor_pos = window->DC.CursorPos;                  // FIXME: not sure of the exact reason of this saving/restore anymore :( need to look into that.
   3932         if (!window_was_active || window_appearing_after_being_hidden) window->SetWindowPosAllowFlags |= ImGuiSetCond_Appearing;
   3933         window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.SetNextWindowPosCond) != 0;
   3934         if (window_pos_set_by_api && ImLengthSqr(g.SetNextWindowPosVal - ImVec2(-FLT_MAX,-FLT_MAX)) < 0.001f)
   3935         {
   3936             window->SetWindowPosCenterWanted = true;                            // May be processed on the next frame if this is our first frame and we are measuring size
   3937             window->SetWindowPosAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing);
   3938         }
   3939         else
   3940         {
   3941             SetWindowPos(window, g.SetNextWindowPosVal, g.SetNextWindowPosCond);
   3942         }
   3943         window->DC.CursorPos = backup_cursor_pos;
   3944         g.SetNextWindowPosCond = 0;
   3945     }
   3946     if (g.SetNextWindowSizeCond)
   3947     {
   3948         if (!window_was_active || window_appearing_after_being_hidden) window->SetWindowSizeAllowFlags |= ImGuiSetCond_Appearing;
   3949         window_size_set_by_api = (window->SetWindowSizeAllowFlags & g.SetNextWindowSizeCond) != 0;
   3950         SetWindowSize(window, g.SetNextWindowSizeVal, g.SetNextWindowSizeCond);
   3951         g.SetNextWindowSizeCond = 0;
   3952     }
   3953     if (g.SetNextWindowContentSizeCond)
   3954     {
   3955         window->SizeContentsExplicit = g.SetNextWindowContentSizeVal;
   3956         g.SetNextWindowContentSizeCond = 0;
   3957     }
   3958     else if (first_begin_of_the_frame)
   3959     {
   3960         window->SizeContentsExplicit = ImVec2(0.0f, 0.0f);
   3961     }
   3962     if (g.SetNextWindowCollapsedCond)
   3963     {
   3964         if (!window_was_active || window_appearing_after_being_hidden) window->SetWindowCollapsedAllowFlags |= ImGuiSetCond_Appearing;
   3965         SetWindowCollapsed(window, g.SetNextWindowCollapsedVal, g.SetNextWindowCollapsedCond);
   3966         g.SetNextWindowCollapsedCond = 0;
   3967     }
   3968     if (g.SetNextWindowFocus)
   3969     {
   3970         ImGui::SetWindowFocus();
   3971         g.SetNextWindowFocus = false;
   3972     }
   3973 
   3974     // Update known root window (if we are a child window, otherwise window == window->RootWindow)
   3975     int root_idx, root_non_popup_idx;
   3976     for (root_idx = g.CurrentWindowStack.Size - 1; root_idx > 0; root_idx--)
   3977         if (!(g.CurrentWindowStack[root_idx]->Flags & ImGuiWindowFlags_ChildWindow))
   3978             break;
   3979     for (root_non_popup_idx = root_idx; root_non_popup_idx > 0; root_non_popup_idx--)
   3980         if (!(g.CurrentWindowStack[root_non_popup_idx]->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
   3981             break;
   3982     window->ParentWindow = parent_window;
   3983     window->RootWindow = g.CurrentWindowStack[root_idx];
   3984     window->RootNonPopupWindow = g.CurrentWindowStack[root_non_popup_idx];      // This is merely for displaying the TitleBgActive color.
   3985 
   3986     // When reusing window again multiple times a frame, just append content (don't need to setup again)
   3987     if (first_begin_of_the_frame)
   3988     {
   3989         window->Active = true;
   3990         window->IndexWithinParent = 0;
   3991         window->BeginCount = 0;
   3992         window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX);
   3993         window->LastFrameActive = current_frame;
   3994         window->IDStack.resize(1);
   3995 
   3996         // Clear draw list, setup texture, outer clipping rectangle
   3997         window->DrawList->Clear();
   3998         window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
   3999         ImRect fullscreen_rect(GetVisibleRect());
   4000         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_ComboBox|ImGuiWindowFlags_Popup)))
   4001             PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true);
   4002         else
   4003             PushClipRect(fullscreen_rect.Min, fullscreen_rect.Max, true);
   4004 
   4005         if (!window_was_active)
   4006         {
   4007             // Popup first latch mouse position, will position itself when it appears next frame
   4008             window->AutoPosLastDirection = -1;
   4009             if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
   4010                 window->PosFloat = g.IO.MousePos;
   4011         }
   4012 
   4013         // Collapse window by double-clicking on title bar
   4014         // 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
   4015         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
   4016         {
   4017             ImRect title_bar_rect = window->TitleBarRect();
   4018             if (g.HoveredWindow == window && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
   4019             {
   4020                 window->Collapsed = !window->Collapsed;
   4021                 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
   4022                     MarkIniSettingsDirty();
   4023                 FocusWindow(window);
   4024             }
   4025         }
   4026         else
   4027         {
   4028             window->Collapsed = false;
   4029         }
   4030 
   4031         // SIZE
   4032 
   4033         // Save contents size from last frame for auto-fitting (unless explicitly specified)
   4034         window->SizeContents.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : ((window_is_new ? 0.0f : window->DC.CursorMaxPos.x - window->Pos.x) + window->Scroll.x));
   4035         window->SizeContents.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : ((window_is_new ? 0.0f : window->DC.CursorMaxPos.y - window->Pos.y) + window->Scroll.y));
   4036 
   4037         // Hide popup/tooltip window when first appearing while we measure size (because we recycle them)
   4038         if (window->HiddenFrames > 0)
   4039             window->HiddenFrames--;
   4040         if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0 && !window_was_active)
   4041         {
   4042             window->HiddenFrames = 1;
   4043             if (flags & ImGuiWindowFlags_AlwaysAutoResize)
   4044             {
   4045                 if (!window_size_set_by_api)
   4046                     window->Size = window->SizeFull = ImVec2(0.f, 0.f);
   4047                 window->SizeContents = ImVec2(0.f, 0.f);
   4048             }
   4049         }
   4050 
   4051         // Lock window padding so that altering the ShowBorders flag for children doesn't have side-effects.
   4052         window->WindowPadding = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_ShowBorders | ImGuiWindowFlags_ComboBox | ImGuiWindowFlags_Popup))) ? ImVec2(0,0) : style.WindowPadding;
   4053 
   4054         // Calculate auto-fit size
   4055         ImVec2 size_auto_fit;
   4056         if ((flags & ImGuiWindowFlags_Tooltip) != 0)
   4057         {
   4058             // Tooltip always resize. We keep the spacing symmetric on both axises for aesthetic purpose.
   4059             size_auto_fit = window->SizeContents + window->WindowPadding - ImVec2(0.0f, style.ItemSpacing.y);
   4060         }
   4061         else
   4062         {
   4063             size_auto_fit = ImClamp(window->SizeContents + window->WindowPadding, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - g.Style.DisplaySafeAreaPadding));
   4064 
   4065             // Handling case of auto fit window not fitting in screen on one axis, we are growing auto fit size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding.
   4066             if (size_auto_fit.x < window->SizeContents.x && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar))
   4067                 size_auto_fit.y += style.ScrollbarSize;
   4068             if (size_auto_fit.y < window->SizeContents.y && !(flags & ImGuiWindowFlags_NoScrollbar))
   4069                 size_auto_fit.x += style.ScrollbarSize;
   4070             size_auto_fit.y = ImMax(size_auto_fit.y - style.ItemSpacing.y, 0.0f);
   4071         }
   4072 
   4073         // Handle automatic resize
   4074         if (window->Collapsed)
   4075         {
   4076             // We still process initial auto-fit on collapsed windows to get a window width,
   4077             // But otherwise we don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
   4078             if (window->AutoFitFramesX > 0)
   4079                 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
   4080             if (window->AutoFitFramesY > 0)
   4081                 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
   4082         }
   4083         else
   4084         {
   4085             if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window_size_set_by_api)
   4086             {
   4087                 window->SizeFull = size_auto_fit;
   4088             }
   4089             else if ((window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) && !window_size_set_by_api)
   4090             {
   4091                 // Auto-fit only grows during the first few frames
   4092                 if (window->AutoFitFramesX > 0)
   4093                     window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
   4094                 if (window->AutoFitFramesY > 0)
   4095                     window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
   4096                 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
   4097                     MarkIniSettingsDirty();
   4098             }
   4099         }
   4100 
   4101         // Apply minimum/maximum window size constraints and final size
   4102         ApplySizeFullWithConstraint(window, window->SizeFull);
   4103         window->Size = window->Collapsed ? window->TitleBarRect().GetSize() : window->SizeFull;
   4104         
   4105         // POSITION
   4106 
   4107         // Position child window
   4108         if (flags & ImGuiWindowFlags_ChildWindow)
   4109         {
   4110             window->IndexWithinParent = parent_window->DC.ChildWindows.Size;
   4111             parent_window->DC.ChildWindows.push_back(window);
   4112         }
   4113         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup))
   4114         {
   4115             window->Pos = window->PosFloat = parent_window->DC.CursorPos;
   4116             window->Size = window->SizeFull = size_on_first_use; // NB: argument name 'size_on_first_use' misleading here, it's really just 'size' as provided by user passed via BeginChild()->Begin().
   4117         }
   4118 
   4119         bool window_pos_center = false;
   4120         window_pos_center |= (window->SetWindowPosCenterWanted && window->HiddenFrames == 0);
   4121         window_pos_center |= ((flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api && window_appearing_after_being_hidden);
   4122         if (window_pos_center)
   4123         {
   4124             // Center (any sort of window)
   4125             SetWindowPos(window, ImMax(style.DisplaySafeAreaPadding, fullscreen_rect.GetCenter() - window->SizeFull * 0.5f), 0);
   4126         }
   4127         else if (flags & ImGuiWindowFlags_ChildMenu)
   4128         {
   4129             // Child menus typically request _any_ position within the parent menu item, and then our FindBestPopupWindowPos() function will move the new menu outside the parent bounds.
   4130             // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
   4131             IM_ASSERT(window_pos_set_by_api);
   4132             float horizontal_overlap = style.ItemSpacing.x; // We want some overlap to convey the relative depth of each popup (currently the amount of overlap it is hard-coded to style.ItemSpacing.x, may need to introduce another style value).
   4133             ImRect rect_to_avoid;
   4134             if (parent_window->DC.MenuBarAppending)
   4135                 rect_to_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight());
   4136             else
   4137                 rect_to_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);
   4138             window->PosFloat = FindBestPopupWindowPos(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
   4139         }
   4140         else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_appearing_after_being_hidden)
   4141         {
   4142             ImRect rect_to_avoid(window->PosFloat.x - 1, window->PosFloat.y - 1, window->PosFloat.x + 1, window->PosFloat.y + 1);
   4143             window->PosFloat = FindBestPopupWindowPos(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
   4144         }
   4145 
   4146         // Position tooltip (always follows mouse)
   4147         if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api)
   4148         {
   4149             ImRect rect_to_avoid(g.IO.MousePos.x - 16, g.IO.MousePos.y - 8, g.IO.MousePos.x + 24, g.IO.MousePos.y + 24); // FIXME: Completely hard-coded. Perhaps center on cursor hit-point instead?
   4150             window->PosFloat = FindBestPopupWindowPos(g.IO.MousePos, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
   4151             if (window->AutoPosLastDirection == -1)
   4152                 window->PosFloat = g.IO.MousePos + ImVec2(2,2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
   4153         }
   4154 
   4155         // Clamp position so it stays visible
   4156         if (!(flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
   4157         {
   4158             if (!window_pos_set_by_api && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f) // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
   4159             {
   4160                 ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
   4161                 window->PosFloat = ImMax(window->PosFloat + window->Size, padding) - window->Size;
   4162                 window->PosFloat = ImMin(window->PosFloat, g.IO.DisplaySize - padding);
   4163             }
   4164         }
   4165         window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
   4166 
   4167         // Default item width. Make it proportional to window size if window manually resizes
   4168         if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
   4169             window->ItemWidthDefault = (float)(int)(window->Size.x * 0.65f);
   4170         else
   4171             window->ItemWidthDefault = (float)(int)(g.FontSize * 16.0f);
   4172 
   4173         // Prepare for focus requests
   4174         window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1);
   4175         window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter+1)) % (window->FocusIdxTabCounter+1);
   4176         window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1;
   4177         window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX;
   4178 
   4179         // Apply scrolling
   4180         if (window->ScrollTarget.x < FLT_MAX)
   4181         {
   4182             window->Scroll.x = window->ScrollTarget.x;
   4183             window->ScrollTarget.x = FLT_MAX;
   4184         }
   4185         if (window->ScrollTarget.y < FLT_MAX)
   4186         {
   4187             float center_ratio = window->ScrollTargetCenterRatio.y;
   4188             window->Scroll.y = window->ScrollTarget.y - ((1.0f - center_ratio) * (window->TitleBarHeight() + window->MenuBarHeight())) - (center_ratio * window->SizeFull.y);
   4189             window->ScrollTarget.y = FLT_MAX;
   4190         }
   4191         window->Scroll = ImMax(window->Scroll, ImVec2(0.0f, 0.0f));
   4192         if (!window->Collapsed && !window->SkipItems)
   4193             window->Scroll = ImMin(window->Scroll, ImMax(ImVec2(0.0f, 0.0f), window->SizeContents - window->SizeFull + window->ScrollbarSizes));
   4194 
   4195         // Modal window darkens what is behind them
   4196         if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostModalRootWindow())
   4197             window->DrawList->AddRectFilled(fullscreen_rect.Min, fullscreen_rect.Max, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio));
   4198 
   4199         // Draw window + handle manual resize
   4200         ImRect title_bar_rect = window->TitleBarRect();
   4201         const float window_rounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildWindowRounding : style.WindowRounding;
   4202         if (window->Collapsed)
   4203         {
   4204             // Draw title bar only
   4205             RenderFrame(title_bar_rect.GetTL(), title_bar_rect.GetBR(),  GetColorU32(ImGuiCol_TitleBgCollapsed), true, window_rounding);
   4206         }
   4207         else
   4208         {
   4209             ImU32 resize_col = 0;
   4210             const float resize_corner_size = ImMax(g.FontSize * 1.35f, window_rounding + 1.0f + g.FontSize * 0.2f);
   4211             if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && !(flags & ImGuiWindowFlags_NoResize))
   4212             {
   4213                 // Manual resize
   4214                 const ImVec2 br = window->Rect().GetBR();
   4215                 const ImRect resize_rect(br - ImVec2(resize_corner_size * 0.75f, resize_corner_size * 0.75f), br);
   4216                 const ImGuiID resize_id = window->GetID("#RESIZE");
   4217                 bool hovered, held;
   4218                 ButtonBehavior(resize_rect, resize_id, &hovered, &held, ImGuiButtonFlags_FlattenChilds);
   4219                 resize_col = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
   4220 
   4221                 if (hovered || held)
   4222                     g.MouseCursor = ImGuiMouseCursor_ResizeNWSE;
   4223 
   4224                 if (g.HoveredWindow == window && held && g.IO.MouseDoubleClicked[0])
   4225                 {
   4226                     // Manual auto-fit when double-clicking
   4227                     ApplySizeFullWithConstraint(window, size_auto_fit);
   4228                     if (!(flags & ImGuiWindowFlags_NoSavedSettings))
   4229                         MarkIniSettingsDirty();
   4230                     ClearActiveID();
   4231                 }
   4232                 else if (held)
   4233                 {
   4234                     // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
   4235                     ApplySizeFullWithConstraint(window, (g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize()) - window->Pos);
   4236                     if (!(flags & ImGuiWindowFlags_NoSavedSettings))
   4237                         MarkIniSettingsDirty();
   4238                 }
   4239 
   4240                 window->Size = window->SizeFull;
   4241                 title_bar_rect = window->TitleBarRect();
   4242             }
   4243 
   4244             // Scrollbars
   4245             window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > window->Size.y + style.ItemSpacing.y) && !(flags & ImGuiWindowFlags_NoScrollbar));
   4246             window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > window->Size.x - (window->ScrollbarY ? style.ScrollbarSize : 0.0f) - window->WindowPadding.x) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
   4247             window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
   4248             window->BorderSize = (flags & ImGuiWindowFlags_ShowBorders) ? 1.0f : 0.0f;
   4249 
   4250             // Window background, Default Alpha
   4251             ImGuiCol bg_color_idx = ImGuiCol_WindowBg;
   4252             if ((flags & ImGuiWindowFlags_ComboBox) != 0)
   4253                 bg_color_idx = ImGuiCol_ComboBg;
   4254             else if ((flags & ImGuiWindowFlags_Tooltip) != 0 || (flags & ImGuiWindowFlags_Popup) != 0)
   4255                 bg_color_idx = ImGuiCol_PopupBg;
   4256             else if ((flags & ImGuiWindowFlags_ChildWindow) != 0)
   4257                 bg_color_idx = ImGuiCol_ChildWindowBg;
   4258             ImVec4 bg_color = style.Colors[bg_color_idx];
   4259             if (bg_alpha >= 0.0f)
   4260                 bg_color.w = bg_alpha;
   4261             bg_color.w *= style.Alpha;
   4262             if (bg_color.w > 0.0f)
   4263                 window->DrawList->AddRectFilled(window->Pos+ImVec2(0,window->TitleBarHeight()), window->Pos+window->Size, ColorConvertFloat4ToU32(bg_color), window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImGuiCorner_All : ImGuiCorner_BottomLeft|ImGuiCorner_BottomRight);
   4264 
   4265             // Title bar
   4266             if (!(flags & ImGuiWindowFlags_NoTitleBar))
   4267                 window->DrawList->AddRectFilled(title_bar_rect.GetTL(), title_bar_rect.GetBR(), GetColorU32((g.FocusedWindow && window->RootNonPopupWindow == g.FocusedWindow->RootNonPopupWindow) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg), window_rounding, ImGuiCorner_TopLeft|ImGuiCorner_TopRight);
   4268 
   4269             // Menu bar
   4270             if (flags & ImGuiWindowFlags_MenuBar)
   4271             {
   4272                 ImRect menu_bar_rect = window->MenuBarRect();
   4273                 if (flags & ImGuiWindowFlags_ShowBorders)
   4274                     window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border));
   4275                 window->DrawList->AddRectFilled(menu_bar_rect.GetTL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImGuiCorner_TopLeft|ImGuiCorner_TopRight);
   4276             }
   4277 
   4278             // Scrollbars
   4279             if (window->ScrollbarX)
   4280                 Scrollbar(window, true);
   4281             if (window->ScrollbarY)
   4282                 Scrollbar(window, false);
   4283 
   4284             // Render resize grip
   4285             // (after the input handling so we don't have a frame of latency)
   4286             if (!(flags & ImGuiWindowFlags_NoResize))
   4287             {
   4288                 const ImVec2 br = window->Rect().GetBR();
   4289                 window->DrawList->PathLineTo(br + ImVec2(-resize_corner_size, -window->BorderSize));
   4290                 window->DrawList->PathLineTo(br + ImVec2(-window->BorderSize, -resize_corner_size));
   4291                 window->DrawList->PathArcToFast(ImVec2(br.x - window_rounding - window->BorderSize, br.y - window_rounding - window->BorderSize), window_rounding, 0, 3);
   4292                 window->DrawList->PathFillConvex(resize_col);
   4293             }
   4294 
   4295             // Borders
   4296             if (flags & ImGuiWindowFlags_ShowBorders)
   4297             {
   4298                 window->DrawList->AddRect(window->Pos+ImVec2(1,1), window->Pos+window->Size+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), window_rounding);
   4299                 window->DrawList->AddRect(window->Pos, window->Pos+window->Size, GetColorU32(ImGuiCol_Border), window_rounding);
   4300                 if (!(flags & ImGuiWindowFlags_NoTitleBar))
   4301                     window->DrawList->AddLine(title_bar_rect.GetBL()+ImVec2(1,0), title_bar_rect.GetBR()-ImVec2(1,0), GetColorU32(ImGuiCol_Border));
   4302             }
   4303         }
   4304 
   4305         // Update ContentsRegionMax. All the variable it depends on are set above in this function.
   4306         window->ContentsRegionRect.Min.x = -window->Scroll.x + window->WindowPadding.x;
   4307         window->ContentsRegionRect.Min.y = -window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight();
   4308         window->ContentsRegionRect.Max.x = -window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x)); 
   4309         window->ContentsRegionRect.Max.y = -window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y)); 
   4310 
   4311         // Setup drawing context
   4312         window->DC.IndentX = 0.0f + window->WindowPadding.x - window->Scroll.x;
   4313         window->DC.GroupOffsetX = 0.0f;
   4314         window->DC.ColumnsOffsetX = 0.0f;
   4315         window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.IndentX + window->DC.ColumnsOffsetX, window->TitleBarHeight() + window->MenuBarHeight() + window->WindowPadding.y - window->Scroll.y);
   4316         window->DC.CursorPos = window->DC.CursorStartPos;
   4317         window->DC.CursorPosPrevLine = window->DC.CursorPos;
   4318         window->DC.CursorMaxPos = window->DC.CursorStartPos;
   4319         window->DC.CurrentLineHeight = window->DC.PrevLineHeight = 0.0f;
   4320         window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
   4321         window->DC.MenuBarAppending = false;
   4322         window->DC.MenuBarOffsetX = ImMax(window->WindowPadding.x, style.ItemSpacing.x);
   4323         window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
   4324         window->DC.ChildWindows.resize(0);
   4325         window->DC.LayoutType = ImGuiLayoutType_Vertical;
   4326         window->DC.ItemWidth = window->ItemWidthDefault;
   4327         window->DC.TextWrapPos = -1.0f; // disabled
   4328         window->DC.AllowKeyboardFocus = true;
   4329         window->DC.ButtonRepeat = false;
   4330         window->DC.ItemWidthStack.resize(0);
   4331         window->DC.AllowKeyboardFocusStack.resize(0);
   4332         window->DC.ButtonRepeatStack.resize(0);
   4333         window->DC.TextWrapPosStack.resize(0);
   4334         window->DC.ColumnsCurrent = 0;
   4335         window->DC.ColumnsCount = 1;
   4336         window->DC.ColumnsStartPosY = window->DC.CursorPos.y;
   4337         window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY = window->DC.ColumnsStartPosY;
   4338         window->DC.TreeDepth = 0;
   4339         window->DC.StateStorage = &window->StateStorage;
   4340         window->DC.GroupStack.resize(0);
   4341         window->DC.ColorEditMode = ImGuiColorEditMode_UserSelect;
   4342         window->MenuColumns.Update(3, style.ItemSpacing.x, !window_was_active);
   4343 
   4344         if (window->AutoFitFramesX > 0)
   4345             window->AutoFitFramesX--;
   4346         if (window->AutoFitFramesY > 0)
   4347             window->AutoFitFramesY--;
   4348 
   4349         // New windows appears in front (we need to do that AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
   4350         if (!window_was_active && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
   4351             if (!(flags & (ImGuiWindowFlags_ChildWindow|ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup))
   4352                 FocusWindow(window);
   4353 
   4354         // Title bar
   4355         if (!(flags & ImGuiWindowFlags_NoTitleBar))
   4356         {
   4357             if (p_open != NULL)
   4358             {
   4359                 const float pad = 2.0f;
   4360                 const float rad = (window->TitleBarHeight() - pad*2.0f) * 0.5f;
   4361                 if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-pad - rad, pad + rad), rad))
   4362                     *p_open = false;
   4363             }
   4364 
   4365             const ImVec2 text_size = CalcTextSize(name, NULL, true);
   4366             if (!(flags & ImGuiWindowFlags_NoCollapse))
   4367                 RenderCollapseTriangle(window->Pos + style.FramePadding, !window->Collapsed, 1.0f);
   4368 
   4369             ImVec2 text_min = window->Pos;
   4370             ImVec2 text_max = window->Pos + ImVec2(window->Size.x, style.FramePadding.y*2 + text_size.y);
   4371             ImRect clip_rect;
   4372             clip_rect.Max = ImVec2(window->Pos.x + window->Size.x - (p_open ? title_bar_rect.GetHeight() - 3 : style.FramePadding.x), text_max.y); // Match the size of CloseWindowButton()
   4373             float pad_left = (flags & ImGuiWindowFlags_NoCollapse) == 0 ? (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x) : style.FramePadding.x;
   4374             float pad_right = (p_open != NULL) ? (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x) : style.FramePadding.x;
   4375             if (style.WindowTitleAlign.x > 0.0f) pad_right = ImLerp(pad_right, pad_left, style.WindowTitleAlign.x);
   4376             text_min.x += pad_left;
   4377             text_max.x -= pad_right;
   4378             clip_rect.Min = ImVec2(text_min.x, window->Pos.y);
   4379             RenderTextClipped(text_min, text_max, name, NULL, &text_size, style.WindowTitleAlign, &clip_rect);
   4380         }
   4381 
   4382         // Save clipped aabb so we can access it in constant-time in FindHoveredWindow()
   4383         window->WindowRectClipped = window->Rect();
   4384         window->WindowRectClipped.Clip(window->ClipRect);
   4385 
   4386         // Pressing CTRL+C while holding on a window copy its content to the clipboard
   4387         // 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.
   4388         // Maybe we can support CTRL+C on every element?
   4389         /*
   4390         if (g.ActiveId == move_id)
   4391             if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
   4392                 ImGui::LogToClipboard();
   4393         */
   4394     }
   4395 
   4396     // Inner clipping rectangle
   4397     // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame
   4398     // Note that if our window is collapsed we will end up with a null clipping rectangle which is the correct behavior.
   4399     const ImRect title_bar_rect = window->TitleBarRect();
   4400     const float border_size = window->BorderSize;
   4401     ImRect clip_rect; // Force round to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
   4402     clip_rect.Min.x = ImFloor(0.5f + title_bar_rect.Min.x + ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f)));
   4403     clip_rect.Min.y = ImFloor(0.5f + title_bar_rect.Max.y + window->MenuBarHeight() + border_size);
   4404     clip_rect.Max.x = ImFloor(0.5f + window->Pos.x + window->Size.x - window->ScrollbarSizes.x - ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f)));
   4405     clip_rect.Max.y = ImFloor(0.5f + window->Pos.y + window->Size.y - window->ScrollbarSizes.y - border_size);
   4406     PushClipRect(clip_rect.Min, clip_rect.Max, true);
   4407 
   4408     // Clear 'accessed' flag last thing
   4409     if (first_begin_of_the_frame)
   4410         window->Accessed = false;
   4411     window->BeginCount++;
   4412     g.SetNextWindowSizeConstraint = false;
   4413 
   4414     // Child window can be out of sight and have "negative" clip windows.
   4415     // Mark them as collapsed so commands are skipped earlier (we can't manually collapse because they have no title bar).
   4416     if (flags & ImGuiWindowFlags_ChildWindow)
   4417     {
   4418         IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
   4419         window->Collapsed = parent_window && parent_window->Collapsed;
   4420 
   4421         if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
   4422             window->Collapsed |= (window->WindowRectClipped.Min.x >= window->WindowRectClipped.Max.x || window->WindowRectClipped.Min.y >= window->WindowRectClipped.Max.y);
   4423 
   4424         // We also hide the window from rendering because we've already added its border to the command list.
   4425         // (we could perform the check earlier in the function but it is simpler at this point)
   4426         if (window->Collapsed)
   4427             window->Active = false;
   4428     }
   4429     if (style.Alpha <= 0.0f)
   4430         window->Active = false;
   4431 
   4432     // Return false if we don't intend to display anything to allow user to perform an early out optimization
   4433     window->SkipItems = (window->Collapsed || !window->Active) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0;
   4434     return !window->SkipItems;
   4435 }
   4436 
   4437 void ImGui::End()
   4438 {
   4439     ImGuiContext& g = *GImGui;
   4440     ImGuiWindow* window = g.CurrentWindow;
   4441 
   4442     if (window->DC.ColumnsCount != 1) // close columns set if any is open
   4443         Columns(1, "#CLOSECOLUMNS");
   4444     PopClipRect();   // inner window clip rectangle
   4445 
   4446     // Stop logging
   4447     if (!(window->Flags & ImGuiWindowFlags_ChildWindow))    // FIXME: add more options for scope of logging
   4448         LogFinish();
   4449 
   4450     // Pop
   4451     // NB: we don't clear 'window->RootWindow'. The pointer is allowed to live until the next call to Begin().
   4452     g.CurrentWindowStack.pop_back();
   4453     if (window->Flags & ImGuiWindowFlags_Popup)
   4454         g.CurrentPopupStack.pop_back();
   4455     CheckStacksSize(window, false);
   4456     SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
   4457 }
   4458 
   4459 // Vertical scrollbar
   4460 // The entire piece of code below is rather confusing because:
   4461 // - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab)
   4462 // - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar
   4463 // - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal.
   4464 static void Scrollbar(ImGuiWindow* window, bool horizontal)
   4465 {
   4466     ImGuiContext& g = *GImGui;
   4467     const ImGuiStyle& style = g.Style;
   4468     const ImGuiID id = window->GetID(horizontal ? "#SCROLLX" : "#SCROLLY");
   4469 
   4470     // Render background
   4471     bool other_scrollbar = (horizontal ? window->ScrollbarY : window->ScrollbarX);
   4472     float other_scrollbar_size_w = other_scrollbar ? style.ScrollbarSize : 0.0f;
   4473     const ImRect window_rect = window->Rect();
   4474     const float border_size = window->BorderSize;
   4475     ImRect bb = horizontal
   4476         ? ImRect(window->Pos.x + border_size, window_rect.Max.y - style.ScrollbarSize, window_rect.Max.x - other_scrollbar_size_w - border_size, window_rect.Max.y - border_size)
   4477         : ImRect(window_rect.Max.x - style.ScrollbarSize, window->Pos.y + border_size, window_rect.Max.x - border_size, window_rect.Max.y - other_scrollbar_size_w - border_size);
   4478     if (!horizontal)
   4479         bb.Min.y += window->TitleBarHeight() + ((window->Flags & ImGuiWindowFlags_MenuBar) ? window->MenuBarHeight() : 0.0f);
   4480     if (bb.GetWidth() <= 0.0f || bb.GetHeight() <= 0.0f)
   4481         return;
   4482 
   4483     float window_rounding = (window->Flags & ImGuiWindowFlags_ChildWindow) ? style.ChildWindowRounding : style.WindowRounding;
   4484     int window_rounding_corners;
   4485     if (horizontal)
   4486         window_rounding_corners = ImGuiCorner_BottomLeft | (other_scrollbar ? 0 : ImGuiCorner_BottomRight);
   4487     else
   4488         window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImGuiCorner_TopRight : 0) | (other_scrollbar ? 0 : ImGuiCorner_BottomRight);
   4489     window->DrawList->AddRectFilled(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_ScrollbarBg), window_rounding, window_rounding_corners);
   4490     bb.Reduce(ImVec2(ImClamp((float)(int)((bb.Max.x - bb.Min.x - 2.0f) * 0.5f), 0.0f, 3.0f), ImClamp((float)(int)((bb.Max.y - bb.Min.y - 2.0f) * 0.5f), 0.0f, 3.0f)));
   4491 
   4492     // V denote the main axis of the scrollbar
   4493     float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight();
   4494     float scroll_v = horizontal ? window->Scroll.x : window->Scroll.y;
   4495     float win_size_avail_v = (horizontal ? window->Size.x : window->Size.y) - other_scrollbar_size_w;
   4496     float win_size_contents_v = horizontal ? window->SizeContents.x : window->SizeContents.y;
   4497 
   4498     // The grabable box size generally represent the amount visible (vs the total scrollable amount)
   4499     // But we maintain a minimum size in pixel to allow for the user to still aim inside.
   4500     const float grab_h_pixels = ImMin(ImMax(scrollbar_size_v * ImSaturate(win_size_avail_v / ImMax(win_size_contents_v, win_size_avail_v)), style.GrabMinSize), scrollbar_size_v);
   4501     const float grab_h_norm = grab_h_pixels / scrollbar_size_v;
   4502 
   4503     // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar().
   4504     bool held = false;
   4505     bool hovered = false;
   4506     const bool previously_held = (g.ActiveId == id);
   4507     ImGui::ButtonBehavior(bb, id, &hovered, &held);
   4508 
   4509     float scroll_max = ImMax(1.0f, win_size_contents_v - win_size_avail_v);
   4510     float scroll_ratio = ImSaturate(scroll_v / scroll_max);
   4511     float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
   4512     if (held && grab_h_norm < 1.0f)
   4513     {
   4514         float scrollbar_pos_v = horizontal ? bb.Min.x : bb.Min.y;
   4515         float mouse_pos_v = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y;
   4516         float* click_delta_to_grab_center_v = horizontal ? &g.ScrollbarClickDeltaToGrabCenter.x : &g.ScrollbarClickDeltaToGrabCenter.y;
   4517 
   4518         // Click position in scrollbar normalized space (0.0f->1.0f)
   4519         const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v);
   4520         ImGui::SetHoveredID(id);
   4521 
   4522         bool seek_absolute = false;
   4523         if (!previously_held)
   4524         {
   4525             // On initial click calculate the distance between mouse and the center of the grab
   4526             if (clicked_v_norm >= grab_v_norm && clicked_v_norm <= grab_v_norm + grab_h_norm)
   4527             {
   4528                 *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f;
   4529             }
   4530             else
   4531             {
   4532                 seek_absolute = true;
   4533                 *click_delta_to_grab_center_v = 0.0f;
   4534             }
   4535         }
   4536 
   4537         // Apply scroll
   4538         // It is ok to modify Scroll here because we are being called in Begin() after the calculation of SizeContents and before setting up our starting position
   4539         const float scroll_v_norm = ImSaturate((clicked_v_norm - *click_delta_to_grab_center_v - grab_h_norm*0.5f) / (1.0f - grab_h_norm));
   4540         scroll_v = (float)(int)(0.5f + scroll_v_norm * scroll_max);//(win_size_contents_v - win_size_v));
   4541         if (horizontal)
   4542             window->Scroll.x = scroll_v;
   4543         else
   4544             window->Scroll.y = scroll_v;
   4545 
   4546         // Update values for rendering
   4547         scroll_ratio = ImSaturate(scroll_v / scroll_max);
   4548         grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
   4549 
   4550         // Update distance to grab now that we have seeked and saturated
   4551         if (seek_absolute)
   4552             *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f;
   4553     }
   4554 
   4555     // Render
   4556     const ImU32 grab_col = ImGui::GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab);
   4557     if (horizontal)
   4558         window->DrawList->AddRectFilled(ImVec2(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y), ImVec2(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, bb.Max.y), grab_col, style.ScrollbarRounding);
   4559     else
   4560         window->DrawList->AddRectFilled(ImVec2(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm)), ImVec2(bb.Max.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels), grab_col, style.ScrollbarRounding);
   4561 }
   4562 
   4563 // Moving window to front of display (which happens to be back of our sorted list)
   4564 void ImGui::FocusWindow(ImGuiWindow* window)
   4565 {
   4566     ImGuiContext& g = *GImGui;
   4567 
   4568     // Always mark the window we passed as focused. This is used for keyboard interactions such as tabbing.
   4569     g.FocusedWindow = window;
   4570 
   4571     // Passing NULL allow to disable keyboard focus
   4572     if (!window)
   4573         return;
   4574 
   4575     // And move its root window to the top of the pile
   4576     if (window->RootWindow)
   4577         window = window->RootWindow;
   4578 
   4579     // Steal focus on active widgets
   4580     if (window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement should be unnecessary. Need further testing before removing it..
   4581         if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != window)
   4582             ClearActiveID();
   4583 
   4584     // Bring to front
   4585     if ((window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus) || g.Windows.back() == window)
   4586         return;
   4587     for (int i = 0; i < g.Windows.Size; i++)
   4588         if (g.Windows[i] == window)
   4589         {
   4590             g.Windows.erase(g.Windows.begin() + i);
   4591             break;
   4592         }
   4593     g.Windows.push_back(window);
   4594 }
   4595 
   4596 void ImGui::PushItemWidth(float item_width)
   4597 {
   4598     ImGuiWindow* window = GetCurrentWindow();
   4599     window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
   4600     window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
   4601 }
   4602 
   4603 static void PushMultiItemsWidths(int components, float w_full)
   4604 {
   4605     ImGuiWindow* window = ImGui::GetCurrentWindow();
   4606     const ImGuiStyle& style = GImGui->Style;
   4607     if (w_full <= 0.0f)
   4608         w_full = ImGui::CalcItemWidth();
   4609     const float w_item_one  = ImMax(1.0f, (float)(int)((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
   4610     const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
   4611     window->DC.ItemWidthStack.push_back(w_item_last);
   4612     for (int i = 0; i < components-1; i++)
   4613         window->DC.ItemWidthStack.push_back(w_item_one);
   4614     window->DC.ItemWidth = window->DC.ItemWidthStack.back();
   4615 }
   4616 
   4617 void ImGui::PopItemWidth()
   4618 {
   4619     ImGuiWindow* window = GetCurrentWindow();
   4620     window->DC.ItemWidthStack.pop_back();
   4621     window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
   4622 }
   4623 
   4624 float ImGui::CalcItemWidth()
   4625 {
   4626     ImGuiWindow* window = GetCurrentWindowRead();
   4627     float w = window->DC.ItemWidth;
   4628     if (w < 0.0f)
   4629     {
   4630         // Align to a right-side limit. We include 1 frame padding in the calculation because this is how the width is always used (we add 2 frame padding to it), but we could move that responsibility to the widget as well.
   4631         float width_to_right_edge = GetContentRegionAvail().x;
   4632         w = ImMax(1.0f, width_to_right_edge + w);
   4633     }
   4634     w = (float)(int)w;
   4635     return w;
   4636 }
   4637 
   4638 static ImFont* GetDefaultFont()
   4639 {
   4640     ImGuiContext& g = *GImGui;
   4641     return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0];
   4642 }
   4643 
   4644 static void SetCurrentFont(ImFont* font)
   4645 {
   4646     ImGuiContext& g = *GImGui;
   4647     IM_ASSERT(font && font->IsLoaded());    // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
   4648     IM_ASSERT(font->Scale > 0.0f);
   4649     g.Font = font;
   4650     g.FontBaseSize = g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale;
   4651     g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
   4652     g.FontTexUvWhitePixel = g.Font->ContainerAtlas->TexUvWhitePixel;
   4653 }
   4654 
   4655 void ImGui::PushFont(ImFont* font)
   4656 {
   4657     ImGuiContext& g = *GImGui;
   4658     if (!font)
   4659         font = GetDefaultFont();
   4660     SetCurrentFont(font);
   4661     g.FontStack.push_back(font);
   4662     g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
   4663 }
   4664 
   4665 void  ImGui::PopFont()
   4666 {
   4667     ImGuiContext& g = *GImGui;
   4668     g.CurrentWindow->DrawList->PopTextureID();
   4669     g.FontStack.pop_back();
   4670     SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
   4671 }
   4672 
   4673 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
   4674 {
   4675     ImGuiWindow* window = GetCurrentWindow();
   4676     window->DC.AllowKeyboardFocus = allow_keyboard_focus;
   4677     window->DC.AllowKeyboardFocusStack.push_back(allow_keyboard_focus);
   4678 }
   4679 
   4680 void ImGui::PopAllowKeyboardFocus()
   4681 {
   4682     ImGuiWindow* window = GetCurrentWindow();
   4683     window->DC.AllowKeyboardFocusStack.pop_back();
   4684     window->DC.AllowKeyboardFocus = window->DC.AllowKeyboardFocusStack.empty() ? true : window->DC.AllowKeyboardFocusStack.back();
   4685 }
   4686 
   4687 void ImGui::PushButtonRepeat(bool repeat)
   4688 {
   4689     ImGuiWindow* window = GetCurrentWindow();
   4690     window->DC.ButtonRepeat = repeat;
   4691     window->DC.ButtonRepeatStack.push_back(repeat);
   4692 }
   4693 
   4694 void ImGui::PopButtonRepeat()
   4695 {
   4696     ImGuiWindow* window = GetCurrentWindow();
   4697     window->DC.ButtonRepeatStack.pop_back();
   4698     window->DC.ButtonRepeat = window->DC.ButtonRepeatStack.empty() ? false : window->DC.ButtonRepeatStack.back();
   4699 }
   4700 
   4701 void ImGui::PushTextWrapPos(float wrap_pos_x)
   4702 {
   4703     ImGuiWindow* window = GetCurrentWindow();
   4704     window->DC.TextWrapPos = wrap_pos_x;
   4705     window->DC.TextWrapPosStack.push_back(wrap_pos_x);
   4706 }
   4707 
   4708 void ImGui::PopTextWrapPos()
   4709 {
   4710     ImGuiWindow* window = GetCurrentWindow();
   4711     window->DC.TextWrapPosStack.pop_back();
   4712     window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
   4713 }
   4714 
   4715 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
   4716 {
   4717     ImGuiContext& g = *GImGui;
   4718     ImGuiColMod backup;
   4719     backup.Col = idx;
   4720     backup.BackupValue = g.Style.Colors[idx];
   4721     g.ColorModifiers.push_back(backup);
   4722     g.Style.Colors[idx] = col;
   4723 }
   4724 
   4725 void ImGui::PopStyleColor(int count)
   4726 {
   4727     ImGuiContext& g = *GImGui;
   4728     while (count > 0)
   4729     {
   4730         ImGuiColMod& backup = g.ColorModifiers.back();
   4731         g.Style.Colors[backup.Col] = backup.BackupValue;
   4732         g.ColorModifiers.pop_back();
   4733         count--;
   4734     }
   4735 }
   4736 
   4737 struct ImGuiStyleVarInfo
   4738 {
   4739     ImGuiDataType   Type;
   4740     ImU32           Offset;
   4741     void*           GetVarPtr() const { return (void*)((unsigned char*)&GImGui->Style + Offset); }
   4742 };
   4743 
   4744 static const ImGuiStyleVarInfo GStyleVarInfo[ImGuiStyleVar_Count_] =
   4745 {
   4746     { ImGuiDataType_Float,  (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) },
   4747     { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) },
   4748     { ImGuiDataType_Float,  (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) },
   4749     { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) },
   4750     { ImGuiDataType_Float,  (ImU32)IM_OFFSETOF(ImGuiStyle, ChildWindowRounding) },
   4751     { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) },
   4752     { ImGuiDataType_Float,  (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) },
   4753     { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) },
   4754     { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) },
   4755     { ImGuiDataType_Float,  (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) },
   4756     { ImGuiDataType_Float,  (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) },
   4757     { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) },
   4758 };
   4759 
   4760 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
   4761 {
   4762     IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_Count_);
   4763     return &GStyleVarInfo[idx];
   4764 }
   4765 
   4766 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
   4767 {
   4768     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
   4769     if (var_info->Type == ImGuiDataType_Float)
   4770     {
   4771         float* pvar = (float*)var_info->GetVarPtr();
   4772         GImGui->StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
   4773         *pvar = val;
   4774         return;
   4775     }
   4776     IM_ASSERT(0); // Called function with wrong-type? Variable is not a float.
   4777 }
   4778 
   4779 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
   4780 {
   4781     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
   4782     if (var_info->Type == ImGuiDataType_Float2)
   4783     {
   4784         ImVec2* pvar = (ImVec2*)var_info->GetVarPtr();
   4785         GImGui->StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
   4786         *pvar = val;
   4787         return;
   4788     }
   4789     IM_ASSERT(0); // Called function with wrong-type? Variable is not a ImVec2.
   4790 }
   4791 
   4792 void ImGui::PopStyleVar(int count)
   4793 {
   4794     ImGuiContext& g = *GImGui;
   4795     while (count > 0)
   4796     {
   4797         ImGuiStyleMod& backup = g.StyleModifiers.back();
   4798         const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
   4799         if (info->Type == ImGuiDataType_Float)          (*(float*)info->GetVarPtr()) = backup.BackupFloat[0];
   4800         else if (info->Type == ImGuiDataType_Float2)    (*(ImVec2*)info->GetVarPtr()) = ImVec2(backup.BackupFloat[0], backup.BackupFloat[1]);
   4801         else if (info->Type == ImGuiDataType_Int)       (*(int*)info->GetVarPtr()) = backup.BackupInt[0];
   4802         g.StyleModifiers.pop_back();
   4803         count--;
   4804     }
   4805 }
   4806 
   4807 const char* ImGui::GetStyleColName(ImGuiCol idx)
   4808 {
   4809     // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
   4810     switch (idx)
   4811     {
   4812     case ImGuiCol_Text: return "Text";
   4813     case ImGuiCol_TextDisabled: return "TextDisabled";
   4814     case ImGuiCol_WindowBg: return "WindowBg";
   4815     case ImGuiCol_ChildWindowBg: return "ChildWindowBg";
   4816     case ImGuiCol_PopupBg: return "PopupBg";
   4817     case ImGuiCol_Border: return "Border";
   4818     case ImGuiCol_BorderShadow: return "BorderShadow";
   4819     case ImGuiCol_FrameBg: return "FrameBg";
   4820     case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
   4821     case ImGuiCol_FrameBgActive: return "FrameBgActive";
   4822     case ImGuiCol_TitleBg: return "TitleBg";
   4823     case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
   4824     case ImGuiCol_TitleBgActive: return "TitleBgActive";
   4825     case ImGuiCol_MenuBarBg: return "MenuBarBg";
   4826     case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
   4827     case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
   4828     case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
   4829     case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
   4830     case ImGuiCol_ComboBg: return "ComboBg";
   4831     case ImGuiCol_CheckMark: return "CheckMark";
   4832     case ImGuiCol_SliderGrab: return "SliderGrab";
   4833     case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
   4834     case ImGuiCol_Button: return "Button";
   4835     case ImGuiCol_ButtonHovered: return "ButtonHovered";
   4836     case ImGuiCol_ButtonActive: return "ButtonActive";
   4837     case ImGuiCol_Header: return "Header";
   4838     case ImGuiCol_HeaderHovered: return "HeaderHovered";
   4839     case ImGuiCol_HeaderActive: return "HeaderActive";
   4840     case ImGuiCol_Column: return "Column";
   4841     case ImGuiCol_ColumnHovered: return "ColumnHovered";
   4842     case ImGuiCol_ColumnActive: return "ColumnActive";
   4843     case ImGuiCol_ResizeGrip: return "ResizeGrip";
   4844     case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
   4845     case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
   4846     case ImGuiCol_CloseButton: return "CloseButton";
   4847     case ImGuiCol_CloseButtonHovered: return "CloseButtonHovered";
   4848     case ImGuiCol_CloseButtonActive: return "CloseButtonActive";
   4849     case ImGuiCol_PlotLines: return "PlotLines";
   4850     case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
   4851     case ImGuiCol_PlotHistogram: return "PlotHistogram";
   4852     case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
   4853     case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
   4854     case ImGuiCol_ModalWindowDarkening: return "ModalWindowDarkening";
   4855     }
   4856     IM_ASSERT(0);
   4857     return "Unknown";
   4858 }
   4859 
   4860 bool ImGui::IsWindowHovered()
   4861 {
   4862     ImGuiContext& g = *GImGui;
   4863     return g.HoveredWindow == g.CurrentWindow && IsWindowContentHoverable(g.HoveredRootWindow);
   4864 }
   4865 
   4866 bool ImGui::IsWindowFocused()
   4867 {
   4868     ImGuiContext& g = *GImGui;
   4869     return g.FocusedWindow == g.CurrentWindow;
   4870 }
   4871 
   4872 bool ImGui::IsRootWindowFocused()
   4873 {
   4874     ImGuiContext& g = *GImGui;
   4875     return g.FocusedWindow == g.CurrentWindow->RootWindow;
   4876 }
   4877 
   4878 bool ImGui::IsRootWindowOrAnyChildFocused()
   4879 {
   4880     ImGuiContext& g = *GImGui;
   4881     return g.FocusedWindow && g.FocusedWindow->RootWindow == g.CurrentWindow->RootWindow;
   4882 }
   4883 
   4884 bool ImGui::IsRootWindowOrAnyChildHovered()
   4885 {
   4886     ImGuiContext& g = *GImGui;
   4887     return g.HoveredRootWindow && (g.HoveredRootWindow == g.CurrentWindow->RootWindow) && IsWindowContentHoverable(g.HoveredRootWindow);
   4888 }
   4889 
   4890 float ImGui::GetWindowWidth()
   4891 {
   4892     ImGuiWindow* window = GImGui->CurrentWindow;
   4893     return window->Size.x;
   4894 }
   4895 
   4896 float ImGui::GetWindowHeight()
   4897 {
   4898     ImGuiWindow* window = GImGui->CurrentWindow;
   4899     return window->Size.y;
   4900 }
   4901 
   4902 ImVec2 ImGui::GetWindowPos()
   4903 {
   4904     ImGuiContext& g = *GImGui;
   4905     ImGuiWindow* window = g.CurrentWindow;
   4906     return window->Pos;
   4907 }
   4908 
   4909 static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y)
   4910 {
   4911     window->DC.CursorMaxPos.y += window->Scroll.y;
   4912     window->Scroll.y = new_scroll_y;
   4913     window->DC.CursorMaxPos.y -= window->Scroll.y;
   4914 }
   4915 
   4916 static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiSetCond cond)
   4917 {
   4918     // Test condition (NB: bit 0 is always true) and clear flags for next time
   4919     if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
   4920         return;
   4921     window->SetWindowPosAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing);
   4922     window->SetWindowPosCenterWanted = false;
   4923 
   4924     // Set
   4925     const ImVec2 old_pos = window->Pos;
   4926     window->PosFloat = pos;
   4927     window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
   4928     window->DC.CursorPos += (window->Pos - old_pos);    // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor
   4929     window->DC.CursorMaxPos += (window->Pos - old_pos); // And more importantly we need to adjust this so size calculation doesn't get affected.
   4930 }
   4931 
   4932 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiSetCond cond)
   4933 {
   4934     ImGuiWindow* window = GetCurrentWindowRead();
   4935     SetWindowPos(window, pos, cond);
   4936 }
   4937 
   4938 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiSetCond cond)
   4939 {
   4940     if (ImGuiWindow* window = FindWindowByName(name))
   4941         SetWindowPos(window, pos, cond);
   4942 }
   4943 
   4944 ImVec2 ImGui::GetWindowSize()
   4945 {
   4946     ImGuiWindow* window = GetCurrentWindowRead();
   4947     return window->Size;
   4948 }
   4949 
   4950 static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiSetCond cond)
   4951 {
   4952     // Test condition (NB: bit 0 is always true) and clear flags for next time
   4953     if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
   4954         return;
   4955     window->SetWindowSizeAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing);
   4956 
   4957     // Set
   4958     if (size.x > 0.0f)
   4959     {
   4960         window->AutoFitFramesX = 0;
   4961         window->SizeFull.x = size.x;
   4962     }
   4963     else
   4964     {
   4965         window->AutoFitFramesX = 2;
   4966         window->AutoFitOnlyGrows = false;
   4967     }
   4968     if (size.y > 0.0f)
   4969     {
   4970         window->AutoFitFramesY = 0;
   4971         window->SizeFull.y = size.y;
   4972     }
   4973     else
   4974     {
   4975         window->AutoFitFramesY = 2;
   4976         window->AutoFitOnlyGrows = false;
   4977     }
   4978 }
   4979 
   4980 void ImGui::SetWindowSize(const ImVec2& size, ImGuiSetCond cond)
   4981 {
   4982     SetWindowSize(GImGui->CurrentWindow, size, cond);
   4983 }
   4984 
   4985 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiSetCond cond)
   4986 {
   4987     ImGuiWindow* window = FindWindowByName(name);
   4988     if (window)
   4989         SetWindowSize(window, size, cond);
   4990 }
   4991 
   4992 static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiSetCond cond)
   4993 {
   4994     // Test condition (NB: bit 0 is always true) and clear flags for next time
   4995     if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
   4996         return;
   4997     window->SetWindowCollapsedAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing);
   4998 
   4999     // Set
   5000     window->Collapsed = collapsed;
   5001 }
   5002 
   5003 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiSetCond cond)
   5004 {
   5005     SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
   5006 }
   5007 
   5008 bool ImGui::IsWindowCollapsed()
   5009 {
   5010     return GImGui->CurrentWindow->Collapsed;
   5011 }
   5012 
   5013 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiSetCond cond)
   5014 {
   5015     ImGuiWindow* window = FindWindowByName(name);
   5016     if (window)
   5017         SetWindowCollapsed(window, collapsed, cond);
   5018 }
   5019 
   5020 void ImGui::SetWindowFocus()
   5021 {
   5022     FocusWindow(GImGui->CurrentWindow);
   5023 }
   5024 
   5025 void ImGui::SetWindowFocus(const char* name)
   5026 {
   5027     if (name)
   5028     {
   5029         if (ImGuiWindow* window = FindWindowByName(name))
   5030             FocusWindow(window);
   5031     }
   5032     else
   5033     {
   5034         FocusWindow(NULL);
   5035     }
   5036 }
   5037 
   5038 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiSetCond cond)
   5039 {
   5040     ImGuiContext& g = *GImGui;
   5041     g.SetNextWindowPosVal = pos;
   5042     g.SetNextWindowPosCond = cond ? cond : ImGuiSetCond_Always;
   5043 }
   5044 
   5045 void ImGui::SetNextWindowPosCenter(ImGuiSetCond cond)
   5046 {
   5047     ImGuiContext& g = *GImGui;
   5048     g.SetNextWindowPosVal = ImVec2(-FLT_MAX, -FLT_MAX);
   5049     g.SetNextWindowPosCond = cond ? cond : ImGuiSetCond_Always;
   5050 }
   5051 
   5052 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiSetCond cond)
   5053 {
   5054     ImGuiContext& g = *GImGui;
   5055     g.SetNextWindowSizeVal = size;
   5056     g.SetNextWindowSizeCond = cond ? cond : ImGuiSetCond_Always;
   5057 }
   5058 
   5059 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeConstraintCallback custom_callback, void* custom_callback_user_data)
   5060 {
   5061     ImGuiContext& g = *GImGui;
   5062     g.SetNextWindowSizeConstraint = true;
   5063     g.SetNextWindowSizeConstraintRect = ImRect(size_min, size_max);
   5064     g.SetNextWindowSizeConstraintCallback = custom_callback;
   5065     g.SetNextWindowSizeConstraintCallbackUserData = custom_callback_user_data;
   5066 }
   5067 
   5068 void ImGui::SetNextWindowContentSize(const ImVec2& size)
   5069 {
   5070     ImGuiContext& g = *GImGui;
   5071     g.SetNextWindowContentSizeVal = size;
   5072     g.SetNextWindowContentSizeCond = ImGuiSetCond_Always;
   5073 }
   5074 
   5075 void ImGui::SetNextWindowContentWidth(float width)
   5076 {
   5077     ImGuiContext& g = *GImGui;
   5078     g.SetNextWindowContentSizeVal = ImVec2(width, g.SetNextWindowContentSizeCond ? g.SetNextWindowContentSizeVal.y : 0.0f);
   5079     g.SetNextWindowContentSizeCond = ImGuiSetCond_Always;
   5080 }
   5081 
   5082 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiSetCond cond)
   5083 {
   5084     ImGuiContext& g = *GImGui;
   5085     g.SetNextWindowCollapsedVal = collapsed;
   5086     g.SetNextWindowCollapsedCond = cond ? cond : ImGuiSetCond_Always;
   5087 }
   5088 
   5089 void ImGui::SetNextWindowFocus()
   5090 {
   5091     ImGuiContext& g = *GImGui;
   5092     g.SetNextWindowFocus = true;
   5093 }
   5094 
   5095 // In window space (not screen space!)
   5096 ImVec2 ImGui::GetContentRegionMax()
   5097 {
   5098     ImGuiWindow* window = GetCurrentWindowRead();
   5099     ImVec2 mx = window->ContentsRegionRect.Max;
   5100     if (window->DC.ColumnsCount != 1)
   5101         mx.x = GetColumnOffset(window->DC.ColumnsCurrent + 1) - window->WindowPadding.x;
   5102     return mx;
   5103 }
   5104 
   5105 ImVec2 ImGui::GetContentRegionAvail()
   5106 {
   5107     ImGuiWindow* window = GetCurrentWindowRead();
   5108     return GetContentRegionMax() - (window->DC.CursorPos - window->Pos);
   5109 }
   5110 
   5111 float ImGui::GetContentRegionAvailWidth()
   5112 {
   5113     return GetContentRegionAvail().x;
   5114 }
   5115 
   5116 // In window space (not screen space!)
   5117 ImVec2 ImGui::GetWindowContentRegionMin()
   5118 {
   5119     ImGuiWindow* window = GetCurrentWindowRead();
   5120     return window->ContentsRegionRect.Min;
   5121 }
   5122 
   5123 ImVec2 ImGui::GetWindowContentRegionMax()
   5124 {
   5125     ImGuiWindow* window = GetCurrentWindowRead();
   5126     return window->ContentsRegionRect.Max;
   5127 }
   5128 
   5129 float ImGui::GetWindowContentRegionWidth()
   5130 {
   5131     ImGuiWindow* window = GetCurrentWindowRead();
   5132     return window->ContentsRegionRect.Max.x - window->ContentsRegionRect.Min.x;
   5133 }
   5134 
   5135 float ImGui::GetTextLineHeight()
   5136 {
   5137     ImGuiContext& g = *GImGui;
   5138     return g.FontSize;
   5139 }
   5140 
   5141 float ImGui::GetTextLineHeightWithSpacing()
   5142 {
   5143     ImGuiContext& g = *GImGui;
   5144     return g.FontSize + g.Style.ItemSpacing.y;
   5145 }
   5146 
   5147 float ImGui::GetItemsLineHeightWithSpacing()
   5148 {
   5149     ImGuiContext& g = *GImGui;
   5150     return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
   5151 }
   5152 
   5153 ImDrawList* ImGui::GetWindowDrawList()
   5154 {
   5155     ImGuiWindow* window = GetCurrentWindow();
   5156     return window->DrawList;
   5157 }
   5158 
   5159 ImFont* ImGui::GetFont()
   5160 {
   5161     return GImGui->Font;
   5162 }
   5163 
   5164 float ImGui::GetFontSize()
   5165 {
   5166     return GImGui->FontSize;
   5167 }
   5168 
   5169 ImVec2 ImGui::GetFontTexUvWhitePixel()
   5170 {
   5171     return GImGui->FontTexUvWhitePixel;
   5172 }
   5173 
   5174 void ImGui::SetWindowFontScale(float scale)
   5175 {
   5176     ImGuiContext& g = *GImGui;
   5177     ImGuiWindow* window = GetCurrentWindow();
   5178     window->FontWindowScale = scale;
   5179     g.FontSize = window->CalcFontSize();
   5180 }
   5181 
   5182 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
   5183 // 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'.
   5184 ImVec2 ImGui::GetCursorPos()
   5185 {
   5186     ImGuiWindow* window = GetCurrentWindowRead();
   5187     return window->DC.CursorPos - window->Pos + window->Scroll;
   5188 }
   5189 
   5190 float ImGui::GetCursorPosX()
   5191 {
   5192     ImGuiWindow* window = GetCurrentWindowRead();
   5193     return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
   5194 }
   5195 
   5196 float ImGui::GetCursorPosY()
   5197 {
   5198     ImGuiWindow* window = GetCurrentWindowRead();
   5199     return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
   5200 }
   5201 
   5202 void ImGui::SetCursorPos(const ImVec2& local_pos)
   5203 {
   5204     ImGuiWindow* window = GetCurrentWindow();
   5205     window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
   5206     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
   5207 }
   5208 
   5209 void ImGui::SetCursorPosX(float x)
   5210 {
   5211     ImGuiWindow* window = GetCurrentWindow();
   5212     window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
   5213     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
   5214 }
   5215 
   5216 void ImGui::SetCursorPosY(float y)
   5217 {
   5218     ImGuiWindow* window = GetCurrentWindow();
   5219     window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
   5220     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
   5221 }
   5222 
   5223 ImVec2 ImGui::GetCursorStartPos()
   5224 {
   5225     ImGuiWindow* window = GetCurrentWindowRead();
   5226     return window->DC.CursorStartPos - window->Pos;
   5227 }
   5228 
   5229 ImVec2 ImGui::GetCursorScreenPos()
   5230 {
   5231     ImGuiWindow* window = GetCurrentWindowRead();
   5232     return window->DC.CursorPos;
   5233 }
   5234 
   5235 void ImGui::SetCursorScreenPos(const ImVec2& screen_pos)
   5236 {
   5237     ImGuiWindow* window = GetCurrentWindow();
   5238     window->DC.CursorPos = screen_pos;
   5239     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
   5240 }
   5241 
   5242 float ImGui::GetScrollX()
   5243 {
   5244     return GImGui->CurrentWindow->Scroll.x;
   5245 }
   5246 
   5247 float ImGui::GetScrollY()
   5248 {
   5249     return GImGui->CurrentWindow->Scroll.y;
   5250 }
   5251 
   5252 float ImGui::GetScrollMaxX()
   5253 {
   5254     ImGuiWindow* window = GetCurrentWindowRead();
   5255     return window->SizeContents.x - window->SizeFull.x - window->ScrollbarSizes.x;
   5256 }
   5257 
   5258 float ImGui::GetScrollMaxY()
   5259 {
   5260     ImGuiWindow* window = GetCurrentWindowRead();
   5261     return window->SizeContents.y - window->SizeFull.y - window->ScrollbarSizes.y;
   5262 }
   5263 
   5264 void ImGui::SetScrollX(float scroll_x)
   5265 {
   5266     ImGuiWindow* window = GetCurrentWindow();
   5267     window->ScrollTarget.x = scroll_x;
   5268     window->ScrollTargetCenterRatio.x = 0.0f;
   5269 }
   5270 
   5271 void ImGui::SetScrollY(float scroll_y)
   5272 {
   5273     ImGuiWindow* window = GetCurrentWindow();
   5274     window->ScrollTarget.y = scroll_y + window->TitleBarHeight() + window->MenuBarHeight(); // title bar height canceled out when using ScrollTargetRelY
   5275     window->ScrollTargetCenterRatio.y = 0.0f;
   5276 }
   5277 
   5278 void ImGui::SetScrollFromPosY(float pos_y, float center_y_ratio)
   5279 {
   5280     // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
   5281     ImGuiWindow* window = GetCurrentWindow();
   5282     IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
   5283     window->ScrollTarget.y = (float)(int)(pos_y + window->Scroll.y);
   5284     if (center_y_ratio <= 0.0f && window->ScrollTarget.y <= window->WindowPadding.y)    // Minor hack to make "scroll to top" take account of WindowPadding, else it would scroll to (WindowPadding.y - ItemSpacing.y)
   5285         window->ScrollTarget.y = 0.0f;
   5286     window->ScrollTargetCenterRatio.y = center_y_ratio;
   5287 }
   5288 
   5289 // center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
   5290 void ImGui::SetScrollHere(float center_y_ratio)
   5291 {
   5292     ImGuiWindow* window = GetCurrentWindow();
   5293     float target_y = window->DC.CursorPosPrevLine.y + (window->DC.PrevLineHeight * center_y_ratio) + (GImGui->Style.ItemSpacing.y * (center_y_ratio - 0.5f) * 2.0f); // Precisely aim above, in the middle or below the last line.
   5294     SetScrollFromPosY(target_y - window->Pos.y, center_y_ratio);
   5295 }
   5296 
   5297 void ImGui::SetKeyboardFocusHere(int offset)
   5298 {
   5299     ImGuiWindow* window = GetCurrentWindow();
   5300     window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset;
   5301     window->FocusIdxTabRequestNext = INT_MAX;
   5302 }
   5303 
   5304 void ImGui::SetStateStorage(ImGuiStorage* tree)
   5305 {
   5306     ImGuiWindow* window = GetCurrentWindow();
   5307     window->DC.StateStorage = tree ? tree : &window->StateStorage;
   5308 }
   5309 
   5310 ImGuiStorage* ImGui::GetStateStorage()
   5311 {
   5312     ImGuiWindow* window = GetCurrentWindowRead();
   5313     return window->DC.StateStorage;
   5314 }
   5315 
   5316 void ImGui::TextV(const char* fmt, va_list args)
   5317 {
   5318     ImGuiWindow* window = GetCurrentWindow();
   5319     if (window->SkipItems)
   5320         return;
   5321 
   5322     ImGuiContext& g = *GImGui;
   5323     const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
   5324     TextUnformatted(g.TempBuffer, text_end);
   5325 }
   5326 
   5327 void ImGui::Text(const char* fmt, ...)
   5328 {
   5329     va_list args;
   5330     va_start(args, fmt);
   5331     TextV(fmt, args);
   5332     va_end(args);
   5333 }
   5334 
   5335 void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args)
   5336 {
   5337     PushStyleColor(ImGuiCol_Text, col);
   5338     TextV(fmt, args);
   5339     PopStyleColor();
   5340 }
   5341 
   5342 void ImGui::TextColored(const ImVec4& col, const char* fmt, ...)
   5343 {
   5344     va_list args;
   5345     va_start(args, fmt);
   5346     TextColoredV(col, fmt, args);
   5347     va_end(args);
   5348 }
   5349 
   5350 void ImGui::TextDisabledV(const char* fmt, va_list args)
   5351 {
   5352     PushStyleColor(ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled]);
   5353     TextV(fmt, args);
   5354     PopStyleColor();
   5355 }
   5356 
   5357 void ImGui::TextDisabled(const char* fmt, ...)
   5358 {
   5359     va_list args;
   5360     va_start(args, fmt);
   5361     TextDisabledV(fmt, args);
   5362     va_end(args);
   5363 }
   5364 
   5365 void ImGui::TextWrappedV(const char* fmt, va_list args)
   5366 {
   5367     bool need_wrap = (GImGui->CurrentWindow->DC.TextWrapPos < 0.0f);    // Keep existing wrap position is one ia already set
   5368     if (need_wrap) PushTextWrapPos(0.0f);
   5369     TextV(fmt, args);
   5370     if (need_wrap) PopTextWrapPos();
   5371 }
   5372 
   5373 void ImGui::TextWrapped(const char* fmt, ...)
   5374 {
   5375     va_list args;
   5376     va_start(args, fmt);
   5377     TextWrappedV(fmt, args);
   5378     va_end(args);
   5379 }
   5380 
   5381 void ImGui::TextUnformatted(const char* text, const char* text_end)
   5382 {
   5383     ImGuiWindow* window = GetCurrentWindow();
   5384     if (window->SkipItems)
   5385         return;
   5386 
   5387     ImGuiContext& g = *GImGui;
   5388     IM_ASSERT(text != NULL);
   5389     const char* text_begin = text;
   5390     if (text_end == NULL)
   5391         text_end = text + strlen(text); // FIXME-OPT
   5392 
   5393     const float wrap_pos_x = window->DC.TextWrapPos;
   5394     const bool wrap_enabled = wrap_pos_x >= 0.0f;
   5395     if (text_end - text > 2000 && !wrap_enabled)
   5396     {
   5397         // Long text!
   5398         // Perform manual coarse clipping to optimize for long multi-line text
   5399         // From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled.
   5400         // We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line.
   5401         const char* line = text;
   5402         const float line_height = GetTextLineHeight();
   5403         const ImVec2 text_pos = window->DC.CursorPos + ImVec2(0.0f, window->DC.CurrentLineTextBaseOffset);
   5404         const ImRect clip_rect = window->ClipRect;
   5405         ImVec2 text_size(0,0);
   5406 
   5407         if (text_pos.y <= clip_rect.Max.y)
   5408         {
   5409             ImVec2 pos = text_pos;
   5410 
   5411             // Lines to skip (can't skip when logging text)
   5412             if (!g.LogEnabled)
   5413             {
   5414                 int lines_skippable = (int)((clip_rect.Min.y - text_pos.y) / line_height);
   5415                 if (lines_skippable > 0)
   5416                 {
   5417                     int lines_skipped = 0;
   5418                     while (line < text_end && lines_skipped < lines_skippable)
   5419                     {
   5420                         const char* line_end = strchr(line, '\n');
   5421                         if (!line_end)
   5422                             line_end = text_end;
   5423                         line = line_end + 1;
   5424                         lines_skipped++;
   5425                     }
   5426                     pos.y += lines_skipped * line_height;
   5427                 }
   5428             }
   5429 
   5430             // Lines to render
   5431             if (line < text_end)
   5432             {
   5433                 ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height));
   5434                 while (line < text_end)
   5435                 {
   5436                     const char* line_end = strchr(line, '\n');
   5437                     if (IsClippedEx(line_rect, NULL, false))
   5438                         break;
   5439 
   5440                     const ImVec2 line_size = CalcTextSize(line, line_end, false);
   5441                     text_size.x = ImMax(text_size.x, line_size.x);
   5442                     RenderText(pos, line, line_end, false);
   5443                     if (!line_end)
   5444                         line_end = text_end;
   5445                     line = line_end + 1;
   5446                     line_rect.Min.y += line_height;
   5447                     line_rect.Max.y += line_height;
   5448                     pos.y += line_height;
   5449                 }
   5450 
   5451                 // Count remaining lines
   5452                 int lines_skipped = 0;
   5453                 while (line < text_end)
   5454                 {
   5455                     const char* line_end = strchr(line, '\n');
   5456                     if (!line_end)
   5457                         line_end = text_end;
   5458                     line = line_end + 1;
   5459                     lines_skipped++;
   5460                 }
   5461                 pos.y += lines_skipped * line_height;
   5462             }
   5463 
   5464             text_size.y += (pos - text_pos).y;
   5465         }
   5466 
   5467         ImRect bb(text_pos, text_pos + text_size);
   5468         ItemSize(bb);
   5469         ItemAdd(bb, NULL);
   5470     }
   5471     else
   5472     {
   5473         const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f;
   5474         const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width);
   5475 
   5476         // Account of baseline offset
   5477         ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrentLineTextBaseOffset);
   5478         ImRect bb(text_pos, text_pos + text_size);
   5479         ItemSize(text_size);
   5480         if (!ItemAdd(bb, NULL))
   5481             return;
   5482 
   5483         // Render (we don't hide text after ## in this end-user function)
   5484         RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width);
   5485     }
   5486 }
   5487 
   5488 void ImGui::AlignFirstTextHeightToWidgets()
   5489 {
   5490     ImGuiWindow* window = GetCurrentWindow();
   5491     if (window->SkipItems)
   5492         return;
   5493 
   5494     // Declare a dummy item size to that upcoming items that are smaller will center-align on the newly expanded line height.
   5495     ImGuiContext& g = *GImGui;
   5496     ItemSize(ImVec2(0, g.FontSize + g.Style.FramePadding.y*2), g.Style.FramePadding.y);
   5497     SameLine(0, 0);
   5498 }
   5499 
   5500 // Add a label+text combo aligned to other label+value widgets
   5501 void ImGui::LabelTextV(const char* label, const char* fmt, va_list args)
   5502 {
   5503     ImGuiWindow* window = GetCurrentWindow();
   5504     if (window->SkipItems)
   5505         return;
   5506 
   5507     ImGuiContext& g = *GImGui;
   5508     const ImGuiStyle& style = g.Style;
   5509     const float w = CalcItemWidth();
   5510 
   5511     const ImVec2 label_size = CalcTextSize(label, NULL, true);
   5512     const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2));
   5513     const ImRect total_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x : 0.0f), style.FramePadding.y*2) + label_size);
   5514     ItemSize(total_bb, style.FramePadding.y);
   5515     if (!ItemAdd(total_bb, NULL))
   5516         return;
   5517 
   5518     // Render
   5519     const char* value_text_begin = &g.TempBuffer[0];
   5520     const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
   5521     RenderTextClipped(value_bb.Min, value_bb.Max, value_text_begin, value_text_end, NULL, ImVec2(0.0f,0.5f));
   5522     if (label_size.x > 0.0f)
   5523         RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label);
   5524 }
   5525 
   5526 void ImGui::LabelText(const char* label, const char* fmt, ...)
   5527 {
   5528     va_list args;
   5529     va_start(args, fmt);
   5530     LabelTextV(label, fmt, args);
   5531     va_end(args);
   5532 }
   5533 
   5534 static inline bool IsWindowContentHoverable(ImGuiWindow* window)
   5535 {
   5536     // An active popup disable hovering on other windows (apart from its own children)
   5537     ImGuiContext& g = *GImGui;
   5538     if (ImGuiWindow* focused_window = g.FocusedWindow)
   5539         if (ImGuiWindow* focused_root_window = focused_window->RootWindow)
   5540             if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) != 0 && focused_root_window->WasActive && focused_root_window != window->RootWindow)
   5541                 return false;
   5542 
   5543     return true;
   5544 }
   5545 
   5546 bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags)
   5547 {
   5548     ImGuiContext& g = *GImGui;
   5549     ImGuiWindow* window = GetCurrentWindow();
   5550 
   5551     if (flags & ImGuiButtonFlags_Disabled)
   5552     {
   5553         if (out_hovered) *out_hovered = false;
   5554         if (out_held) *out_held = false;
   5555         if (g.ActiveId == id) ClearActiveID();
   5556         return false;
   5557     }
   5558 
   5559     if ((flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick)) == 0)
   5560         flags |= ImGuiButtonFlags_PressedOnClickRelease;
   5561 
   5562     bool pressed = false;
   5563     bool hovered = IsHovered(bb, id, (flags & ImGuiButtonFlags_FlattenChilds) != 0);
   5564     if (hovered)
   5565     {
   5566         SetHoveredID(id);
   5567         if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt))
   5568         {
   5569             //                        | CLICKING        | HOLDING with ImGuiButtonFlags_Repeat
   5570             // PressedOnClickRelease  |  <on release>*  |  <on repeat> <on repeat> .. (NOT on release)  <-- MOST COMMON! (*) only if both click/release were over bounds
   5571             // PressedOnClick         |  <on click>     |  <on click> <on repeat> <on repeat> ..
   5572             // PressedOnRelease       |  <on release>   |  <on repeat> <on repeat> .. (NOT on release)
   5573             // PressedOnDoubleClick   |  <on dclick>    |  <on dclick> <on repeat> <on repeat> ..
   5574             if ((flags & ImGuiButtonFlags_PressedOnClickRelease) && g.IO.MouseClicked[0])
   5575             {
   5576                 SetActiveID(id, window); // Hold on ID
   5577                 FocusWindow(window);
   5578                 g.ActiveIdClickOffset = g.IO.MousePos - bb.Min;
   5579             }
   5580             if (((flags & ImGuiButtonFlags_PressedOnClick) && g.IO.MouseClicked[0]) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDoubleClicked[0]))
   5581             {
   5582                 pressed = true;
   5583                 ClearActiveID();
   5584                 FocusWindow(window);
   5585             }
   5586             if ((flags & ImGuiButtonFlags_PressedOnRelease) && g.IO.MouseReleased[0])
   5587             {
   5588                 if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay))  // Repeat mode trumps <on release>
   5589                     pressed = true;
   5590                 ClearActiveID();
   5591             }
   5592 
   5593             // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above). 
   5594             // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings.
   5595             if ((flags & ImGuiButtonFlags_Repeat) && g.ActiveId == id && g.IO.MouseDownDuration[0] > 0.0f && IsMouseClicked(0, true))
   5596                 pressed = true;
   5597         }
   5598     }
   5599 
   5600     bool held = false;
   5601     if (g.ActiveId == id)
   5602     {
   5603         if (g.IO.MouseDown[0])
   5604         {
   5605             held = true;
   5606         }
   5607         else
   5608         {
   5609             if (hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease))
   5610                 if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay))  // Repeat mode trumps <on release>
   5611                     pressed = true;
   5612             ClearActiveID();
   5613         }
   5614     }
   5615 
   5616     // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one.
   5617     if (hovered && (flags & ImGuiButtonFlags_AllowOverlapMode) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0))
   5618         hovered = pressed = held = false;
   5619 
   5620     if (out_hovered) *out_hovered = hovered;
   5621     if (out_held) *out_held = held;
   5622 
   5623     return pressed;
   5624 }
   5625 
   5626 bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags)
   5627 {
   5628     ImGuiWindow* window = GetCurrentWindow();
   5629     if (window->SkipItems)
   5630         return false;
   5631 
   5632     ImGuiContext& g = *GImGui;
   5633     const ImGuiStyle& style = g.Style;
   5634     const ImGuiID id = window->GetID(label);
   5635     const ImVec2 label_size = CalcTextSize(label, NULL, true);
   5636 
   5637     ImVec2 pos = window->DC.CursorPos;
   5638     if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrentLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag)
   5639         pos.y += window->DC.CurrentLineTextBaseOffset - style.FramePadding.y;
   5640     ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f);
   5641 
   5642     const ImRect bb(pos, pos + size);
   5643     ItemSize(bb, style.FramePadding.y);
   5644     if (!ItemAdd(bb, &id))
   5645         return false;
   5646 
   5647     if (window->DC.ButtonRepeat) flags |= ImGuiButtonFlags_Repeat;
   5648     bool hovered, held;
   5649     bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
   5650 
   5651     // Render
   5652     const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
   5653     RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
   5654     RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb);
   5655 
   5656     // Automatically close popups
   5657     //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
   5658     //    CloseCurrentPopup();
   5659 
   5660     return pressed;
   5661 }
   5662 
   5663 bool ImGui::Button(const char* label, const ImVec2& size_arg)
   5664 {
   5665     return ButtonEx(label, size_arg, 0);
   5666 }
   5667 
   5668 // Small buttons fits within text without additional vertical spacing.
   5669 bool ImGui::SmallButton(const char* label)
   5670 {
   5671     ImGuiContext& g = *GImGui;
   5672     float backup_padding_y = g.Style.FramePadding.y;
   5673     g.Style.FramePadding.y = 0.0f;
   5674     bool pressed = ButtonEx(label, ImVec2(0,0), ImGuiButtonFlags_AlignTextBaseLine);
   5675     g.Style.FramePadding.y = backup_padding_y;
   5676     return pressed;
   5677 }
   5678 
   5679 // Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack.
   5680 // Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id)
   5681 bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg)
   5682 {
   5683     ImGuiWindow* window = GetCurrentWindow();
   5684     if (window->SkipItems)
   5685         return false;
   5686 
   5687     const ImGuiID id = window->GetID(str_id);
   5688     ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f);
   5689     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
   5690     ItemSize(bb);
   5691     if (!ItemAdd(bb, &id))
   5692         return false;
   5693 
   5694     bool hovered, held;
   5695     bool pressed = ButtonBehavior(bb, id, &hovered, &held);
   5696 
   5697     return pressed;
   5698 }
   5699 
   5700 // Upper-right button to close a window.
   5701 bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius)
   5702 {
   5703     ImGuiWindow* window = GetCurrentWindow();
   5704 
   5705     const ImRect bb(pos - ImVec2(radius,radius), pos + ImVec2(radius,radius));
   5706 
   5707     bool hovered, held;
   5708     bool pressed = ButtonBehavior(bb, id, &hovered, &held);
   5709 
   5710     // Render
   5711     const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_CloseButtonActive : hovered ? ImGuiCol_CloseButtonHovered : ImGuiCol_CloseButton);
   5712     const ImVec2 center = bb.GetCenter();
   5713     window->DrawList->AddCircleFilled(center, ImMax(2.0f, radius), col, 12);
   5714 
   5715     const float cross_extent = (radius * 0.7071f) - 1.0f;
   5716     if (hovered)
   5717     {
   5718         window->DrawList->AddLine(center + ImVec2(+cross_extent,+cross_extent), center + ImVec2(-cross_extent,-cross_extent), GetColorU32(ImGuiCol_Text));
   5719         window->DrawList->AddLine(center + ImVec2(+cross_extent,-cross_extent), center + ImVec2(-cross_extent,+cross_extent), GetColorU32(ImGuiCol_Text));
   5720     }
   5721 
   5722     return pressed;
   5723 }
   5724 
   5725 void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
   5726 {
   5727     ImGuiWindow* window = GetCurrentWindow();
   5728     if (window->SkipItems)
   5729         return;
   5730 
   5731     ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
   5732     if (border_col.w > 0.0f)
   5733         bb.Max += ImVec2(2,2);
   5734     ItemSize(bb);
   5735     if (!ItemAdd(bb, NULL))
   5736         return;
   5737 
   5738     if (border_col.w > 0.0f)
   5739     {
   5740         window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f);
   5741         window->DrawList->AddImage(user_texture_id, bb.Min+ImVec2(1,1), bb.Max-ImVec2(1,1), uv0, uv1, GetColorU32(tint_col));
   5742     }
   5743     else
   5744     {
   5745         window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, GetColorU32(tint_col));
   5746     }
   5747 }
   5748 
   5749 // frame_padding < 0: uses FramePadding from style (default)
   5750 // frame_padding = 0: no framing
   5751 // frame_padding > 0: set framing size
   5752 // The color used are the button colors.
   5753 bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col)
   5754 {
   5755     ImGuiWindow* window = GetCurrentWindow();
   5756     if (window->SkipItems)
   5757         return false;
   5758 
   5759     ImGuiContext& g = *GImGui;
   5760     const ImGuiStyle& style = g.Style;
   5761 
   5762     // Default to using texture ID as ID. User can still push string/integer prefixes.
   5763     // We could hash the size/uv to create a unique ID but that would prevent the user from animating UV.
   5764     PushID((void *)user_texture_id);
   5765     const ImGuiID id = window->GetID("#image");
   5766     PopID();
   5767 
   5768     const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : style.FramePadding;
   5769     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding*2);
   5770     const ImRect image_bb(window->DC.CursorPos + padding, window->DC.CursorPos + padding + size);
   5771     ItemSize(bb);
   5772     if (!ItemAdd(bb, &id))
   5773         return false;
   5774 
   5775     bool hovered, held;
   5776     bool pressed = ButtonBehavior(bb, id, &hovered, &held);
   5777 
   5778     // Render
   5779     const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
   5780     RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding));
   5781     if (bg_col.w > 0.0f)
   5782         window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, GetColorU32(bg_col));
   5783     window->DrawList->AddImage(user_texture_id, image_bb.Min, image_bb.Max, uv0, uv1, GetColorU32(tint_col));
   5784 
   5785     return pressed;
   5786 }
   5787 
   5788 // Start logging ImGui output to TTY
   5789 void ImGui::LogToTTY(int max_depth)
   5790 {
   5791     ImGuiContext& g = *GImGui;
   5792     if (g.LogEnabled)
   5793         return;
   5794     ImGuiWindow* window = GetCurrentWindowRead();
   5795 
   5796     g.LogEnabled = true;
   5797     g.LogFile = stdout;
   5798     g.LogStartDepth = window->DC.TreeDepth;
   5799     if (max_depth >= 0)
   5800         g.LogAutoExpandMaxDepth = max_depth;
   5801 }
   5802 
   5803 // Start logging ImGui output to given file
   5804 void ImGui::LogToFile(int max_depth, const char* filename)
   5805 {
   5806     ImGuiContext& g = *GImGui;
   5807     if (g.LogEnabled)
   5808         return;
   5809     ImGuiWindow* window = GetCurrentWindowRead();
   5810 
   5811     if (!filename)
   5812     {
   5813         filename = g.IO.LogFilename;
   5814         if (!filename)
   5815             return;
   5816     }
   5817 
   5818     g.LogFile = ImFileOpen(filename, "ab");
   5819     if (!g.LogFile)
   5820     {
   5821         IM_ASSERT(g.LogFile != NULL); // Consider this an error
   5822         return;
   5823     }
   5824     g.LogEnabled = true;
   5825     g.LogStartDepth = window->DC.TreeDepth;
   5826     if (max_depth >= 0)
   5827         g.LogAutoExpandMaxDepth = max_depth;
   5828 }
   5829 
   5830 // Start logging ImGui output to clipboard
   5831 void ImGui::LogToClipboard(int max_depth)
   5832 {
   5833     ImGuiContext& g = *GImGui;
   5834     if (g.LogEnabled)
   5835         return;
   5836     ImGuiWindow* window = GetCurrentWindowRead();
   5837 
   5838     g.LogEnabled = true;
   5839     g.LogFile = NULL;
   5840     g.LogStartDepth = window->DC.TreeDepth;
   5841     if (max_depth >= 0)
   5842         g.LogAutoExpandMaxDepth = max_depth;
   5843 }
   5844 
   5845 void ImGui::LogFinish()
   5846 {
   5847     ImGuiContext& g = *GImGui;
   5848     if (!g.LogEnabled)
   5849         return;
   5850 
   5851     LogText(IM_NEWLINE);
   5852     g.LogEnabled = false;
   5853     if (g.LogFile != NULL)
   5854     {
   5855         if (g.LogFile == stdout)
   5856             fflush(g.LogFile);
   5857         else
   5858             fclose(g.LogFile);
   5859         g.LogFile = NULL;
   5860     }
   5861     if (g.LogClipboard->size() > 1)
   5862     {
   5863         SetClipboardText(g.LogClipboard->begin());
   5864         g.LogClipboard->clear();
   5865     }
   5866 }
   5867 
   5868 // Helper to display logging buttons
   5869 void ImGui::LogButtons()
   5870 {
   5871     ImGuiContext& g = *GImGui;
   5872 
   5873     PushID("LogButtons");
   5874     const bool log_to_tty = Button("Log To TTY"); SameLine();
   5875     const bool log_to_file = Button("Log To File"); SameLine();
   5876     const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
   5877     PushItemWidth(80.0f);
   5878     PushAllowKeyboardFocus(false);
   5879     SliderInt("Depth", &g.LogAutoExpandMaxDepth, 0, 9, NULL);
   5880     PopAllowKeyboardFocus();
   5881     PopItemWidth();
   5882     PopID();
   5883 
   5884     // Start logging at the end of the function so that the buttons don't appear in the log
   5885     if (log_to_tty)
   5886         LogToTTY(g.LogAutoExpandMaxDepth);
   5887     if (log_to_file)
   5888         LogToFile(g.LogAutoExpandMaxDepth, g.IO.LogFilename);
   5889     if (log_to_clipboard)
   5890         LogToClipboard(g.LogAutoExpandMaxDepth);
   5891 }
   5892 
   5893 bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags)
   5894 {
   5895     if (flags & ImGuiTreeNodeFlags_Leaf)
   5896         return true;
   5897 
   5898     // We only write to the tree storage if the user clicks (or explicitely use SetNextTreeNode*** functions)
   5899     ImGuiContext& g = *GImGui;
   5900     ImGuiWindow* window = g.CurrentWindow;
   5901     ImGuiStorage* storage = window->DC.StateStorage;
   5902 
   5903     bool is_open;
   5904     if (g.SetNextTreeNodeOpenCond != 0)
   5905     {
   5906         if (g.SetNextTreeNodeOpenCond & ImGuiSetCond_Always)
   5907         {
   5908             is_open = g.SetNextTreeNodeOpenVal;
   5909             storage->SetInt(id, is_open);
   5910         }
   5911         else
   5912         {
   5913             // We treat ImGuiSetCondition_Once and ImGuiSetCondition_FirstUseEver the same because tree node state are not saved persistently.
   5914             const int stored_value = storage->GetInt(id, -1);
   5915             if (stored_value == -1)
   5916             {
   5917                 is_open = g.SetNextTreeNodeOpenVal;
   5918                 storage->SetInt(id, is_open);
   5919             }
   5920             else
   5921             {
   5922                 is_open = stored_value != 0;
   5923             }
   5924         }
   5925         g.SetNextTreeNodeOpenCond = 0;
   5926     }
   5927     else
   5928     {
   5929         is_open = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0;
   5930     }
   5931 
   5932     // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior).
   5933     // NB- If we are above max depth we still allow manually opened nodes to be logged.
   5934     if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth)
   5935         is_open = true;
   5936 
   5937     return is_open;
   5938 }
   5939 
   5940 bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end)
   5941 {
   5942     ImGuiWindow* window = GetCurrentWindow();
   5943     if (window->SkipItems)
   5944         return false;
   5945 
   5946     ImGuiContext& g = *GImGui;
   5947     const ImGuiStyle& style = g.Style;
   5948     const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0;
   5949     const ImVec2 padding = display_frame ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f);
   5950 
   5951     if (!label_end)
   5952         label_end = FindRenderedTextEnd(label);
   5953     const ImVec2 label_size = CalcTextSize(label, label_end, false);
   5954 
   5955     // We vertically grow up to current line height up the typical widget height.
   5956     const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset - padding.y); // Latch before ItemSize changes it
   5957     const float frame_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2);
   5958     ImRect bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height));
   5959     if (display_frame)
   5960     {
   5961         // Framed header expand a little outside the default padding
   5962         bb.Min.x -= (float)(int)(window->WindowPadding.x*0.5f) - 1;
   5963         bb.Max.x += (float)(int)(window->WindowPadding.x*0.5f) - 1;
   5964     }
   5965 
   5966     const float text_offset_x = (g.FontSize + (display_frame ? padding.x*3 : padding.x*2));   // Collapser arrow width + Spacing
   5967     const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x*2 : 0.0f);   // Include collapser
   5968     ItemSize(ImVec2(text_width, frame_height), text_base_offset_y);
   5969 
   5970     // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing
   5971     // (Ideally we'd want to add a flag for the user to specify we want want the hit test to be done up to the right side of the content or not)
   5972     const ImRect interact_bb = display_frame ? bb : ImRect(bb.Min.x, bb.Min.y, bb.Min.x + text_width + style.ItemSpacing.x*2, bb.Max.y);
   5973     bool is_open = TreeNodeBehaviorIsOpen(id, flags);
   5974     if (!ItemAdd(interact_bb, &id))
   5975     {
   5976         if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
   5977             TreePushRawID(id);
   5978         return is_open;
   5979     }
   5980 
   5981     // Flags that affects opening behavior:
   5982     // - 0(default) ..................... single-click anywhere to open
   5983     // - OpenOnDoubleClick .............. double-click anywhere to open
   5984     // - OpenOnArrow .................... single-click on arrow to open
   5985     // - OpenOnDoubleClick|OpenOnArrow .. single-click on arrow or double-click anywhere to open
   5986     ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers | ((flags & ImGuiTreeNodeFlags_AllowOverlapMode) ? ImGuiButtonFlags_AllowOverlapMode : 0);
   5987     if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
   5988         button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0);
   5989     bool hovered, held, pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags);
   5990     if (pressed && !(flags & ImGuiTreeNodeFlags_Leaf))
   5991     {
   5992         bool toggled = !(flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick));
   5993         if (flags & ImGuiTreeNodeFlags_OpenOnArrow)
   5994             toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y));
   5995         if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
   5996             toggled |= g.IO.MouseDoubleClicked[0];
   5997         if (toggled)
   5998         {
   5999             is_open = !is_open;
   6000             window->DC.StateStorage->SetInt(id, is_open);
   6001         }
   6002     }
   6003     if (flags & ImGuiTreeNodeFlags_AllowOverlapMode)
   6004         SetItemAllowOverlap();
   6005 
   6006     // Render
   6007     const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
   6008     const ImVec2 text_pos = bb.Min + ImVec2(text_offset_x, padding.y + text_base_offset_y);
   6009     if (display_frame)
   6010     {
   6011         // Framed type
   6012         RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
   6013         RenderCollapseTriangle(bb.Min + padding + ImVec2(0.0f, text_base_offset_y), is_open, 1.0f);
   6014         if (g.LogEnabled)
   6015         {
   6016             // NB: '##' is normally used to hide text (as a library-wide feature), so we need to specify the text range to make sure the ## aren't stripped out here.
   6017             const char log_prefix[] = "\n##";
   6018             const char log_suffix[] = "##";
   6019             LogRenderedText(text_pos, log_prefix, log_prefix+3);
   6020             RenderTextClipped(text_pos, bb.Max, label, label_end, &label_size);
   6021             LogRenderedText(text_pos, log_suffix+1, log_suffix+3);
   6022         }
   6023         else
   6024         {
   6025             RenderTextClipped(text_pos, bb.Max, label, label_end, &label_size);
   6026         }
   6027     }
   6028     else
   6029     {
   6030         // Unframed typed for tree nodes
   6031         if (hovered || (flags & ImGuiTreeNodeFlags_Selected))
   6032             RenderFrame(bb.Min, bb.Max, col, false);
   6033 
   6034         if (flags & ImGuiTreeNodeFlags_Bullet)
   6035             RenderBullet(bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y));
   6036         else if (!(flags & ImGuiTreeNodeFlags_Leaf))
   6037             RenderCollapseTriangle(bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), is_open, 0.70f);
   6038         if (g.LogEnabled)
   6039             LogRenderedText(text_pos, ">");
   6040         RenderText(text_pos, label, label_end, false);
   6041     }
   6042 
   6043     if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
   6044         TreePushRawID(id);
   6045     return is_open;
   6046 }
   6047 
   6048 // CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag).
   6049 // This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode().
   6050 bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags)
   6051 {
   6052     ImGuiWindow* window = GetCurrentWindow();
   6053     if (window->SkipItems)
   6054         return false;
   6055 
   6056     return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen, label);
   6057 }
   6058 
   6059 bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags)
   6060 {
   6061     ImGuiWindow* window = GetCurrentWindow();
   6062     if (window->SkipItems)
   6063         return false;
   6064 
   6065     if (p_open && !*p_open)
   6066         return false;
   6067 
   6068     ImGuiID id = window->GetID(label);
   6069     bool is_open = TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen | (p_open ? ImGuiTreeNodeFlags_AllowOverlapMode : 0), label);
   6070     if (p_open)
   6071     {
   6072         // Create a small overlapping close button // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc.
   6073         ImGuiContext& g = *GImGui;
   6074         float button_sz = g.FontSize * 0.5f;
   6075         if (CloseButton(window->GetID((void*)(intptr_t)(id+1)), ImVec2(ImMin(window->DC.LastItemRect.Max.x, window->ClipRect.Max.x) - g.Style.FramePadding.x - button_sz, window->DC.LastItemRect.Min.y + g.Style.FramePadding.y + button_sz), button_sz))
   6076             *p_open = false;
   6077     }
   6078 
   6079     return is_open;
   6080 }
   6081 
   6082 bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags)
   6083 {
   6084     ImGuiWindow* window = GetCurrentWindow();
   6085     if (window->SkipItems)
   6086         return false;
   6087 
   6088     return TreeNodeBehavior(window->GetID(label), flags, label, NULL);
   6089 }
   6090 
   6091 bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
   6092 {
   6093     ImGuiWindow* window = GetCurrentWindow();
   6094     if (window->SkipItems)
   6095         return false;
   6096 
   6097     ImGuiContext& g = *GImGui;
   6098     const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
   6099     return TreeNodeBehavior(window->GetID(str_id), flags, g.TempBuffer, label_end);
   6100 }
   6101 
   6102 bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
   6103 {
   6104     ImGuiWindow* window = GetCurrentWindow();
   6105     if (window->SkipItems)
   6106         return false;
   6107 
   6108     ImGuiContext& g = *GImGui;
   6109     const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
   6110     return TreeNodeBehavior(window->GetID(ptr_id), flags, g.TempBuffer, label_end);
   6111 }
   6112 
   6113 bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args)
   6114 {
   6115     return TreeNodeExV(str_id, 0, fmt, args);
   6116 }
   6117 
   6118 bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args)
   6119 {
   6120     return TreeNodeExV(ptr_id, 0, fmt, args);
   6121 }
   6122 
   6123 bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
   6124 {
   6125     va_list args;
   6126     va_start(args, fmt);
   6127     bool is_open = TreeNodeExV(str_id, flags, fmt, args);
   6128     va_end(args);
   6129     return is_open;
   6130 }
   6131 
   6132 bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
   6133 {
   6134     va_list args;
   6135     va_start(args, fmt);
   6136     bool is_open = TreeNodeExV(ptr_id, flags, fmt, args);
   6137     va_end(args);
   6138     return is_open;
   6139 }
   6140 
   6141 bool ImGui::TreeNode(const char* str_id, const char* fmt, ...)
   6142 {
   6143     va_list args;
   6144     va_start(args, fmt);
   6145     bool is_open = TreeNodeExV(str_id, 0, fmt, args);
   6146     va_end(args);
   6147     return is_open;
   6148 }
   6149 
   6150 bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...)
   6151 {
   6152     va_list args;
   6153     va_start(args, fmt);
   6154     bool is_open = TreeNodeExV(ptr_id, 0, fmt, args);
   6155     va_end(args);
   6156     return is_open;
   6157 }
   6158 
   6159 bool ImGui::TreeNode(const char* label)
   6160 {
   6161     ImGuiWindow* window = GetCurrentWindow();
   6162     if (window->SkipItems)
   6163         return false;
   6164     return TreeNodeBehavior(window->GetID(label), 0, label, NULL);
   6165 }
   6166 
   6167 void ImGui::TreeAdvanceToLabelPos()
   6168 {
   6169     ImGuiContext& g = *GImGui;
   6170     g.CurrentWindow->DC.CursorPos.x += GetTreeNodeToLabelSpacing();
   6171 }
   6172 
   6173 // Horizontal distance preceding label when using TreeNode() or Bullet()
   6174 float ImGui::GetTreeNodeToLabelSpacing()
   6175 {
   6176     ImGuiContext& g = *GImGui;
   6177     return g.FontSize + (g.Style.FramePadding.x * 2.0f);
   6178 }
   6179 
   6180 void ImGui::SetNextTreeNodeOpen(bool is_open, ImGuiSetCond cond)
   6181 {
   6182     ImGuiContext& g = *GImGui;
   6183     g.SetNextTreeNodeOpenVal = is_open;
   6184     g.SetNextTreeNodeOpenCond = cond ? cond : ImGuiSetCond_Always;
   6185 }
   6186 
   6187 void ImGui::PushID(const char* str_id)
   6188 {
   6189     ImGuiWindow* window = GetCurrentWindow();
   6190     window->IDStack.push_back(window->GetID(str_id));
   6191 }
   6192 
   6193 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
   6194 {
   6195     ImGuiWindow* window = GetCurrentWindow();
   6196     window->IDStack.push_back(window->GetID(str_id_begin, str_id_end));
   6197 }
   6198 
   6199 void ImGui::PushID(const void* ptr_id)
   6200 {
   6201     ImGuiWindow* window = GetCurrentWindow();
   6202     window->IDStack.push_back(window->GetID(ptr_id));
   6203 }
   6204 
   6205 void ImGui::PushID(int int_id)
   6206 {
   6207     const void* ptr_id = (void*)(intptr_t)int_id;
   6208     ImGuiWindow* window = GetCurrentWindow();
   6209     window->IDStack.push_back(window->GetID(ptr_id));
   6210 }
   6211 
   6212 void ImGui::PopID()
   6213 {
   6214     ImGuiWindow* window = GetCurrentWindow();
   6215     window->IDStack.pop_back();
   6216 }
   6217 
   6218 ImGuiID ImGui::GetID(const char* str_id)
   6219 {
   6220     return GImGui->CurrentWindow->GetID(str_id);
   6221 }
   6222 
   6223 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
   6224 {
   6225     return GImGui->CurrentWindow->GetID(str_id_begin, str_id_end);
   6226 }
   6227 
   6228 ImGuiID ImGui::GetID(const void* ptr_id)
   6229 {
   6230     return GImGui->CurrentWindow->GetID(ptr_id);
   6231 }
   6232 
   6233 void ImGui::Bullet()
   6234 {
   6235     ImGuiWindow* window = GetCurrentWindow();
   6236     if (window->SkipItems)
   6237         return;
   6238 
   6239     ImGuiContext& g = *GImGui;
   6240     const ImGuiStyle& style = g.Style;
   6241     const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize);
   6242     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height));
   6243     ItemSize(bb);
   6244     if (!ItemAdd(bb, NULL))
   6245     {
   6246         SameLine(0, style.FramePadding.x*2);
   6247         return;
   6248     }
   6249 
   6250     // Render and stay on same line
   6251     RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f));
   6252     SameLine(0, style.FramePadding.x*2);
   6253 }
   6254 
   6255 // Text with a little bullet aligned to the typical tree node.
   6256 void ImGui::BulletTextV(const char* fmt, va_list args)
   6257 {
   6258     ImGuiWindow* window = GetCurrentWindow();
   6259     if (window->SkipItems)
   6260         return;
   6261 
   6262     ImGuiContext& g = *GImGui;
   6263     const ImGuiStyle& style = g.Style;
   6264 
   6265     const char* text_begin = g.TempBuffer;
   6266     const char* text_end = text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
   6267     const ImVec2 label_size = CalcTextSize(text_begin, text_end, false);
   6268     const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it
   6269     const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize);
   6270     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x*2) : 0.0f), ImMax(line_height, label_size.y)));  // Empty text doesn't add padding
   6271     ItemSize(bb);
   6272     if (!ItemAdd(bb, NULL))
   6273         return;
   6274 
   6275     // Render
   6276     RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f));
   6277     RenderText(bb.Min+ImVec2(g.FontSize + style.FramePadding.x*2, text_base_offset_y), text_begin, text_end, false);
   6278 }
   6279 
   6280 void ImGui::BulletText(const char* fmt, ...)
   6281 {
   6282     va_list args;
   6283     va_start(args, fmt);
   6284     BulletTextV(fmt, args);
   6285     va_end(args);
   6286 }
   6287 
   6288 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size)
   6289 {
   6290     if (data_type == ImGuiDataType_Int)
   6291         ImFormatString(buf, buf_size, display_format, *(int*)data_ptr);
   6292     else if (data_type == ImGuiDataType_Float)
   6293         ImFormatString(buf, buf_size, display_format, *(float*)data_ptr);
   6294 }
   6295 
   6296 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size)
   6297 {
   6298     if (data_type == ImGuiDataType_Int)
   6299     {
   6300         if (decimal_precision < 0)
   6301             ImFormatString(buf, buf_size, "%d", *(int*)data_ptr);
   6302         else
   6303             ImFormatString(buf, buf_size, "%.*d", decimal_precision, *(int*)data_ptr);
   6304     }
   6305     else if (data_type == ImGuiDataType_Float)
   6306     {
   6307         if (decimal_precision < 0)
   6308             ImFormatString(buf, buf_size, "%f", *(float*)data_ptr);     // Ideally we'd have a minimum decimal precision of 1 to visually denote that it is a float, while hiding non-significant digits?
   6309         else
   6310             ImFormatString(buf, buf_size, "%.*f", decimal_precision, *(float*)data_ptr);
   6311     }
   6312 }
   6313 
   6314 static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2)// Store into value1
   6315 {
   6316     if (data_type == ImGuiDataType_Int)
   6317     {
   6318         if (op == '+')
   6319             *(int*)value1 = *(int*)value1 + *(const int*)value2;
   6320         else if (op == '-')
   6321             *(int*)value1 = *(int*)value1 - *(const int*)value2;
   6322     }
   6323     else if (data_type == ImGuiDataType_Float)
   6324     {
   6325         if (op == '+')
   6326             *(float*)value1 = *(float*)value1 + *(const float*)value2;
   6327         else if (op == '-')
   6328             *(float*)value1 = *(float*)value1 - *(const float*)value2;
   6329     }
   6330 }
   6331 
   6332 // User can input math operators (e.g. +100) to edit a numerical values.
   6333 static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format)
   6334 {
   6335     while (ImCharIsSpace(*buf))
   6336         buf++;
   6337 
   6338     // We don't support '-' op because it would conflict with inputing negative value.
   6339     // Instead you can use +-100 to subtract from an existing value
   6340     char op = buf[0];
   6341     if (op == '+' || op == '*' || op == '/')
   6342     {
   6343         buf++;
   6344         while (ImCharIsSpace(*buf))
   6345             buf++;
   6346     }
   6347     else
   6348     {
   6349         op = 0;
   6350     }
   6351     if (!buf[0])
   6352         return false;
   6353 
   6354     if (data_type == ImGuiDataType_Int)
   6355     {
   6356         if (!scalar_format)
   6357             scalar_format = "%d";
   6358         int* v = (int*)data_ptr;
   6359         const int old_v = *v;
   6360         int arg0 = *v;
   6361         if (op && sscanf(initial_value_buf, scalar_format, &arg0) < 1)
   6362             return false;
   6363 
   6364         // Store operand in a float so we can use fractional value for multipliers (*1.1), but constant always parsed as integer so we can fit big integers (e.g. 2000000003) past float precision
   6365         float arg1 = 0.0f;
   6366         if (op == '+')      { if (sscanf(buf, "%f", &arg1) == 1) *v = (int)(arg0 + arg1); }                // Add (use "+-" to subtract)
   6367         else if (op == '*') { if (sscanf(buf, "%f", &arg1) == 1) *v = (int)(arg0 * arg1); }                // Multiply
   6368         else if (op == '/') { if (sscanf(buf, "%f", &arg1) == 1 && arg1 != 0.0f) *v = (int)(arg0 / arg1); }// Divide
   6369         else                { if (sscanf(buf, scalar_format, &arg0) == 1) *v = arg0; }                     // Assign constant
   6370         return (old_v != *v);
   6371     }
   6372     else if (data_type == ImGuiDataType_Float)
   6373     {
   6374         // For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in
   6375         scalar_format = "%f";
   6376         float* v = (float*)data_ptr;
   6377         const float old_v = *v;
   6378         float arg0 = *v;
   6379         if (op && sscanf(initial_value_buf, scalar_format, &arg0) < 1)
   6380             return false;
   6381 
   6382         float arg1 = 0.0f;
   6383         if (sscanf(buf, scalar_format, &arg1) < 1)
   6384             return false;
   6385         if (op == '+')      { *v = arg0 + arg1; }                    // Add (use "+-" to subtract)
   6386         else if (op == '*') { *v = arg0 * arg1; }                    // Multiply
   6387         else if (op == '/') { if (arg1 != 0.0f) *v = arg0 / arg1; }  // Divide
   6388         else                { *v = arg1; }                           // Assign constant
   6389         return (old_v != *v);
   6390     }
   6391 
   6392     return false;
   6393 }
   6394 
   6395 // Create text input in place of a slider (when CTRL+Clicking on slider)
   6396 bool ImGui::InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label, ImGuiDataType data_type, void* data_ptr, ImGuiID id, int decimal_precision)
   6397 {
   6398     ImGuiContext& g = *GImGui;
   6399     ImGuiWindow* window = GetCurrentWindow();
   6400 
   6401     // Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen)
   6402     SetActiveID(g.ScalarAsInputTextId, window);
   6403     SetHoveredID(0);
   6404     FocusableItemUnregister(window);
   6405 
   6406     char buf[32];
   6407     DataTypeFormatString(data_type, data_ptr, decimal_precision, buf, IM_ARRAYSIZE(buf));
   6408     bool text_value_changed = InputTextEx(label, buf, IM_ARRAYSIZE(buf), aabb.GetSize(), ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_AutoSelectAll);
   6409     if (g.ScalarAsInputTextId == 0)
   6410     {
   6411         // First frame
   6412         IM_ASSERT(g.ActiveId == id);    // InputText ID expected to match the Slider ID (else we'd need to store them both, which is also possible)
   6413         g.ScalarAsInputTextId = g.ActiveId;
   6414         SetHoveredID(id);
   6415     }
   6416     else if (g.ActiveId != g.ScalarAsInputTextId)
   6417     {
   6418         // Release
   6419         g.ScalarAsInputTextId = 0;
   6420     }
   6421     if (text_value_changed)
   6422         return DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, NULL);
   6423     return false;
   6424 }
   6425 
   6426 // Parse display precision back from the display format string
   6427 int ImGui::ParseFormatPrecision(const char* fmt, int default_precision)
   6428 {
   6429     int precision = default_precision;
   6430     while ((fmt = strchr(fmt, '%')) != NULL)
   6431     {
   6432         fmt++;
   6433         if (fmt[0] == '%') { fmt++; continue; } // Ignore "%%"
   6434         while (*fmt >= '0' && *fmt <= '9')
   6435             fmt++;
   6436         if (*fmt == '.')
   6437         {
   6438             precision = atoi(fmt + 1);
   6439             if (precision < 0 || precision > 10)
   6440                 precision = default_precision;
   6441         }
   6442         break;
   6443     }
   6444     return precision;
   6445 }
   6446 
   6447 float ImGui::RoundScalar(float value, int decimal_precision)
   6448 {
   6449     // Round past decimal precision
   6450     // So when our value is 1.99999 with a precision of 0.001 we'll end up rounding to 2.0
   6451     // FIXME: Investigate better rounding methods
   6452     static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f };
   6453     float min_step = (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : powf(10.0f, (float)-decimal_precision);
   6454     bool negative = value < 0.0f;
   6455     value = fabsf(value);
   6456     float remainder = fmodf(value, min_step);
   6457     if (remainder <= min_step*0.5f)
   6458         value -= remainder;
   6459     else
   6460         value += (min_step - remainder);
   6461     return negative ? -value : value;
   6462 }
   6463 
   6464 static inline float SliderBehaviorCalcRatioFromValue(float v, float v_min, float v_max, float power, float linear_zero_pos)
   6465 {
   6466     if (v_min == v_max)
   6467         return 0.0f;
   6468 
   6469     const bool is_non_linear = (power < 1.0f-0.00001f) || (power > 1.0f+0.00001f);
   6470     const float v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min);
   6471     if (is_non_linear)
   6472     {
   6473         if (v_clamped < 0.0f)
   6474         {
   6475             const float f = 1.0f - (v_clamped - v_min) / (ImMin(0.0f,v_max) - v_min);
   6476             return (1.0f - powf(f, 1.0f/power)) * linear_zero_pos;
   6477         }
   6478         else
   6479         {
   6480             const float f = (v_clamped - ImMax(0.0f,v_min)) / (v_max - ImMax(0.0f,v_min));
   6481             return linear_zero_pos + powf(f, 1.0f/power) * (1.0f - linear_zero_pos);
   6482         }
   6483     }
   6484 
   6485     // Linear slider
   6486     return (v_clamped - v_min) / (v_max - v_min);
   6487 }
   6488 
   6489 bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags)
   6490 {
   6491     ImGuiContext& g = *GImGui;
   6492     ImGuiWindow* window = GetCurrentWindow();
   6493     const ImGuiStyle& style = g.Style;
   6494 
   6495     // Draw frame
   6496     RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
   6497 
   6498     const bool is_non_linear = (power < 1.0f-0.00001f) || (power > 1.0f+0.00001f);
   6499     const bool is_horizontal = (flags & ImGuiSliderFlags_Vertical) == 0;
   6500 
   6501     const float grab_padding = 2.0f;
   6502     const float slider_sz = is_horizontal ? (frame_bb.GetWidth() - grab_padding * 2.0f) : (frame_bb.GetHeight() - grab_padding * 2.0f);
   6503     float grab_sz;
   6504     if (decimal_precision > 0)
   6505         grab_sz = ImMin(style.GrabMinSize, slider_sz);
   6506     else
   6507         grab_sz = ImMin(ImMax(1.0f * (slider_sz / ((v_min < v_max ? v_max - v_min : v_min - v_max) + 1.0f)), style.GrabMinSize), slider_sz);  // Integer sliders, if possible have the grab size represent 1 unit
   6508     const float slider_usable_sz = slider_sz - grab_sz;
   6509     const float slider_usable_pos_min = (is_horizontal ? frame_bb.Min.x : frame_bb.Min.y) + grab_padding + grab_sz*0.5f;
   6510     const float slider_usable_pos_max = (is_horizontal ? frame_bb.Max.x : frame_bb.Max.y) - grab_padding - grab_sz*0.5f;
   6511 
   6512     // For logarithmic sliders that cross over sign boundary we want the exponential increase to be symmetric around 0.0f
   6513     float linear_zero_pos = 0.0f;   // 0.0->1.0f
   6514     if (v_min * v_max < 0.0f)
   6515     {
   6516         // Different sign
   6517         const float linear_dist_min_to_0 = powf(fabsf(0.0f - v_min), 1.0f/power);
   6518         const float linear_dist_max_to_0 = powf(fabsf(v_max - 0.0f), 1.0f/power);
   6519         linear_zero_pos = linear_dist_min_to_0 / (linear_dist_min_to_0+linear_dist_max_to_0);
   6520     }
   6521     else
   6522     {
   6523         // Same sign
   6524         linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f;
   6525     }
   6526 
   6527     // Process clicking on the slider
   6528     bool value_changed = false;
   6529     if (g.ActiveId == id)
   6530     {
   6531         if (g.IO.MouseDown[0])
   6532         {
   6533             const float mouse_abs_pos = is_horizontal ? g.IO.MousePos.x : g.IO.MousePos.y;
   6534             float clicked_t = (slider_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f) : 0.0f;
   6535             if (!is_horizontal)
   6536                 clicked_t = 1.0f - clicked_t;
   6537 
   6538             float new_value;
   6539             if (is_non_linear)
   6540             {
   6541                 // Account for logarithmic scale on both sides of the zero
   6542                 if (clicked_t < linear_zero_pos)
   6543                 {
   6544                     // Negative: rescale to the negative range before powering
   6545                     float a = 1.0f - (clicked_t / linear_zero_pos);
   6546                     a = powf(a, power);
   6547                     new_value = ImLerp(ImMin(v_max,0.0f), v_min, a);
   6548                 }
   6549                 else
   6550                 {
   6551                     // Positive: rescale to the positive range before powering
   6552                     float a;
   6553                     if (fabsf(linear_zero_pos - 1.0f) > 1.e-6f)
   6554                         a = (clicked_t - linear_zero_pos) / (1.0f - linear_zero_pos);
   6555                     else
   6556                         a = clicked_t;
   6557                     a = powf(a, power);
   6558                     new_value = ImLerp(ImMax(v_min,0.0f), v_max, a);
   6559                 }
   6560             }
   6561             else
   6562             {
   6563                 // Linear slider
   6564                 new_value = ImLerp(v_min, v_max, clicked_t);
   6565             }
   6566 
   6567             // Round past decimal precision
   6568             new_value = RoundScalar(new_value, decimal_precision);
   6569             if (*v != new_value)
   6570             {
   6571                 *v = new_value;
   6572                 value_changed = true;
   6573             }
   6574         }
   6575         else
   6576         {
   6577             ClearActiveID();
   6578         }
   6579     }
   6580 
   6581     // Calculate slider grab positioning
   6582     float grab_t = SliderBehaviorCalcRatioFromValue(*v, v_min, v_max, power, linear_zero_pos);
   6583 
   6584     // Draw
   6585     if (!is_horizontal)
   6586         grab_t = 1.0f - grab_t;
   6587     const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t);
   6588     ImRect grab_bb;
   6589     if (is_horizontal)
   6590         grab_bb = ImRect(ImVec2(grab_pos - grab_sz*0.5f, frame_bb.Min.y + grab_padding), ImVec2(grab_pos + grab_sz*0.5f, frame_bb.Max.y - grab_padding));
   6591     else
   6592         grab_bb = ImRect(ImVec2(frame_bb.Min.x + grab_padding, grab_pos - grab_sz*0.5f), ImVec2(frame_bb.Max.x - grab_padding, grab_pos + grab_sz*0.5f));
   6593     window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);
   6594 
   6595     return value_changed;
   6596 }
   6597 
   6598 // Use power!=1.0 for logarithmic sliders.
   6599 // Adjust display_format to decorate the value with a prefix or a suffix.
   6600 //   "%.3f"         1.234
   6601 //   "%5.2f secs"   01.23 secs
   6602 //   "Gold: %.0f"   Gold: 1
   6603 bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format, float power)
   6604 {
   6605     ImGuiWindow* window = GetCurrentWindow();
   6606     if (window->SkipItems)
   6607         return false;
   6608 
   6609     ImGuiContext& g = *GImGui;
   6610     const ImGuiStyle& style = g.Style;
   6611     const ImGuiID id = window->GetID(label);
   6612     const float w = CalcItemWidth();
   6613 
   6614     const ImVec2 label_size = CalcTextSize(label, NULL, true);
   6615     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
   6616     const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
   6617 
   6618     // NB- we don't call ItemSize() yet because we may turn into a text edit box below
   6619     if (!ItemAdd(total_bb, &id))
   6620     {
   6621         ItemSize(total_bb, style.FramePadding.y);
   6622         return false;
   6623     }
   6624 
   6625     const bool hovered = IsHovered(frame_bb, id);
   6626     if (hovered)
   6627         SetHoveredID(id);
   6628 
   6629     if (!display_format)
   6630         display_format = "%.3f";
   6631     int decimal_precision = ParseFormatPrecision(display_format, 3);
   6632 
   6633     // Tabbing or CTRL-clicking on Slider turns it into an input box
   6634     bool start_text_input = false;
   6635     const bool tab_focus_requested = FocusableItemRegister(window, g.ActiveId == id);
   6636     if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]))
   6637     {
   6638         SetActiveID(id, window);
   6639         FocusWindow(window);
   6640 
   6641         if (tab_focus_requested || g.IO.KeyCtrl)
   6642         {
   6643             start_text_input = true;
   6644             g.ScalarAsInputTextId = 0;
   6645         }
   6646     }
   6647     if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
   6648         return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision);
   6649 
   6650     ItemSize(total_bb, style.FramePadding.y);
   6651 
   6652     // Actual slider behavior + render grab
   6653     const bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision);
   6654 
   6655     // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
   6656     char value_buf[64];
   6657     const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
   6658     RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f));
   6659 
   6660     if (label_size.x > 0.0f)
   6661         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
   6662 
   6663     return value_changed;
   6664 }
   6665 
   6666 bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* display_format, float power)
   6667 {
   6668     ImGuiWindow* window = GetCurrentWindow();
   6669     if (window->SkipItems)
   6670         return false;
   6671 
   6672     ImGuiContext& g = *GImGui;
   6673     const ImGuiStyle& style = g.Style;
   6674     const ImGuiID id = window->GetID(label);
   6675 
   6676     const ImVec2 label_size = CalcTextSize(label, NULL, true);
   6677     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
   6678     const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
   6679 
   6680     ItemSize(bb, style.FramePadding.y);
   6681     if (!ItemAdd(frame_bb, &id))
   6682         return false;
   6683 
   6684     const bool hovered = IsHovered(frame_bb, id);
   6685     if (hovered)
   6686         SetHoveredID(id);
   6687 
   6688     if (!display_format)
   6689         display_format = "%.3f";
   6690     int decimal_precision = ParseFormatPrecision(display_format, 3);
   6691 
   6692     if (hovered && g.IO.MouseClicked[0])
   6693     {
   6694         SetActiveID(id, window);
   6695         FocusWindow(window);
   6696     }
   6697 
   6698     // Actual slider behavior + render grab
   6699     bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision, ImGuiSliderFlags_Vertical);
   6700 
   6701     // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
   6702     // For the vertical slider we allow centered text to overlap the frame padding
   6703     char value_buf[64];
   6704     char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
   6705     RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.0f));
   6706     if (label_size.x > 0.0f)
   6707         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
   6708 
   6709     return value_changed;
   6710 }
   6711 
   6712 bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max)
   6713 {
   6714     float v_deg = (*v_rad) * 360.0f / (2*IM_PI);
   6715     bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, "%.0f deg", 1.0f);
   6716     *v_rad = v_deg * (2*IM_PI) / 360.0f;
   6717     return value_changed;
   6718 }
   6719 
   6720 bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* display_format)
   6721 {
   6722     if (!display_format)
   6723         display_format = "%.0f";
   6724     float v_f = (float)*v;
   6725     bool value_changed = SliderFloat(label, &v_f, (float)v_min, (float)v_max, display_format, 1.0f);
   6726     *v = (int)v_f;
   6727     return value_changed;
   6728 }
   6729 
   6730 bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* display_format)
   6731 {
   6732     if (!display_format)
   6733         display_format = "%.0f";
   6734     float v_f = (float)*v;
   6735     bool value_changed = VSliderFloat(label, size, &v_f, (float)v_min, (float)v_max, display_format, 1.0f);
   6736     *v = (int)v_f;
   6737     return value_changed;
   6738 }
   6739 
   6740 // Add multiple sliders on 1 line for compact edition of multiple components
   6741 bool ImGui::SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* display_format, float power)
   6742 {
   6743     ImGuiWindow* window = GetCurrentWindow();
   6744     if (window->SkipItems)
   6745         return false;
   6746 
   6747     ImGuiContext& g = *GImGui;
   6748     bool value_changed = false;
   6749     BeginGroup();
   6750     PushID(label);
   6751     PushMultiItemsWidths(components);
   6752     for (int i = 0; i < components; i++)
   6753     {
   6754         PushID(i);
   6755         value_changed |= SliderFloat("##v", &v[i], v_min, v_max, display_format, power);
   6756         SameLine(0, g.Style.ItemInnerSpacing.x);
   6757         PopID();
   6758         PopItemWidth();
   6759     }
   6760     PopID();
   6761 
   6762     TextUnformatted(label, FindRenderedTextEnd(label));
   6763     EndGroup();
   6764 
   6765     return value_changed;
   6766 }
   6767 
   6768 bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format, float power)
   6769 {
   6770     return SliderFloatN(label, v, 2, v_min, v_max, display_format, power);
   6771 }
   6772 
   6773 bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format, float power)
   6774 {
   6775     return SliderFloatN(label, v, 3, v_min, v_max, display_format, power);
   6776 }
   6777 
   6778 bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* display_format, float power)
   6779 {
   6780     return SliderFloatN(label, v, 4, v_min, v_max, display_format, power);
   6781 }
   6782 
   6783 bool ImGui::SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* display_format)
   6784 {
   6785     ImGuiWindow* window = GetCurrentWindow();
   6786     if (window->SkipItems)
   6787         return false;
   6788 
   6789     ImGuiContext& g = *GImGui;
   6790     bool value_changed = false;
   6791     BeginGroup();
   6792     PushID(label);
   6793     PushMultiItemsWidths(components);
   6794     for (int i = 0; i < components; i++)
   6795     {
   6796         PushID(i);
   6797         value_changed |= SliderInt("##v", &v[i], v_min, v_max, display_format);
   6798         SameLine(0, g.Style.ItemInnerSpacing.x);
   6799         PopID();
   6800         PopItemWidth();
   6801     }
   6802     PopID();
   6803 
   6804     TextUnformatted(label, FindRenderedTextEnd(label));
   6805     EndGroup();
   6806 
   6807     return value_changed;
   6808 }
   6809 
   6810 bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* display_format)
   6811 {
   6812     return SliderIntN(label, v, 2, v_min, v_max, display_format);
   6813 }
   6814 
   6815 bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* display_format)
   6816 {
   6817     return SliderIntN(label, v, 3, v_min, v_max, display_format);
   6818 }
   6819 
   6820 bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* display_format)
   6821 {
   6822     return SliderIntN(label, v, 4, v_min, v_max, display_format);
   6823 }
   6824 
   6825 bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, int decimal_precision, float power)
   6826 {
   6827     ImGuiContext& g = *GImGui;
   6828     const ImGuiStyle& style = g.Style;
   6829 
   6830     // Draw frame
   6831     const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
   6832     RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding);
   6833 
   6834     bool value_changed = false;
   6835 
   6836     // Process clicking on the drag
   6837     if (g.ActiveId == id)
   6838     {
   6839         if (g.IO.MouseDown[0])
   6840         {
   6841             if (g.ActiveIdIsJustActivated)
   6842             {
   6843                 // Lock current value on click
   6844                 g.DragCurrentValue = *v;
   6845                 g.DragLastMouseDelta = ImVec2(0.f, 0.f);
   6846             }
   6847 
   6848             float v_cur = g.DragCurrentValue;
   6849             const ImVec2 mouse_drag_delta = GetMouseDragDelta(0, 1.0f);
   6850             if (fabsf(mouse_drag_delta.x - g.DragLastMouseDelta.x) > 0.0f)
   6851             {
   6852                 float speed = v_speed;
   6853                 if (speed == 0.0f && (v_max - v_min) != 0.0f && (v_max - v_min) < FLT_MAX)
   6854                     speed = (v_max - v_min) * g.DragSpeedDefaultRatio;
   6855                 if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f)
   6856                     speed = speed * g.DragSpeedScaleFast;
   6857                 if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f)
   6858                     speed = speed * g.DragSpeedScaleSlow;
   6859 
   6860                 float delta = (mouse_drag_delta.x - g.DragLastMouseDelta.x) * speed;
   6861                 if (fabsf(power - 1.0f) > 0.001f)
   6862                 {
   6863                     // Logarithmic curve on both side of 0.0
   6864                     float v0_abs = v_cur >= 0.0f ? v_cur : -v_cur;
   6865                     float v0_sign = v_cur >= 0.0f ? 1.0f : -1.0f;
   6866                     float v1 = powf(v0_abs, 1.0f / power) + (delta * v0_sign);
   6867                     float v1_abs = v1 >= 0.0f ? v1 : -v1;
   6868                     float v1_sign = v1 >= 0.0f ? 1.0f : -1.0f;          // Crossed sign line
   6869                     v_cur = powf(v1_abs, power) * v0_sign * v1_sign;    // Reapply sign
   6870                 }
   6871                 else
   6872                 {
   6873                     v_cur += delta;
   6874                 }
   6875                 g.DragLastMouseDelta.x = mouse_drag_delta.x;
   6876 
   6877                 // Clamp
   6878                 if (v_min < v_max)
   6879                     v_cur = ImClamp(v_cur, v_min, v_max);
   6880                 g.DragCurrentValue = v_cur;
   6881             }
   6882 
   6883             // Round to user desired precision, then apply
   6884             v_cur = RoundScalar(v_cur, decimal_precision);
   6885             if (*v != v_cur)
   6886             {
   6887                 *v = v_cur;
   6888                 value_changed = true;
   6889             }
   6890         }
   6891         else
   6892         {
   6893             ClearActiveID();
   6894         }
   6895     }
   6896 
   6897     return value_changed;
   6898 }
   6899 
   6900 bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* display_format, float power)
   6901 {
   6902     ImGuiWindow* window = GetCurrentWindow();
   6903     if (window->SkipItems)
   6904         return false;
   6905 
   6906     ImGuiContext& g = *GImGui;
   6907     const ImGuiStyle& style = g.Style;
   6908     const ImGuiID id = window->GetID(label);
   6909     const float w = CalcItemWidth();
   6910 
   6911     const ImVec2 label_size = CalcTextSize(label, NULL, true);
   6912     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
   6913     const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
   6914     const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
   6915 
   6916     // NB- we don't call ItemSize() yet because we may turn into a text edit box below
   6917     if (!ItemAdd(total_bb, &id))
   6918     {
   6919         ItemSize(total_bb, style.FramePadding.y);
   6920         return false;
   6921     }
   6922 
   6923     const bool hovered = IsHovered(frame_bb, id);
   6924     if (hovered)
   6925         SetHoveredID(id);
   6926 
   6927     if (!display_format)
   6928         display_format = "%.3f";
   6929     int decimal_precision = ParseFormatPrecision(display_format, 3);
   6930 
   6931     // Tabbing or CTRL-clicking on Drag turns it into an input box
   6932     bool start_text_input = false;
   6933     const bool tab_focus_requested = FocusableItemRegister(window, g.ActiveId == id);
   6934     if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] | g.IO.MouseDoubleClicked[0])))
   6935     {
   6936         SetActiveID(id, window);
   6937         FocusWindow(window);
   6938 
   6939         if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0])
   6940         {
   6941             start_text_input = true;
   6942             g.ScalarAsInputTextId = 0;
   6943         }
   6944     }
   6945     if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
   6946         return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision);
   6947 
   6948     // Actual drag behavior
   6949     ItemSize(total_bb, style.FramePadding.y);
   6950     const bool value_changed = DragBehavior(frame_bb, id, v, v_speed, v_min, v_max, decimal_precision, power);
   6951 
   6952     // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
   6953     char value_buf[64];
   6954     const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
   6955     RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f));
   6956 
   6957     if (label_size.x > 0.0f)
   6958         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
   6959 
   6960     return value_changed;
   6961 }
   6962 
   6963 bool ImGui::DragFloatN(const char* label, float* v, int components, float v_speed, float v_min, float v_max, const char* display_format, float power)
   6964 {
   6965     ImGuiWindow* window = GetCurrentWindow();
   6966     if (window->SkipItems)
   6967         return false;
   6968 
   6969     ImGuiContext& g = *GImGui;
   6970     bool value_changed = false;
   6971     BeginGroup();
   6972     PushID(label);
   6973     PushMultiItemsWidths(components);
   6974     for (int i = 0; i < components; i++)
   6975     {
   6976         PushID(i);
   6977         value_changed |= DragFloat("##v", &v[i], v_speed, v_min, v_max, display_format, power);
   6978         SameLine(0, g.Style.ItemInnerSpacing.x);
   6979         PopID();
   6980         PopItemWidth();
   6981     }
   6982     PopID();
   6983 
   6984     TextUnformatted(label, FindRenderedTextEnd(label));
   6985     EndGroup();
   6986 
   6987     return value_changed;
   6988 }
   6989 
   6990 bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* display_format, float power)
   6991 {
   6992     return DragFloatN(label, v, 2, v_speed, v_min, v_max, display_format, power);
   6993 }
   6994 
   6995 bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* display_format, float power)
   6996 {
   6997     return DragFloatN(label, v, 3, v_speed, v_min, v_max, display_format, power);
   6998 }
   6999 
   7000 bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* display_format, float power)
   7001 {
   7002     return DragFloatN(label, v, 4, v_speed, v_min, v_max, display_format, power);
   7003 }
   7004 
   7005 bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* display_format, const char* display_format_max, float power)
   7006 {
   7007     ImGuiWindow* window = GetCurrentWindow();
   7008     if (window->SkipItems)
   7009         return false;
   7010 
   7011     ImGuiContext& g = *GImGui;
   7012     PushID(label);
   7013     BeginGroup();
   7014     PushMultiItemsWidths(2);
   7015 
   7016     bool value_changed = DragFloat("##min", v_current_min, v_speed, (v_min >= v_max) ? -FLT_MAX : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), display_format, power);
   7017     PopItemWidth();
   7018     SameLine(0, g.Style.ItemInnerSpacing.x);
   7019     value_changed |= DragFloat("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? FLT_MAX : v_max, display_format_max ? display_format_max : display_format, power);
   7020     PopItemWidth();
   7021     SameLine(0, g.Style.ItemInnerSpacing.x);
   7022 
   7023     TextUnformatted(label, FindRenderedTextEnd(label));
   7024     EndGroup();
   7025     PopID();
   7026 
   7027     return value_changed;
   7028 }
   7029 
   7030 // NB: v_speed is float to allow adjusting the drag speed with more precision
   7031 bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* display_format)
   7032 {
   7033     if (!display_format)
   7034         display_format = "%.0f";
   7035     float v_f = (float)*v;
   7036     bool value_changed = DragFloat(label, &v_f, v_speed, (float)v_min, (float)v_max, display_format);
   7037     *v = (int)v_f;
   7038     return value_changed;
   7039 }
   7040 
   7041 bool ImGui::DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* display_format)
   7042 {
   7043     ImGuiWindow* window = GetCurrentWindow();
   7044     if (window->SkipItems)
   7045         return false;
   7046 
   7047     ImGuiContext& g = *GImGui;
   7048     bool value_changed = false;
   7049     BeginGroup();
   7050     PushID(label);
   7051     PushMultiItemsWidths(components);
   7052     for (int i = 0; i < components; i++)
   7053     {
   7054         PushID(i);
   7055         value_changed |= DragInt("##v", &v[i], v_speed, v_min, v_max, display_format);
   7056         SameLine(0, g.Style.ItemInnerSpacing.x);
   7057         PopID();
   7058         PopItemWidth();
   7059     }
   7060     PopID();
   7061 
   7062     TextUnformatted(label, FindRenderedTextEnd(label));
   7063     EndGroup();
   7064 
   7065     return value_changed;
   7066 }
   7067 
   7068 bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* display_format)
   7069 {
   7070     return DragIntN(label, v, 2, v_speed, v_min, v_max, display_format);
   7071 }
   7072 
   7073 bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* display_format)
   7074 {
   7075     return DragIntN(label, v, 3, v_speed, v_min, v_max, display_format);
   7076 }
   7077 
   7078 bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* display_format)
   7079 {
   7080     return DragIntN(label, v, 4, v_speed, v_min, v_max, display_format);
   7081 }
   7082 
   7083 bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* display_format, const char* display_format_max)
   7084 {
   7085     ImGuiWindow* window = GetCurrentWindow();
   7086     if (window->SkipItems)
   7087         return false;
   7088 
   7089     ImGuiContext& g = *GImGui;
   7090     PushID(label);
   7091     BeginGroup();
   7092     PushMultiItemsWidths(2);
   7093 
   7094     bool value_changed = DragInt("##min", v_current_min, v_speed, (v_min >= v_max) ? INT_MIN : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), display_format);
   7095     PopItemWidth();
   7096     SameLine(0, g.Style.ItemInnerSpacing.x);
   7097     value_changed |= DragInt("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? INT_MAX : v_max, display_format_max ? display_format_max : display_format);
   7098     PopItemWidth();
   7099     SameLine(0, g.Style.ItemInnerSpacing.x);
   7100 
   7101     TextUnformatted(label, FindRenderedTextEnd(label));
   7102     EndGroup();
   7103     PopID();
   7104 
   7105     return value_changed;
   7106 }
   7107 
   7108 void ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)
   7109 {
   7110     ImGuiWindow* window = GetCurrentWindow();
   7111     if (window->SkipItems)
   7112         return;
   7113 
   7114     ImGuiContext& g = *GImGui;
   7115     const ImGuiStyle& style = g.Style;
   7116 
   7117     const ImVec2 label_size = CalcTextSize(label, NULL, true);
   7118     if (graph_size.x == 0.0f)
   7119         graph_size.x = CalcItemWidth();
   7120     if (graph_size.y == 0.0f)
   7121         graph_size.y = label_size.y + (style.FramePadding.y * 2);
   7122 
   7123     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(graph_size.x, graph_size.y));
   7124     const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
   7125     const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0));
   7126     ItemSize(total_bb, style.FramePadding.y);
   7127     if (!ItemAdd(total_bb, NULL))
   7128         return;
   7129 
   7130     // Determine scale from values if not specified
   7131     if (scale_min == FLT_MAX || scale_max == FLT_MAX)
   7132     {
   7133         float v_min = FLT_MAX;
   7134         float v_max = -FLT_MAX;
   7135         for (int i = 0; i < values_count; i++)
   7136         {
   7137             const float v = values_getter(data, i);
   7138             v_min = ImMin(v_min, v);
   7139             v_max = ImMax(v_max, v);
   7140         }
   7141         if (scale_min == FLT_MAX)
   7142             scale_min = v_min;
   7143         if (scale_max == FLT_MAX)
   7144             scale_max = v_max;
   7145     }
   7146 
   7147     RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
   7148 
   7149     if (values_count > 0)
   7150     {
   7151         int res_w = ImMin((int)graph_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
   7152         int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
   7153 
   7154         // Tooltip on hover
   7155         int v_hovered = -1;
   7156         if (IsHovered(inner_bb, 0))
   7157         {
   7158             const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f);
   7159             const int v_idx = (int)(t * item_count);
   7160             IM_ASSERT(v_idx >= 0 && v_idx < values_count);
   7161 
   7162             const float v0 = values_getter(data, (v_idx + values_offset) % values_count);
   7163             const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count);
   7164             if (plot_type == ImGuiPlotType_Lines)
   7165                 SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx+1, v1);
   7166             else if (plot_type == ImGuiPlotType_Histogram)
   7167                 SetTooltip("%d: %8.4g", v_idx, v0);
   7168             v_hovered = v_idx;
   7169         }
   7170 
   7171         const float t_step = 1.0f / (float)res_w;
   7172 
   7173         float v0 = values_getter(data, (0 + values_offset) % values_count);
   7174         float t0 = 0.0f;
   7175         ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) / (scale_max - scale_min)) );    // Point in the normalized space of our target rectangle
   7176 
   7177         const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram);
   7178         const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered);
   7179 
   7180         for (int n = 0; n < res_w; n++)
   7181         {
   7182             const float t1 = t0 + t_step;
   7183             const int v1_idx = (int)(t0 * item_count + 0.5f);
   7184             IM_ASSERT(v1_idx >= 0 && v1_idx < values_count);
   7185             const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count);
   7186             const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) / (scale_max - scale_min)) );
   7187 
   7188             // NB: Draw calls are merged together by the DrawList system. Still, we should render our batch are lower level to save a bit of CPU.
   7189             ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0);
   7190             ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, 1.0f));
   7191             if (plot_type == ImGuiPlotType_Lines)
   7192             {
   7193                 window->DrawList->AddLine(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
   7194             }
   7195             else if (plot_type == ImGuiPlotType_Histogram)
   7196             {
   7197                 if (pos1.x >= pos0.x + 2.0f)
   7198                     pos1.x -= 1.0f;
   7199                 window->DrawList->AddRectFilled(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
   7200             }
   7201 
   7202             t0 = t1;
   7203             tp0 = tp1;
   7204         }
   7205     }
   7206 
   7207     // Text overlay
   7208     if (overlay_text)
   7209         RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f,0.0f));
   7210 
   7211     if (label_size.x > 0.0f)
   7212         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
   7213 }
   7214 
   7215 struct ImGuiPlotArrayGetterData
   7216 {
   7217     const float* Values;
   7218     int Stride;
   7219 
   7220     ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; }
   7221 };
   7222 
   7223 static float Plot_ArrayGetter(void* data, int idx)
   7224 {
   7225     ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data;
   7226     const float v = *(float*)(void*)((unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride);
   7227     return v;
   7228 }
   7229 
   7230 void ImGui::PlotLines(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride)
   7231 {
   7232     ImGuiPlotArrayGetterData data(values, stride);
   7233     PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
   7234 }
   7235 
   7236 void ImGui::PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)
   7237 {
   7238     PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
   7239 }
   7240 
   7241 void ImGui::PlotHistogram(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride)
   7242 {
   7243     ImGuiPlotArrayGetterData data(values, stride);
   7244     PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
   7245 }
   7246 
   7247 void ImGui::PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)
   7248 {
   7249     PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
   7250 }
   7251 
   7252 // size_arg (for each axis) < 0.0f: align to end, 0.0f: auto, > 0.0f: specified size
   7253 void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay)
   7254 {
   7255     ImGuiWindow* window = GetCurrentWindow();
   7256     if (window->SkipItems)
   7257         return;
   7258 
   7259     ImGuiContext& g = *GImGui;
   7260     const ImGuiStyle& style = g.Style;
   7261 
   7262     ImVec2 pos = window->DC.CursorPos;
   7263     ImRect bb(pos, pos + CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y*2.0f));
   7264     ItemSize(bb, style.FramePadding.y);
   7265     if (!ItemAdd(bb, NULL))
   7266         return;
   7267 
   7268     // Render
   7269     fraction = ImSaturate(fraction);
   7270     RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
   7271     bb.Reduce(ImVec2(window->BorderSize, window->BorderSize));
   7272     const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y);
   7273     RenderFrame(bb.Min, fill_br, GetColorU32(ImGuiCol_PlotHistogram), false, style.FrameRounding);
   7274 
   7275     // Default displaying the fraction as percentage string, but user can override it
   7276     char overlay_buf[32];
   7277     if (!overlay)
   7278     {
   7279         ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction*100+0.01f);
   7280         overlay = overlay_buf;
   7281     }
   7282 
   7283     ImVec2 overlay_size = CalcTextSize(overlay, NULL);
   7284     if (overlay_size.x > 0.0f)
   7285         RenderTextClipped(ImVec2(ImClamp(fill_br.x + style.ItemSpacing.x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f,0.5f), &bb);
   7286 }
   7287 
   7288 bool ImGui::Checkbox(const char* label, bool* v)
   7289 {
   7290     ImGuiWindow* window = GetCurrentWindow();
   7291     if (window->SkipItems)
   7292         return false;
   7293 
   7294     ImGuiContext& g = *GImGui;
   7295     const ImGuiStyle& style = g.Style;
   7296     const ImGuiID id = window->GetID(label);
   7297     const ImVec2 label_size = CalcTextSize(label, NULL, true);
   7298 
   7299     const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2, label_size.y + style.FramePadding.y*2));
   7300     ItemSize(check_bb, style.FramePadding.y);
   7301 
   7302     ImRect total_bb = check_bb;
   7303     if (label_size.x > 0)
   7304         SameLine(0, style.ItemInnerSpacing.x);
   7305     const ImRect text_bb(window->DC.CursorPos + ImVec2(0,style.FramePadding.y), window->DC.CursorPos + ImVec2(0,style.FramePadding.y) + label_size);
   7306     if (label_size.x > 0)
   7307     {
   7308         ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y);
   7309         total_bb = ImRect(ImMin(check_bb.Min, text_bb.Min), ImMax(check_bb.Max, text_bb.Max));
   7310     }
   7311 
   7312     if (!ItemAdd(total_bb, &id))
   7313         return false;
   7314 
   7315     bool hovered, held;
   7316     bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
   7317     if (pressed)
   7318         *v = !(*v);
   7319 
   7320     RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding);
   7321     if (*v)
   7322     {
   7323         const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight());
   7324         const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f));
   7325         window->DrawList->AddRectFilled(check_bb.Min+ImVec2(pad,pad), check_bb.Max-ImVec2(pad,pad), GetColorU32(ImGuiCol_CheckMark), style.FrameRounding);
   7326     }
   7327 
   7328     if (g.LogEnabled)
   7329         LogRenderedText(text_bb.GetTL(), *v ? "[x]" : "[ ]");
   7330     if (label_size.x > 0.0f)
   7331         RenderText(text_bb.GetTL(), label);
   7332 
   7333     return pressed;
   7334 }
   7335 
   7336 bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value)
   7337 {
   7338     bool v = ((*flags & flags_value) == flags_value);
   7339     bool pressed = Checkbox(label, &v);
   7340     if (pressed)
   7341     {
   7342         if (v)
   7343             *flags |= flags_value;
   7344         else
   7345             *flags &= ~flags_value;
   7346     }
   7347 
   7348     return pressed;
   7349 }
   7350 
   7351 bool ImGui::RadioButton(const char* label, bool active)
   7352 {
   7353     ImGuiWindow* window = GetCurrentWindow();
   7354     if (window->SkipItems)
   7355         return false;
   7356 
   7357     ImGuiContext& g = *GImGui;
   7358     const ImGuiStyle& style = g.Style;
   7359     const ImGuiID id = window->GetID(label);
   7360     const ImVec2 label_size = CalcTextSize(label, NULL, true);
   7361 
   7362     const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2-1, label_size.y + style.FramePadding.y*2-1));
   7363     ItemSize(check_bb, style.FramePadding.y);
   7364 
   7365     ImRect total_bb = check_bb;
   7366     if (label_size.x > 0)
   7367         SameLine(0, style.ItemInnerSpacing.x);
   7368     const ImRect text_bb(window->DC.CursorPos + ImVec2(0, style.FramePadding.y), window->DC.CursorPos + ImVec2(0, style.FramePadding.y) + label_size);
   7369     if (label_size.x > 0)
   7370     {
   7371         ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y);
   7372         total_bb.Add(text_bb);
   7373     }
   7374 
   7375     if (!ItemAdd(total_bb, &id))
   7376         return false;
   7377 
   7378     ImVec2 center = check_bb.GetCenter();
   7379     center.x = (float)(int)center.x + 0.5f;
   7380     center.y = (float)(int)center.y + 0.5f;
   7381     const float radius = check_bb.GetHeight() * 0.5f;
   7382 
   7383     bool hovered, held;
   7384     bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
   7385 
   7386     window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16);
   7387     if (active)
   7388     {
   7389         const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight());
   7390         const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f));
   7391         window->DrawList->AddCircleFilled(center, radius-pad, GetColorU32(ImGuiCol_CheckMark), 16);
   7392     }
   7393 
   7394     if (window->Flags & ImGuiWindowFlags_ShowBorders)
   7395     {
   7396         window->DrawList->AddCircle(center+ImVec2(1,1), radius, GetColorU32(ImGuiCol_BorderShadow), 16);
   7397         window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16);
   7398     }
   7399 
   7400     if (g.LogEnabled)
   7401         LogRenderedText(text_bb.GetTL(), active ? "(x)" : "( )");
   7402     if (label_size.x > 0.0f)
   7403         RenderText(text_bb.GetTL(), label);
   7404 
   7405     return pressed;
   7406 }
   7407 
   7408 bool ImGui::RadioButton(const char* label, int* v, int v_button)
   7409 {
   7410     const bool pressed = RadioButton(label, *v == v_button);
   7411     if (pressed)
   7412     {
   7413         *v = v_button;
   7414     }
   7415     return pressed;
   7416 }
   7417 
   7418 static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end)
   7419 {
   7420     int line_count = 0;
   7421     const char* s = text_begin;
   7422     while (char c = *s++) // We are only matching for \n so we can ignore UTF-8 decoding
   7423         if (c == '\n')
   7424             line_count++;
   7425     s--;
   7426     if (s[0] != '\n' && s[0] != '\r')
   7427         line_count++;
   7428     *out_text_end = s;
   7429     return line_count;
   7430 }
   7431 
   7432 static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line)
   7433 {
   7434     ImFont* font = GImGui->Font;
   7435     const float line_height = GImGui->FontSize;
   7436     const float scale = line_height / font->FontSize;
   7437 
   7438     ImVec2 text_size = ImVec2(0,0);
   7439     float line_width = 0.0f;
   7440 
   7441     const ImWchar* s = text_begin;
   7442     while (s < text_end)
   7443     {
   7444         unsigned int c = (unsigned int)(*s++);
   7445         if (c == '\n')
   7446         {
   7447             text_size.x = ImMax(text_size.x, line_width);
   7448             text_size.y += line_height;
   7449             line_width = 0.0f;
   7450             if (stop_on_new_line)
   7451                 break;
   7452             continue;
   7453         }
   7454         if (c == '\r')
   7455             continue;
   7456 
   7457         const float char_width = font->GetCharAdvance((unsigned short)c) * scale;
   7458         line_width += char_width;
   7459     }
   7460 
   7461     if (text_size.x < line_width)
   7462         text_size.x = line_width;
   7463 
   7464     if (out_offset)
   7465         *out_offset = ImVec2(line_width, text_size.y + line_height);  // offset allow for the possibility of sitting after a trailing \n
   7466 
   7467     if (line_width > 0 || text_size.y == 0.0f)                        // whereas size.y will ignore the trailing \n
   7468         text_size.y += line_height;
   7469 
   7470     if (remaining)
   7471         *remaining = s;
   7472 
   7473     return text_size;
   7474 }
   7475 
   7476 // Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar)
   7477 namespace ImGuiStb
   7478 {
   7479 
   7480 static int     STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj)                             { return obj->CurLenW; }
   7481 static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx)                      { return obj->Text[idx]; }
   7482 static float   STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx)  { ImWchar c = obj->Text[line_start_idx+char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; return GImGui->Font->GetCharAdvance(c) * (GImGui->FontSize / GImGui->Font->FontSize); }
   7483 static int     STB_TEXTEDIT_KEYTOTEXT(int key)                                                    { return key >= 0x10000 ? 0 : key; }
   7484 static ImWchar STB_TEXTEDIT_NEWLINE = '\n';
   7485 static void    STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx)
   7486 {
   7487     const ImWchar* text = obj->Text.Data;
   7488     const ImWchar* text_remaining = NULL;
   7489     const ImVec2 size = InputTextCalcTextSizeW(text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true);
   7490     r->x0 = 0.0f;
   7491     r->x1 = size.x;
   7492     r->baseline_y_delta = size.y;
   7493     r->ymin = 0.0f;
   7494     r->ymax = size.y;
   7495     r->num_chars = (int)(text_remaining - (text + line_start_idx));
   7496 }
   7497 
   7498 static bool is_separator(unsigned int c)                                        { return ImCharIsSpace(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; }
   7499 static int  is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx)      { return idx > 0 ? (is_separator( obj->Text[idx-1] ) && !is_separator( obj->Text[idx] ) ) : 1; }
   7500 static int  STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx)   { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; }
   7501 #ifdef __APPLE__    // FIXME: Move setting to IO structure
   7502 static int  is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx)       { return idx > 0 ? (!is_separator( obj->Text[idx-1] ) && is_separator( obj->Text[idx] ) ) : 1; }
   7503 static int  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx)  { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; }
   7504 #else
   7505 static int  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx)  { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; }
   7506 #endif
   7507 #define STB_TEXTEDIT_MOVEWORDLEFT   STB_TEXTEDIT_MOVEWORDLEFT_IMPL    // They need to be #define for stb_textedit.h
   7508 #define STB_TEXTEDIT_MOVEWORDRIGHT  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL
   7509 
   7510 static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n)
   7511 {
   7512     ImWchar* dst = obj->Text.Data + pos;
   7513 
   7514     // We maintain our buffer length in both UTF-8 and wchar formats
   7515     obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n);
   7516     obj->CurLenW -= n;
   7517 
   7518     // Offset remaining text
   7519     const ImWchar* src = obj->Text.Data + pos + n;
   7520     while (ImWchar c = *src++)
   7521         *dst++ = c;
   7522     *dst = '\0';
   7523 }
   7524 
   7525 static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len)
   7526 {
   7527     const int text_len = obj->CurLenW;
   7528     IM_ASSERT(pos <= text_len);
   7529     if (new_text_len + text_len + 1 > obj->Text.Size)
   7530         return false;
   7531 
   7532     const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len);
   7533     if (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufSizeA)
   7534         return false;
   7535 
   7536     ImWchar* text = obj->Text.Data;
   7537     if (pos != text_len)
   7538         memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar));
   7539     memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar));
   7540 
   7541     obj->CurLenW += new_text_len;
   7542     obj->CurLenA += new_text_len_utf8;
   7543     obj->Text[obj->CurLenW] = '\0';
   7544 
   7545     return true;
   7546 }
   7547 
   7548 // We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols)
   7549 #define STB_TEXTEDIT_K_LEFT         0x10000 // keyboard input to move cursor left
   7550 #define STB_TEXTEDIT_K_RIGHT        0x10001 // keyboard input to move cursor right
   7551 #define STB_TEXTEDIT_K_UP           0x10002 // keyboard input to move cursor up
   7552 #define STB_TEXTEDIT_K_DOWN         0x10003 // keyboard input to move cursor down
   7553 #define STB_TEXTEDIT_K_LINESTART    0x10004 // keyboard input to move cursor to start of line
   7554 #define STB_TEXTEDIT_K_LINEEND      0x10005 // keyboard input to move cursor to end of line
   7555 #define STB_TEXTEDIT_K_TEXTSTART    0x10006 // keyboard input to move cursor to start of text
   7556 #define STB_TEXTEDIT_K_TEXTEND      0x10007 // keyboard input to move cursor to end of text
   7557 #define STB_TEXTEDIT_K_DELETE       0x10008 // keyboard input to delete selection or character under cursor
   7558 #define STB_TEXTEDIT_K_BACKSPACE    0x10009 // keyboard input to delete selection or character left of cursor
   7559 #define STB_TEXTEDIT_K_UNDO         0x1000A // keyboard input to perform undo
   7560 #define STB_TEXTEDIT_K_REDO         0x1000B // keyboard input to perform redo
   7561 #define STB_TEXTEDIT_K_WORDLEFT     0x1000C // keyboard input to move cursor left one word
   7562 #define STB_TEXTEDIT_K_WORDRIGHT    0x1000D // keyboard input to move cursor right one word
   7563 #define STB_TEXTEDIT_K_SHIFT        0x20000
   7564 
   7565 #define STB_TEXTEDIT_IMPLEMENTATION
   7566 #include "stb_textedit.h"
   7567 
   7568 }
   7569 
   7570 void ImGuiTextEditState::OnKeyPressed(int key)
   7571 {
   7572     stb_textedit_key(this, &StbState, key);
   7573     CursorFollow = true;
   7574     CursorAnimReset();
   7575 }
   7576 
   7577 // Public API to manipulate UTF-8 text
   7578 // We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar)
   7579 // FIXME: The existence of this rarely exercised code path is a bit of a nuisance.
   7580 void ImGuiTextEditCallbackData::DeleteChars(int pos, int bytes_count)
   7581 {
   7582     IM_ASSERT(pos + bytes_count <= BufTextLen);
   7583     char* dst = Buf + pos;
   7584     const char* src = Buf + pos + bytes_count;
   7585     while (char c = *src++)
   7586         *dst++ = c;
   7587     *dst = '\0';
   7588 
   7589     if (CursorPos + bytes_count >= pos)
   7590         CursorPos -= bytes_count;
   7591     else if (CursorPos >= pos)
   7592         CursorPos = pos;
   7593     SelectionStart = SelectionEnd = CursorPos;
   7594     BufDirty = true;
   7595     BufTextLen -= bytes_count;
   7596 }
   7597 
   7598 void ImGuiTextEditCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end)
   7599 {
   7600     const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text);
   7601     if (new_text_len + BufTextLen + 1 >= BufSize)
   7602         return;
   7603 
   7604     if (BufTextLen != pos)
   7605         memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos));
   7606     memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char));
   7607     Buf[BufTextLen + new_text_len] = '\0';
   7608 
   7609     if (CursorPos >= pos)
   7610         CursorPos += new_text_len;
   7611     SelectionStart = SelectionEnd = CursorPos;
   7612     BufDirty = true;
   7613     BufTextLen += new_text_len;
   7614 }
   7615 
   7616 // Return false to discard a character.
   7617 static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
   7618 {
   7619     unsigned int c = *p_char;
   7620 
   7621     if (c < 128 && c != ' ' && !isprint((int)(c & 0xFF)))
   7622     {
   7623         bool pass = false;
   7624         pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline));
   7625         pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput));
   7626         if (!pass)
   7627             return false;
   7628     }
   7629 
   7630     if (c >= 0xE000 && c <= 0xF8FF) // Filter private Unicode range. I don't imagine anybody would want to input them. GLFW on OSX seems to send private characters for special keys like arrow keys.
   7631         return false;
   7632 
   7633     if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank))
   7634     {
   7635         if (flags & ImGuiInputTextFlags_CharsDecimal)
   7636             if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/'))
   7637                 return false;
   7638 
   7639         if (flags & ImGuiInputTextFlags_CharsHexadecimal)
   7640             if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F'))
   7641                 return false;
   7642 
   7643         if (flags & ImGuiInputTextFlags_CharsUppercase)
   7644             if (c >= 'a' && c <= 'z')
   7645                 *p_char = (c += (unsigned int)('A'-'a'));
   7646 
   7647         if (flags & ImGuiInputTextFlags_CharsNoBlank)
   7648             if (ImCharIsSpace(c))
   7649                 return false;
   7650     }
   7651 
   7652     if (flags & ImGuiInputTextFlags_CallbackCharFilter)
   7653     {
   7654         ImGuiTextEditCallbackData callback_data;
   7655         memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData));
   7656         callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter;
   7657         callback_data.EventChar = (ImWchar)c;
   7658         callback_data.Flags = flags;
   7659         callback_data.UserData = user_data;
   7660         if (callback(&callback_data) != 0)
   7661             return false;
   7662         *p_char = callback_data.EventChar;
   7663         if (!callback_data.EventChar)
   7664             return false;
   7665     }
   7666 
   7667     return true;
   7668 }
   7669 
   7670 // Edit a string of text
   7671 // NB: when active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while active has no effect.
   7672 // FIXME: Rather messy function partly because we are doing UTF8 > u16 > UTF8 conversions on the go to more easily handle stb_textedit calls. Ideally we should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188
   7673 bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
   7674 {
   7675     ImGuiWindow* window = GetCurrentWindow();
   7676     if (window->SkipItems)
   7677         return false;
   7678 
   7679     IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys)
   7680     IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key)
   7681 
   7682     ImGuiContext& g = *GImGui;
   7683     const ImGuiIO& io = g.IO;
   7684     const ImGuiStyle& style = g.Style;
   7685 
   7686     const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0;
   7687     const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0;
   7688     const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0;
   7689 
   7690     if (is_multiline) // Open group before calling GetID() because groups tracks id created during their spawn
   7691         BeginGroup();
   7692     const ImGuiID id = window->GetID(label);
   7693     const ImVec2 label_size = CalcTextSize(label, NULL, true);
   7694     ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? GetTextLineHeight() * 8.0f : label_size.y) + style.FramePadding.y*2.0f); // Arbitrary default of 8 lines high for multi-line
   7695     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
   7696     const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? (style.ItemInnerSpacing.x + label_size.x) : 0.0f, 0.0f));
   7697 
   7698     ImGuiWindow* draw_window = window;
   7699     if (is_multiline)
   7700     {
   7701         if (!BeginChildFrame(id, frame_bb.GetSize()))
   7702         {
   7703             EndChildFrame();
   7704             EndGroup();
   7705             return false;
   7706         }
   7707         draw_window = GetCurrentWindow();
   7708         size.x -= draw_window->ScrollbarSizes.x;
   7709     }
   7710     else
   7711     {
   7712         ItemSize(total_bb, style.FramePadding.y);
   7713         if (!ItemAdd(total_bb, &id))
   7714             return false;
   7715     }
   7716 
   7717     // Password pushes a temporary font with only a fallback glyph
   7718     if (is_password)
   7719     {
   7720         const ImFont::Glyph* glyph = g.Font->FindGlyph('*');
   7721         ImFont* password_font = &g.InputTextPasswordFont;
   7722         password_font->FontSize = g.Font->FontSize;
   7723         password_font->Scale = g.Font->Scale;
   7724         password_font->DisplayOffset = g.Font->DisplayOffset;
   7725         password_font->Ascent = g.Font->Ascent;
   7726         password_font->Descent = g.Font->Descent;
   7727         password_font->ContainerAtlas = g.Font->ContainerAtlas;
   7728         password_font->FallbackGlyph = glyph;
   7729         password_font->FallbackXAdvance = glyph->XAdvance;
   7730         IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexXAdvance.empty() && password_font->IndexLookup.empty());
   7731         PushFont(password_font);
   7732     }
   7733 
   7734     // NB: we are only allowed to access 'edit_state' if we are the active widget.
   7735     ImGuiTextEditState& edit_state = g.InputTextState;
   7736 
   7737     const bool focus_requested = FocusableItemRegister(window, g.ActiveId == id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 0);    // Using completion callback disable keyboard tabbing
   7738     const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent);
   7739     const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code;
   7740 
   7741     const bool hovered = IsHovered(frame_bb, id);
   7742     if (hovered)
   7743     {
   7744         SetHoveredID(id);
   7745         g.MouseCursor = ImGuiMouseCursor_TextInput;
   7746     }
   7747     const bool user_clicked = hovered && io.MouseClicked[0];
   7748     const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.Id == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY");
   7749 
   7750     bool select_all = (g.ActiveId != id) && (flags & ImGuiInputTextFlags_AutoSelectAll) != 0;
   7751     if (focus_requested || user_clicked || user_scrolled)
   7752     {
   7753         if (g.ActiveId != id)
   7754         {
   7755             // Start edition
   7756             // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar)
   7757             // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode)
   7758             const int prev_len_w = edit_state.CurLenW;
   7759             edit_state.Text.resize(buf_size+1);        // wchar count <= UTF-8 count. we use +1 to make sure that .Data isn't NULL so it doesn't crash.
   7760             edit_state.InitialText.resize(buf_size+1); // UTF-8. we use +1 to make sure that .Data isn't NULL so it doesn't crash.
   7761             ImStrncpy(edit_state.InitialText.Data, buf, edit_state.InitialText.Size);
   7762             const char* buf_end = NULL;
   7763             edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end);
   7764             edit_state.CurLenA = (int)(buf_end - buf); // We can't get the result from ImFormatString() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8.
   7765             edit_state.CursorAnimReset();
   7766 
   7767             // Preserve cursor position and undo/redo stack if we come back to same widget
   7768             // FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar).
   7769             const bool recycle_state = (edit_state.Id == id) && (prev_len_w == edit_state.CurLenW);
   7770             if (recycle_state)
   7771             {
   7772                 // Recycle existing cursor/selection/undo stack but clamp position
   7773                 // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler.
   7774                 edit_state.CursorClamp();
   7775             }
   7776             else
   7777             {
   7778                 edit_state.Id = id;
   7779                 edit_state.ScrollX = 0.0f;
   7780                 stb_textedit_initialize_state(&edit_state.StbState, !is_multiline);
   7781                 if (!is_multiline && focus_requested_by_code)
   7782                     select_all = true;
   7783             }
   7784             if (flags & ImGuiInputTextFlags_AlwaysInsertMode)
   7785                 edit_state.StbState.insert_mode = true;
   7786             if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl)))
   7787                 select_all = true;
   7788         }
   7789         SetActiveID(id, window);
   7790         FocusWindow(window);
   7791     }
   7792     else if (io.MouseClicked[0])
   7793     {
   7794         // Release focus when we click outside
   7795         if (g.ActiveId == id)
   7796             ClearActiveID();
   7797     }
   7798 
   7799     bool value_changed = false;
   7800     bool enter_pressed = false;
   7801 
   7802     if (g.ActiveId == id)
   7803     {
   7804         if (!is_editable && !g.ActiveIdIsJustActivated)
   7805         {
   7806             // When read-only we always use the live data passed to the function
   7807             edit_state.Text.resize(buf_size+1);
   7808             const char* buf_end = NULL;
   7809             edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end);
   7810             edit_state.CurLenA = (int)(buf_end - buf);
   7811             edit_state.CursorClamp();
   7812         }
   7813 
   7814         edit_state.BufSizeA = buf_size;
   7815 
   7816         // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget.
   7817         // Down the line we should have a cleaner library-wide concept of Selected vs Active.
   7818         g.ActiveIdAllowOverlap = !io.MouseDown[0];
   7819 
   7820         // Edit in progress
   7821         const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX;
   7822         const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize*0.5f));
   7823 
   7824         const bool osx_double_click_selects_words = io.OSXBehaviors;      // OS X style: Double click selects by word instead of selecting whole text
   7825         if (select_all || (hovered && !osx_double_click_selects_words && io.MouseDoubleClicked[0]))
   7826         {
   7827             edit_state.SelectAll();
   7828             edit_state.SelectedAllMouseLock = true;
   7829         }
   7830         else if (hovered && osx_double_click_selects_words && io.MouseDoubleClicked[0])
   7831         {
   7832             // Select a word only, OS X style (by simulating keystrokes)
   7833             edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT);
   7834             edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT);
   7835         }
   7836         else if (io.MouseClicked[0] && !edit_state.SelectedAllMouseLock)
   7837         {
   7838             stb_textedit_click(&edit_state, &edit_state.StbState, mouse_x, mouse_y);
   7839             edit_state.CursorAnimReset();
   7840         }
   7841         else if (io.MouseDown[0] && !edit_state.SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f))
   7842         {
   7843             stb_textedit_drag(&edit_state, &edit_state.StbState, mouse_x, mouse_y);
   7844             edit_state.CursorAnimReset();
   7845             edit_state.CursorFollow = true;
   7846         }
   7847         if (edit_state.SelectedAllMouseLock && !io.MouseDown[0])
   7848             edit_state.SelectedAllMouseLock = false;
   7849 
   7850         if (io.InputCharacters[0])
   7851         {
   7852             // Process text input (before we check for Return because using some IME will effectively send a Return?)
   7853             // We ignore CTRL inputs, but need to allow CTRL+ALT as some keyboards (e.g. German) use AltGR - which is Alt+Ctrl - to input certain characters.
   7854             if (!(io.KeyCtrl && !io.KeyAlt) && is_editable)
   7855             {
   7856                 for (int n = 0; n < IM_ARRAYSIZE(io.InputCharacters) && io.InputCharacters[n]; n++)
   7857                     if (unsigned int c = (unsigned int)io.InputCharacters[n])
   7858                     {
   7859                         // Insert character if they pass filtering
   7860                         if (!InputTextFilterCharacter(&c, flags, callback, user_data))
   7861                             continue;
   7862                         edit_state.OnKeyPressed((int)c);
   7863                     }
   7864             }
   7865 
   7866             // Consume characters
   7867             memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters));
   7868         }
   7869 
   7870         // Handle various key-presses
   7871         bool cancel_edit = false;
   7872         const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0);
   7873         const bool is_shortcut_key_only = (io.OSXBehaviors ? (io.KeySuper && !io.KeyCtrl) : (io.KeyCtrl && !io.KeySuper)) && !io.KeyAlt && !io.KeyShift; // OS X style: Shortcuts using Cmd/Super instead of Ctrl
   7874         const bool is_wordmove_key_down = io.OSXBehaviors ? io.KeyAlt : io.KeyCtrl;                     // OS X style: Text editing cursor movement using Alt instead of Ctrl
   7875         const bool is_startend_key_down = io.OSXBehaviors && io.KeySuper && !io.KeyCtrl && !io.KeyAlt;  // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End
   7876 
   7877         if (IsKeyPressedMap(ImGuiKey_LeftArrow))                        { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); }
   7878         else if (IsKeyPressedMap(ImGuiKey_RightArrow))                  { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); }
   7879         else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline)     { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); }
   7880         else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline)   { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); }
   7881         else if (IsKeyPressedMap(ImGuiKey_Home))                        { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); }
   7882         else if (IsKeyPressedMap(ImGuiKey_End))                         { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); }
   7883         else if (IsKeyPressedMap(ImGuiKey_Delete) && is_editable)       { edit_state.OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); }
   7884         else if (IsKeyPressedMap(ImGuiKey_Backspace) && is_editable)
   7885         {
   7886             if (!edit_state.HasSelection())
   7887             {
   7888                 if (is_wordmove_key_down) edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT|STB_TEXTEDIT_K_SHIFT);
   7889                 else if (io.OSXBehaviors && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) edit_state.OnKeyPressed(STB_TEXTEDIT_K_LINESTART|STB_TEXTEDIT_K_SHIFT);
   7890             }
   7891             edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask);
   7892         }
   7893         else if (IsKeyPressedMap(ImGuiKey_Enter))
   7894         {
   7895             bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0;
   7896             if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl))
   7897             {
   7898                 ClearActiveID();
   7899                 enter_pressed = true;
   7900             }
   7901             else if (is_editable)
   7902             {
   7903                 unsigned int c = '\n'; // Insert new line
   7904                 if (InputTextFilterCharacter(&c, flags, callback, user_data))
   7905                     edit_state.OnKeyPressed((int)c);
   7906             }
   7907         }
   7908         else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !io.KeyCtrl && !io.KeyShift && !io.KeyAlt && is_editable)
   7909         {
   7910             unsigned int c = '\t'; // Insert TAB
   7911             if (InputTextFilterCharacter(&c, flags, callback, user_data))
   7912                 edit_state.OnKeyPressed((int)c);
   7913         }
   7914         else if (IsKeyPressedMap(ImGuiKey_Escape))                                     { ClearActiveID(); cancel_edit = true; }
   7915         else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Z) && is_editable)   { edit_state.OnKeyPressed(STB_TEXTEDIT_K_UNDO); edit_state.ClearSelection(); }
   7916         else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Y) && is_editable)   { edit_state.OnKeyPressed(STB_TEXTEDIT_K_REDO); edit_state.ClearSelection(); }
   7917         else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_A))                  { edit_state.SelectAll(); edit_state.CursorFollow = true; }
   7918         else if (is_shortcut_key_only && !is_password && ((IsKeyPressedMap(ImGuiKey_X) && is_editable) || IsKeyPressedMap(ImGuiKey_C)) && (!is_multiline || edit_state.HasSelection()))
   7919         {
   7920             // Cut, Copy
   7921             const bool cut = IsKeyPressedMap(ImGuiKey_X);
   7922             if (cut && !edit_state.HasSelection())
   7923                 edit_state.SelectAll();
   7924 
   7925             if (io.SetClipboardTextFn)
   7926             {
   7927                 const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0;
   7928                 const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : edit_state.CurLenW;
   7929                 edit_state.TempTextBuffer.resize((ie-ib) * 4 + 1);
   7930                 ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data+ib, edit_state.Text.Data+ie);
   7931                 SetClipboardText(edit_state.TempTextBuffer.Data);
   7932             }
   7933 
   7934             if (cut)
   7935             {
   7936                 edit_state.CursorFollow = true;
   7937                 stb_textedit_cut(&edit_state, &edit_state.StbState);
   7938             }
   7939         }
   7940         else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_V) && is_editable)
   7941         {
   7942             // Paste
   7943             if (const char* clipboard = GetClipboardText())
   7944             {
   7945                 // Filter pasted buffer
   7946                 const int clipboard_len = (int)strlen(clipboard);
   7947                 ImWchar* clipboard_filtered = (ImWchar*)ImGui::MemAlloc((clipboard_len+1) * sizeof(ImWchar));
   7948                 int clipboard_filtered_len = 0;
   7949                 for (const char* s = clipboard; *s; )
   7950                 {
   7951                     unsigned int c;
   7952                     s += ImTextCharFromUtf8(&c, s, NULL);
   7953                     if (c == 0)
   7954                         break;
   7955                     if (c >= 0x10000 || !InputTextFilterCharacter(&c, flags, callback, user_data))
   7956                         continue;
   7957                     clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c;
   7958                 }
   7959                 clipboard_filtered[clipboard_filtered_len] = 0;
   7960                 if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation
   7961                 {
   7962                     stb_textedit_paste(&edit_state, &edit_state.StbState, clipboard_filtered, clipboard_filtered_len);
   7963                     edit_state.CursorFollow = true;
   7964                 }
   7965                 ImGui::MemFree(clipboard_filtered);
   7966             }
   7967         }
   7968 
   7969         if (cancel_edit)
   7970         {
   7971             // Restore initial value
   7972             if (is_editable)
   7973             {
   7974                 ImStrncpy(buf, edit_state.InitialText.Data, buf_size);
   7975                 value_changed = true;
   7976             }
   7977         }
   7978         else
   7979         {
   7980             // Apply new value immediately - copy modified buffer back
   7981             // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer
   7982             // FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect.
   7983             // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks.
   7984             if (is_editable)
   7985             {
   7986                 edit_state.TempTextBuffer.resize(edit_state.Text.Size * 4);
   7987                 ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data, NULL);
   7988             }
   7989 
   7990             // User callback
   7991             if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0)
   7992             {
   7993                 IM_ASSERT(callback != NULL);
   7994 
   7995                 // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment.
   7996                 ImGuiInputTextFlags event_flag = 0;
   7997                 ImGuiKey event_key = ImGuiKey_COUNT;
   7998                 if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab))
   7999                 {
   8000                     event_flag = ImGuiInputTextFlags_CallbackCompletion;
   8001                     event_key = ImGuiKey_Tab;
   8002                 }
   8003                 else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow))
   8004                 {
   8005                     event_flag = ImGuiInputTextFlags_CallbackHistory;
   8006                     event_key = ImGuiKey_UpArrow;
   8007                 }
   8008                 else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow))
   8009                 {
   8010                     event_flag = ImGuiInputTextFlags_CallbackHistory;
   8011                     event_key = ImGuiKey_DownArrow;
   8012                 }
   8013                 else if (flags & ImGuiInputTextFlags_CallbackAlways)
   8014                     event_flag = ImGuiInputTextFlags_CallbackAlways;
   8015 
   8016                 if (event_flag)
   8017                 {
   8018                     ImGuiTextEditCallbackData callback_data;
   8019                     memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData));
   8020                     callback_data.EventFlag = event_flag;
   8021                     callback_data.Flags = flags;
   8022                     callback_data.UserData = user_data;
   8023                     callback_data.ReadOnly = !is_editable;
   8024 
   8025                     callback_data.EventKey = event_key;
   8026                     callback_data.Buf = edit_state.TempTextBuffer.Data;
   8027                     callback_data.BufTextLen = edit_state.CurLenA;
   8028                     callback_data.BufSize = edit_state.BufSizeA;
   8029                     callback_data.BufDirty = false;
   8030 
   8031                     // We have to convert from wchar-positions to UTF-8-positions, which can be pretty slow (an incentive to ditch the ImWchar buffer, see https://github.com/nothings/stb/issues/188)
   8032                     ImWchar* text = edit_state.Text.Data;
   8033                     const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor);
   8034                     const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start);
   8035                     const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_end);
   8036 
   8037                     // Call user code
   8038                     callback(&callback_data);
   8039 
   8040                     // Read back what user may have modified
   8041                     IM_ASSERT(callback_data.Buf == edit_state.TempTextBuffer.Data);  // Invalid to modify those fields
   8042                     IM_ASSERT(callback_data.BufSize == edit_state.BufSizeA);
   8043                     IM_ASSERT(callback_data.Flags == flags);
   8044                     if (callback_data.CursorPos != utf8_cursor_pos)            edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos);
   8045                     if (callback_data.SelectionStart != utf8_selection_start)  edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart);
   8046                     if (callback_data.SelectionEnd != utf8_selection_end)      edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd);
   8047                     if (callback_data.BufDirty)
   8048                     {
   8049                         IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!
   8050                         edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, callback_data.Buf, NULL);
   8051                         edit_state.CurLenA = callback_data.BufTextLen;  // Assume correct length and valid UTF-8 from user, saves us an extra strlen()
   8052                         edit_state.CursorAnimReset();
   8053                     }
   8054                 }
   8055             }
   8056 
   8057             // Copy back to user buffer
   8058             if (is_editable && strcmp(edit_state.TempTextBuffer.Data, buf) != 0)
   8059             {
   8060                 ImStrncpy(buf, edit_state.TempTextBuffer.Data, buf_size);
   8061                 value_changed = true;
   8062             }
   8063         }
   8064     }
   8065 
   8066     // Render
   8067     // Select which buffer we are going to display. When ImGuiInputTextFlags_NoLiveEdit is set 'buf' might still be the old value. We set buf to NULL to prevent accidental usage from now on.
   8068     const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempTextBuffer.Data : buf; buf = NULL; 
   8069 
   8070     if (!is_multiline)
   8071         RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
   8072 
   8073     const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + size.x, frame_bb.Min.y + size.y); // Not using frame_bb.Max because we have adjusted size
   8074     ImVec2 render_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding;
   8075     ImVec2 text_size(0.f, 0.f);
   8076     const bool is_currently_scrolling = (edit_state.Id == id && is_multiline && g.ActiveId == draw_window->GetIDNoKeepAlive("#SCROLLY"));
   8077     if (g.ActiveId == id || is_currently_scrolling)
   8078     {
   8079         edit_state.CursorAnim += io.DeltaTime;
   8080 
   8081         // This is going to be messy. We need to:
   8082         // - Display the text (this alone can be more easily clipped)
   8083         // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation)
   8084         // - Measure text height (for scrollbar)
   8085         // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort)
   8086         // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8.
   8087         const ImWchar* text_begin = edit_state.Text.Data;
   8088         ImVec2 cursor_offset, select_start_offset;
   8089 
   8090         {
   8091             // Count lines + find lines numbers straddling 'cursor' and 'select_start' position.
   8092             const ImWchar* searches_input_ptr[2];
   8093             searches_input_ptr[0] = text_begin + edit_state.StbState.cursor;
   8094             searches_input_ptr[1] = NULL;
   8095             int searches_remaining = 1;
   8096             int searches_result_line_number[2] = { -1, -999 };
   8097             if (edit_state.StbState.select_start != edit_state.StbState.select_end)
   8098             {
   8099                 searches_input_ptr[1] = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
   8100                 searches_result_line_number[1] = -1;
   8101                 searches_remaining++;
   8102             }
   8103 
   8104             // Iterate all lines to find our line numbers
   8105             // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter.
   8106             searches_remaining += is_multiline ? 1 : 0;
   8107             int line_count = 0;
   8108             for (const ImWchar* s = text_begin; *s != 0; s++)
   8109                 if (*s == '\n')
   8110                 {
   8111                     line_count++;
   8112                     if (searches_result_line_number[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_number[0] = line_count; if (--searches_remaining <= 0) break; }
   8113                     if (searches_result_line_number[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_number[1] = line_count; if (--searches_remaining <= 0) break; }
   8114                 }
   8115             line_count++;
   8116             if (searches_result_line_number[0] == -1) searches_result_line_number[0] = line_count;
   8117             if (searches_result_line_number[1] == -1) searches_result_line_number[1] = line_count;
   8118 
   8119             // Calculate 2d position by finding the beginning of the line and measuring distance
   8120             cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x;
   8121             cursor_offset.y = searches_result_line_number[0] * g.FontSize;
   8122             if (searches_result_line_number[1] >= 0)
   8123             {
   8124                 select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x;
   8125                 select_start_offset.y = searches_result_line_number[1] * g.FontSize;
   8126             }
   8127 
   8128             // Calculate text height
   8129             if (is_multiline)
   8130                 text_size = ImVec2(size.x, line_count * g.FontSize);
   8131         }
   8132 
   8133         // Scroll
   8134         if (edit_state.CursorFollow)
   8135         {
   8136             // Horizontal scroll in chunks of quarter width
   8137             if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll))
   8138             {
   8139                 const float scroll_increment_x = size.x * 0.25f;
   8140                 if (cursor_offset.x < edit_state.ScrollX)
   8141                     edit_state.ScrollX = (float)(int)ImMax(0.0f, cursor_offset.x - scroll_increment_x);
   8142                 else if (cursor_offset.x - size.x >= edit_state.ScrollX)
   8143                     edit_state.ScrollX = (float)(int)(cursor_offset.x - size.x + scroll_increment_x);
   8144             }
   8145             else
   8146             {
   8147                 edit_state.ScrollX = 0.0f;
   8148             }
   8149 
   8150             // Vertical scroll
   8151             if (is_multiline)
   8152             {
   8153                 float scroll_y = draw_window->Scroll.y;
   8154                 if (cursor_offset.y - g.FontSize < scroll_y)
   8155                     scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize);
   8156                 else if (cursor_offset.y - size.y >= scroll_y)
   8157                     scroll_y = cursor_offset.y - size.y;
   8158                 draw_window->DC.CursorPos.y += (draw_window->Scroll.y - scroll_y);   // To avoid a frame of lag
   8159                 draw_window->Scroll.y = scroll_y;
   8160                 render_pos.y = draw_window->DC.CursorPos.y;
   8161             }
   8162         }
   8163         edit_state.CursorFollow = false;
   8164         const ImVec2 render_scroll = ImVec2(edit_state.ScrollX, 0.0f);
   8165 
   8166         // Draw selection
   8167         if (edit_state.StbState.select_start != edit_state.StbState.select_end)
   8168         {
   8169             const ImWchar* text_selected_begin = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
   8170             const ImWchar* text_selected_end = text_begin + ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end);
   8171 
   8172             float bg_offy_up = is_multiline ? 0.0f : -1.0f;    // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection.
   8173             float bg_offy_dn = is_multiline ? 0.0f : 2.0f;
   8174             ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg);
   8175             ImVec2 rect_pos = render_pos + select_start_offset - render_scroll;
   8176             for (const ImWchar* p = text_selected_begin; p < text_selected_end; )
   8177             {
   8178                 if (rect_pos.y > clip_rect.w + g.FontSize)
   8179                     break;
   8180                 if (rect_pos.y < clip_rect.y)
   8181                 {
   8182                     while (p < text_selected_end)
   8183                         if (*p++ == '\n')
   8184                             break;
   8185                 }
   8186                 else
   8187                 {
   8188                     ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true);
   8189                     if (rect_size.x <= 0.0f) rect_size.x = (float)(int)(g.Font->GetCharAdvance((unsigned short)' ') * 0.50f); // So we can see selected empty lines
   8190                     ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos +ImVec2(rect_size.x, bg_offy_dn));
   8191                     rect.Clip(clip_rect);
   8192                     if (rect.Overlaps(clip_rect))
   8193                         draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
   8194                 }
   8195                 rect_pos.x = render_pos.x - render_scroll.x;
   8196                 rect_pos.y += g.FontSize;
   8197             }
   8198         }
   8199 
   8200         draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf_display, buf_display + edit_state.CurLenA, 0.0f, is_multiline ? NULL : &clip_rect);
   8201 
   8202         // Draw blinking cursor
   8203         bool cursor_is_visible = (g.InputTextState.CursorAnim <= 0.0f) || fmodf(g.InputTextState.CursorAnim, 1.20f) <= 0.80f;
   8204         ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll;
   8205         ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y-g.FontSize+0.5f, cursor_screen_pos.x+1.0f, cursor_screen_pos.y-1.5f);
   8206         if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
   8207             draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text));
   8208 
   8209         // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.)
   8210         if (is_editable)
   8211             g.OsImePosRequest = ImVec2(cursor_screen_pos.x - 1, cursor_screen_pos.y - g.FontSize);
   8212     }
   8213     else
   8214     {
   8215         // Render text only
   8216         const char* buf_end = NULL;
   8217         if (is_multiline)
   8218             text_size = ImVec2(size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_end) * g.FontSize); // We don't need width
   8219         draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos, GetColorU32(ImGuiCol_Text), buf_display, buf_end, 0.0f, is_multiline ? NULL : &clip_rect);
   8220     }
   8221 
   8222     if (is_multiline)
   8223     {
   8224         Dummy(text_size + ImVec2(0.0f, g.FontSize)); // Always add room to scroll an extra line
   8225         EndChildFrame();
   8226         EndGroup();
   8227     }
   8228 
   8229     if (is_password)
   8230         PopFont();
   8231 
   8232     // Log as text
   8233     if (g.LogEnabled && !is_password)
   8234         LogRenderedText(render_pos, buf_display, NULL);
   8235 
   8236     if (label_size.x > 0)
   8237         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
   8238 
   8239     if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0)
   8240         return enter_pressed;
   8241     else
   8242         return value_changed;
   8243 }
   8244 
   8245 bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
   8246 {
   8247     IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline()
   8248     return InputTextEx(label, buf, (int)buf_size, ImVec2(0,0), flags, callback, user_data);
   8249 }
   8250 
   8251 bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
   8252 {
   8253     return InputTextEx(label, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data);
   8254 }
   8255 
   8256 // NB: scalar_format here must be a simple "%xx" format string with no prefix/suffix (unlike the Drag/Slider functions "display_format" argument)
   8257 bool ImGui::InputScalarEx(const char* label, ImGuiDataType data_type, void* data_ptr, void* step_ptr, void* step_fast_ptr, const char* scalar_format, ImGuiInputTextFlags extra_flags)
   8258 {
   8259     ImGuiWindow* window = GetCurrentWindow();
   8260     if (window->SkipItems)
   8261         return false;
   8262 
   8263     ImGuiContext& g = *GImGui;
   8264     const ImGuiStyle& style = g.Style;
   8265     const ImVec2 label_size = CalcTextSize(label, NULL, true);
   8266 
   8267     BeginGroup();
   8268     PushID(label);
   8269     const ImVec2 button_sz = ImVec2(g.FontSize, g.FontSize) + style.FramePadding*2.0f;
   8270     if (step_ptr)
   8271         PushItemWidth(ImMax(1.0f, CalcItemWidth() - (button_sz.x + style.ItemInnerSpacing.x)*2));
   8272 
   8273     char buf[64];
   8274     DataTypeFormatString(data_type, data_ptr, scalar_format, buf, IM_ARRAYSIZE(buf));
   8275 
   8276     bool value_changed = false;
   8277     if (!(extra_flags & ImGuiInputTextFlags_CharsHexadecimal))
   8278         extra_flags |= ImGuiInputTextFlags_CharsDecimal;
   8279     extra_flags |= ImGuiInputTextFlags_AutoSelectAll;
   8280     if (InputText("", buf, IM_ARRAYSIZE(buf), extra_flags)) // PushId(label) + "" gives us the expected ID from outside point of view
   8281         value_changed = DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, scalar_format);
   8282 
   8283     // Step buttons
   8284     if (step_ptr)
   8285     {
   8286         PopItemWidth();
   8287         SameLine(0, style.ItemInnerSpacing.x);
   8288         if (ButtonEx("-", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups))
   8289         {
   8290             DataTypeApplyOp(data_type, '-', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr);
   8291             value_changed = true;
   8292         }
   8293         SameLine(0, style.ItemInnerSpacing.x);
   8294         if (ButtonEx("+", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups))
   8295         {
   8296             DataTypeApplyOp(data_type, '+', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr);
   8297             value_changed = true;
   8298         }
   8299     }
   8300     PopID();
   8301 
   8302     if (label_size.x > 0)
   8303     {
   8304         SameLine(0, style.ItemInnerSpacing.x);
   8305         RenderText(ImVec2(window->DC.CursorPos.x, window->DC.CursorPos.y + style.FramePadding.y), label);
   8306         ItemSize(label_size, style.FramePadding.y);
   8307     }
   8308     EndGroup();
   8309 
   8310     return value_changed;
   8311 }
   8312 
   8313 bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags extra_flags)
   8314 {
   8315     char display_format[16];
   8316     if (decimal_precision < 0)
   8317         strcpy(display_format, "%f");      // Ideally we'd have a minimum decimal precision of 1 to visually denote that this is a float, while hiding non-significant digits? %f doesn't have a minimum of 1
   8318     else
   8319         ImFormatString(display_format, IM_ARRAYSIZE(display_format), "%%.%df", decimal_precision);
   8320     return InputScalarEx(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), display_format, extra_flags);
   8321 }
   8322 
   8323 bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags extra_flags)
   8324 {
   8325     // Hexadecimal input provided as a convenience but the flag name is awkward. Typically you'd use InputText() to parse your own data, if you want to handle prefixes.
   8326     const char* scalar_format = (extra_flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d";
   8327     return InputScalarEx(label, ImGuiDataType_Int, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), scalar_format, extra_flags);
   8328 }
   8329 
   8330 bool ImGui::InputFloatN(const char* label, float* v, int components, int decimal_precision, ImGuiInputTextFlags extra_flags)
   8331 {
   8332     ImGuiWindow* window = GetCurrentWindow();
   8333     if (window->SkipItems)
   8334         return false;
   8335 
   8336     ImGuiContext& g = *GImGui;
   8337     bool value_changed = false;
   8338     BeginGroup();
   8339     PushID(label);
   8340     PushMultiItemsWidths(components);
   8341     for (int i = 0; i < components; i++)
   8342     {
   8343         PushID(i);
   8344         value_changed |= InputFloat("##v", &v[i], 0, 0, decimal_precision, extra_flags);
   8345         SameLine(0, g.Style.ItemInnerSpacing.x);
   8346         PopID();
   8347         PopItemWidth();
   8348     }
   8349     PopID();
   8350 
   8351     window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y);
   8352     TextUnformatted(label, FindRenderedTextEnd(label));
   8353     EndGroup();
   8354 
   8355     return value_changed;
   8356 }
   8357 
   8358 bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags extra_flags)
   8359 {
   8360     return InputFloatN(label, v, 2, decimal_precision, extra_flags);
   8361 }
   8362 
   8363 bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags extra_flags)
   8364 {
   8365     return InputFloatN(label, v, 3, decimal_precision, extra_flags);
   8366 }
   8367 
   8368 bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags extra_flags)
   8369 {
   8370     return InputFloatN(label, v, 4, decimal_precision, extra_flags);
   8371 }
   8372 
   8373 bool ImGui::InputIntN(const char* label, int* v, int components, ImGuiInputTextFlags extra_flags)
   8374 {
   8375     ImGuiWindow* window = GetCurrentWindow();
   8376     if (window->SkipItems)
   8377         return false;
   8378 
   8379     ImGuiContext& g = *GImGui;
   8380     bool value_changed = false;
   8381     BeginGroup();
   8382     PushID(label);
   8383     PushMultiItemsWidths(components);
   8384     for (int i = 0; i < components; i++)
   8385     {
   8386         PushID(i);
   8387         value_changed |= InputInt("##v", &v[i], 0, 0, extra_flags);
   8388         SameLine(0, g.Style.ItemInnerSpacing.x);
   8389         PopID();
   8390         PopItemWidth();
   8391     }
   8392     PopID();
   8393 
   8394     window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y);
   8395     TextUnformatted(label, FindRenderedTextEnd(label));
   8396     EndGroup();
   8397 
   8398     return value_changed;
   8399 }
   8400 
   8401 bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags)
   8402 {
   8403     return InputIntN(label, v, 2, extra_flags);
   8404 }
   8405 
   8406 bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags)
   8407 {
   8408     return InputIntN(label, v, 3, extra_flags);
   8409 }
   8410 
   8411 bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags)
   8412 {
   8413     return InputIntN(label, v, 4, extra_flags);
   8414 }
   8415 
   8416 static bool Items_ArrayGetter(void* data, int idx, const char** out_text)
   8417 {
   8418     const char* const* items = (const char* const*)data;
   8419     if (out_text)
   8420         *out_text = items[idx];
   8421     return true;
   8422 }
   8423 
   8424 static bool Items_SingleStringGetter(void* data, int idx, const char** out_text)
   8425 {
   8426     // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited.
   8427     const char* items_separated_by_zeros = (const char*)data;
   8428     int items_count = 0;
   8429     const char* p = items_separated_by_zeros;
   8430     while (*p)
   8431     {
   8432         if (idx == items_count)
   8433             break;
   8434         p += strlen(p) + 1;
   8435         items_count++;
   8436     }
   8437     if (!*p)
   8438         return false;
   8439     if (out_text)
   8440         *out_text = p;
   8441     return true;
   8442 }
   8443 
   8444 // Combo box helper allowing to pass an array of strings.
   8445 bool ImGui::Combo(const char* label, int* current_item, const char* const* items, int items_count, int height_in_items)
   8446 {
   8447     const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items);
   8448     return value_changed;
   8449 }
   8450 
   8451 // Combo box helper allowing to pass all items in a single string.
   8452 bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items)
   8453 {
   8454     int items_count = 0;
   8455     const char* p = items_separated_by_zeros;       // FIXME-OPT: Avoid computing this, or at least only when combo is open
   8456     while (*p)
   8457     {
   8458         p += strlen(p) + 1;
   8459         items_count++;
   8460     }
   8461     bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items);
   8462     return value_changed;
   8463 }
   8464 
   8465 // Combo box function.
   8466 bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items)
   8467 {
   8468     ImGuiWindow* window = GetCurrentWindow();
   8469     if (window->SkipItems)
   8470         return false;
   8471 
   8472     ImGuiContext& g = *GImGui;
   8473     const ImGuiStyle& style = g.Style;
   8474     const ImGuiID id = window->GetID(label);
   8475     const float w = CalcItemWidth();
   8476 
   8477     const ImVec2 label_size = CalcTextSize(label, NULL, true);
   8478     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
   8479     const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
   8480     ItemSize(total_bb, style.FramePadding.y);
   8481     if (!ItemAdd(total_bb, &id))
   8482         return false;
   8483 
   8484     const float arrow_size = (g.FontSize + style.FramePadding.x * 2.0f);
   8485     const bool hovered = IsHovered(frame_bb, id);
   8486     bool popup_open = IsPopupOpen(id);
   8487     bool popup_opened_now = false;
   8488 
   8489     const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f));
   8490     RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
   8491     RenderFrame(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32(popup_open || hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button), true, style.FrameRounding); // FIXME-ROUNDING
   8492     RenderCollapseTriangle(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y) + style.FramePadding, true);
   8493 
   8494     if (*current_item >= 0 && *current_item < items_count)
   8495     {
   8496         const char* item_text;
   8497         if (items_getter(data, *current_item, &item_text))
   8498             RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, item_text, NULL, NULL, ImVec2(0.0f,0.0f));
   8499     }
   8500 
   8501     if (label_size.x > 0)
   8502         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
   8503 
   8504     if (hovered)
   8505     {
   8506         SetHoveredID(id);
   8507         if (g.IO.MouseClicked[0])
   8508         {
   8509             ClearActiveID();
   8510             if (IsPopupOpen(id))
   8511             {
   8512                 ClosePopup(id);
   8513             }
   8514             else
   8515             {
   8516                 FocusWindow(window);
   8517                 OpenPopup(label);
   8518                 popup_open = popup_opened_now = true;
   8519             }
   8520         }
   8521     }
   8522 
   8523     bool value_changed = false;
   8524     if (IsPopupOpen(id))
   8525     {
   8526         // Size default to hold ~7 items
   8527         if (height_in_items < 0)
   8528             height_in_items = 7;
   8529 
   8530         float popup_height = (label_size.y + style.ItemSpacing.y) * ImMin(items_count, height_in_items) + (style.FramePadding.y * 3);
   8531         float popup_y1 = frame_bb.Max.y;
   8532         float popup_y2 = ImClamp(popup_y1 + popup_height, popup_y1, g.IO.DisplaySize.y - style.DisplaySafeAreaPadding.y);
   8533         if ((popup_y2 - popup_y1) < ImMin(popup_height, frame_bb.Min.y - style.DisplaySafeAreaPadding.y))
   8534         {
   8535             // Position our combo ABOVE because there's more space to fit! (FIXME: Handle in Begin() or use a shared helper. We have similar code in Begin() for popup placement)
   8536             popup_y1 = ImClamp(frame_bb.Min.y - popup_height, style.DisplaySafeAreaPadding.y, frame_bb.Min.y);
   8537             popup_y2 = frame_bb.Min.y;
   8538         }
   8539         ImRect popup_rect(ImVec2(frame_bb.Min.x, popup_y1), ImVec2(frame_bb.Max.x, popup_y2));
   8540         SetNextWindowPos(popup_rect.Min);
   8541         SetNextWindowSize(popup_rect.GetSize());
   8542         PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
   8543 
   8544         const ImGuiWindowFlags flags = ImGuiWindowFlags_ComboBox | ((window->Flags & ImGuiWindowFlags_ShowBorders) ? ImGuiWindowFlags_ShowBorders : 0);
   8545         if (BeginPopupEx(label, flags))
   8546         {
   8547             // Display items
   8548             Spacing();
   8549             for (int i = 0; i < items_count; i++)
   8550             {
   8551                 PushID((void*)(intptr_t)i);
   8552                 const bool item_selected = (i == *current_item);
   8553                 const char* item_text;
   8554                 if (!items_getter(data, i, &item_text))
   8555                     item_text = "*Unknown item*";
   8556                 if (Selectable(item_text, item_selected))
   8557                 {
   8558                     ClearActiveID();
   8559                     value_changed = true;
   8560                     *current_item = i;
   8561                 }
   8562                 if (item_selected && popup_opened_now)
   8563                     SetScrollHere();
   8564                 PopID();
   8565             }
   8566             EndPopup();
   8567         }
   8568         PopStyleVar();
   8569     }
   8570     return value_changed;
   8571 }
   8572 
   8573 // Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image.
   8574 // But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID.
   8575 bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
   8576 {
   8577     ImGuiWindow* window = GetCurrentWindow();
   8578     if (window->SkipItems)
   8579         return false;
   8580 
   8581     ImGuiContext& g = *GImGui;
   8582     const ImGuiStyle& style = g.Style;
   8583 
   8584     if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1)
   8585         PopClipRect();
   8586 
   8587     ImGuiID id = window->GetID(label);
   8588     ImVec2 label_size = CalcTextSize(label, NULL, true);
   8589     ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y);
   8590     ImVec2 pos = window->DC.CursorPos;
   8591     pos.y += window->DC.CurrentLineTextBaseOffset;
   8592     ImRect bb(pos, pos + size);
   8593     ItemSize(bb);
   8594 
   8595     // Fill horizontal space.
   8596     ImVec2 window_padding = window->WindowPadding;
   8597     float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? GetWindowContentRegionMax().x : GetContentRegionMax().x;
   8598     float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - window->DC.CursorPos.x);
   8599     ImVec2 size_draw((size_arg.x != 0 && !(flags & ImGuiSelectableFlags_DrawFillAvailWidth)) ? size_arg.x : w_draw, size_arg.y != 0.0f ? size_arg.y : size.y);
   8600     ImRect bb_with_spacing(pos, pos + size_draw);
   8601     if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth))
   8602         bb_with_spacing.Max.x += window_padding.x;
   8603 
   8604     // Selectables are tightly packed together, we extend the box to cover spacing between selectable.
   8605     float spacing_L = (float)(int)(style.ItemSpacing.x * 0.5f);
   8606     float spacing_U = (float)(int)(style.ItemSpacing.y * 0.5f);
   8607     float spacing_R = style.ItemSpacing.x - spacing_L;
   8608     float spacing_D = style.ItemSpacing.y - spacing_U;
   8609     bb_with_spacing.Min.x -= spacing_L;
   8610     bb_with_spacing.Min.y -= spacing_U;
   8611     bb_with_spacing.Max.x += spacing_R;
   8612     bb_with_spacing.Max.y += spacing_D;
   8613     if (!ItemAdd(bb_with_spacing, &id))
   8614     {
   8615         if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1)
   8616             PushColumnClipRect();
   8617         return false;
   8618     }
   8619 
   8620     ImGuiButtonFlags button_flags = 0;
   8621     if (flags & ImGuiSelectableFlags_Menu) button_flags |= ImGuiButtonFlags_PressedOnClick;
   8622     if (flags & ImGuiSelectableFlags_MenuItem) button_flags |= ImGuiButtonFlags_PressedOnClick|ImGuiButtonFlags_PressedOnRelease;
   8623     if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled;
   8624     if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick;
   8625     bool hovered, held;
   8626     bool pressed = ButtonBehavior(bb_with_spacing, id, &hovered, &held, button_flags);
   8627     if (flags & ImGuiSelectableFlags_Disabled)
   8628         selected = false;
   8629 
   8630     // Render
   8631     if (hovered || selected)
   8632     {
   8633         const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
   8634         RenderFrame(bb_with_spacing.Min, bb_with_spacing.Max, col, false, 0.0f);
   8635     }
   8636 
   8637     if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1)
   8638     {
   8639         PushColumnClipRect();
   8640         bb_with_spacing.Max.x -= (GetContentRegionMax().x - max_x);
   8641     }
   8642 
   8643     if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
   8644     RenderTextClipped(bb.Min, bb_with_spacing.Max, label, NULL, &label_size, ImVec2(0.0f,0.0f));
   8645     if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor();
   8646 
   8647     // Automatically close popups
   8648     if (pressed && !(flags & ImGuiSelectableFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
   8649         CloseCurrentPopup();
   8650     return pressed;
   8651 }
   8652 
   8653 bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
   8654 {
   8655     if (Selectable(label, *p_selected, flags, size_arg))
   8656     {
   8657         *p_selected = !*p_selected;
   8658         return true;
   8659     }
   8660     return false;
   8661 }
   8662 
   8663 // Helper to calculate the size of a listbox and display a label on the right.
   8664 // Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an empty label "##empty"
   8665 bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg)
   8666 {
   8667     ImGuiWindow* window = GetCurrentWindow();
   8668     if (window->SkipItems)
   8669         return false;
   8670 
   8671     const ImGuiStyle& style = GetStyle();
   8672     const ImGuiID id = GetID(label);
   8673     const ImVec2 label_size = CalcTextSize(label, NULL, true);
   8674 
   8675     // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
   8676     ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y);
   8677     ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y));
   8678     ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
   8679     ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
   8680     window->DC.LastItemRect = bb;
   8681 
   8682     BeginGroup();
   8683     if (label_size.x > 0)
   8684         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
   8685 
   8686     BeginChildFrame(id, frame_bb.GetSize());
   8687     return true;
   8688 }
   8689 
   8690 bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items)
   8691 {
   8692     // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
   8693     // However we don't add +0.40f if items_count <= height_in_items. It is slightly dodgy, because it means a dynamic list of items will make the widget resize occasionally when it crosses that size.
   8694     // I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution.
   8695     if (height_in_items < 0)
   8696         height_in_items = ImMin(items_count, 7);
   8697     float height_in_items_f = height_in_items < items_count ? (height_in_items + 0.40f) : (height_in_items + 0.00f);
   8698 
   8699     // We include ItemSpacing.y so that a list sized for the exact number of items doesn't make a scrollbar appears. We could also enforce that by passing a flag to BeginChild().
   8700     ImVec2 size;
   8701     size.x = 0.0f;
   8702     size.y = GetTextLineHeightWithSpacing() * height_in_items_f + GetStyle().ItemSpacing.y;
   8703     return ListBoxHeader(label, size);
   8704 }
   8705 
   8706 void ImGui::ListBoxFooter()
   8707 {
   8708     ImGuiWindow* parent_window = GetParentWindow();
   8709     const ImRect bb = parent_window->DC.LastItemRect;
   8710     const ImGuiStyle& style = GetStyle();
   8711 
   8712     EndChildFrame();
   8713 
   8714     // Redeclare item size so that it includes the label (we have stored the full size in LastItemRect)
   8715     // We call SameLine() to restore DC.CurrentLine* data
   8716     SameLine();
   8717     parent_window->DC.CursorPos = bb.Min;
   8718     ItemSize(bb, style.FramePadding.y);
   8719     EndGroup();
   8720 }
   8721 
   8722 bool ImGui::ListBox(const char* label, int* current_item, const char* const* items, int items_count, int height_items)
   8723 {
   8724     const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items);
   8725     return value_changed;
   8726 }
   8727 
   8728 bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items)
   8729 {
   8730     if (!ListBoxHeader(label, items_count, height_in_items))
   8731         return false;
   8732 
   8733     // Assume all items have even height (= 1 line of text). If you need items of different or variable sizes you can create a custom version of ListBox() in your code without using the clipper.
   8734     bool value_changed = false;
   8735     ImGuiListClipper clipper(items_count, GetTextLineHeightWithSpacing());
   8736     while (clipper.Step())
   8737         for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
   8738         {
   8739             const bool item_selected = (i == *current_item);
   8740             const char* item_text;
   8741             if (!items_getter(data, i, &item_text))
   8742                 item_text = "*Unknown item*";
   8743 
   8744             PushID(i);
   8745             if (Selectable(item_text, item_selected))
   8746             {
   8747                 *current_item = i;
   8748                 value_changed = true;
   8749             }
   8750             PopID();
   8751         }
   8752     ListBoxFooter();
   8753     return value_changed;
   8754 }
   8755 
   8756 bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled)
   8757 {
   8758     ImGuiWindow* window = GetCurrentWindow();
   8759     if (window->SkipItems)
   8760         return false;
   8761 
   8762     ImGuiContext& g = *GImGui;
   8763     ImVec2 pos = window->DC.CursorPos;
   8764     ImVec2 label_size = CalcTextSize(label, NULL, true);
   8765     ImVec2 shortcut_size = shortcut ? CalcTextSize(shortcut, NULL) : ImVec2(0.0f, 0.0f);
   8766     float w = window->MenuColumns.DeclColumns(label_size.x, shortcut_size.x, (float)(int)(g.FontSize * 1.20f)); // Feedback for next frame
   8767     float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
   8768 
   8769     bool pressed = Selectable(label, false, ImGuiSelectableFlags_MenuItem | ImGuiSelectableFlags_DrawFillAvailWidth | (enabled ? 0 : ImGuiSelectableFlags_Disabled), ImVec2(w, 0.0f));
   8770     if (shortcut_size.x > 0.0f)
   8771     {
   8772         PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
   8773         RenderText(pos + ImVec2(window->MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false);
   8774         PopStyleColor();
   8775     }
   8776 
   8777     if (selected)
   8778         RenderCheckMark(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.20f, 0.0f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled));
   8779 
   8780     return pressed;
   8781 }
   8782 
   8783 bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled)
   8784 {
   8785     if (MenuItem(label, shortcut, p_selected ? *p_selected : false, enabled))
   8786     {
   8787         if (p_selected)
   8788             *p_selected = !*p_selected;
   8789         return true;
   8790     }
   8791     return false;
   8792 }
   8793 
   8794 bool ImGui::BeginMainMenuBar()
   8795 {
   8796     ImGuiContext& g = *GImGui;
   8797     SetNextWindowPos(ImVec2(0.0f, 0.0f));
   8798     SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.FontBaseSize + g.Style.FramePadding.y * 2.0f));
   8799     PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
   8800     PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0));
   8801     if (!Begin("##MainMenuBar", NULL, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_MenuBar)
   8802         || !BeginMenuBar())
   8803     {
   8804         End();
   8805         PopStyleVar(2);
   8806         return false;
   8807     }
   8808     g.CurrentWindow->DC.MenuBarOffsetX += g.Style.DisplaySafeAreaPadding.x;
   8809     return true;
   8810 }
   8811 
   8812 void ImGui::EndMainMenuBar()
   8813 {
   8814     EndMenuBar();
   8815     End();
   8816     PopStyleVar(2);
   8817 }
   8818 
   8819 bool ImGui::BeginMenuBar()
   8820 {
   8821     ImGuiWindow* window = GetCurrentWindow();
   8822     if (window->SkipItems)
   8823         return false;
   8824     if (!(window->Flags & ImGuiWindowFlags_MenuBar))
   8825         return false;
   8826 
   8827     IM_ASSERT(!window->DC.MenuBarAppending);
   8828     BeginGroup(); // Save position
   8829     PushID("##menubar");
   8830     ImRect rect = window->MenuBarRect();
   8831     PushClipRect(ImVec2(ImFloor(rect.Min.x+0.5f), ImFloor(rect.Min.y + window->BorderSize + 0.5f)), ImVec2(ImFloor(rect.Max.x+0.5f), ImFloor(rect.Max.y+0.5f)), false);
   8832     window->DC.CursorPos = ImVec2(rect.Min.x + window->DC.MenuBarOffsetX, rect.Min.y);// + g.Style.FramePadding.y);
   8833     window->DC.LayoutType = ImGuiLayoutType_Horizontal;
   8834     window->DC.MenuBarAppending = true;
   8835     AlignFirstTextHeightToWidgets();
   8836     return true;
   8837 }
   8838 
   8839 void ImGui::EndMenuBar()
   8840 {
   8841     ImGuiWindow* window = GetCurrentWindow();
   8842     if (window->SkipItems)
   8843         return;
   8844 
   8845     IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar);
   8846     IM_ASSERT(window->DC.MenuBarAppending);
   8847     PopClipRect();
   8848     PopID();
   8849     window->DC.MenuBarOffsetX = window->DC.CursorPos.x - window->MenuBarRect().Min.x;
   8850     window->DC.GroupStack.back().AdvanceCursor = false;
   8851     EndGroup();
   8852     window->DC.LayoutType = ImGuiLayoutType_Vertical;
   8853     window->DC.MenuBarAppending = false;
   8854 }
   8855 
   8856 bool ImGui::BeginMenu(const char* label, bool enabled)
   8857 {
   8858     ImGuiWindow* window = GetCurrentWindow();
   8859     if (window->SkipItems)
   8860         return false;
   8861 
   8862     ImGuiContext& g = *GImGui;
   8863     const ImGuiStyle& style = g.Style;
   8864     const ImGuiID id = window->GetID(label);
   8865 
   8866     ImVec2 label_size = CalcTextSize(label, NULL, true);
   8867     ImGuiWindow* backed_focused_window = g.FocusedWindow;
   8868 
   8869     bool pressed;
   8870     bool menu_is_open = IsPopupOpen(id);
   8871     bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentMenuSet == window->GetID("##menus"));
   8872     if (menuset_is_open)
   8873         g.FocusedWindow = window;
   8874 
   8875     // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu (using FindBestPopupWindowPos).
   8876     ImVec2 popup_pos, pos = window->DC.CursorPos;
   8877     if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
   8878     {
   8879         popup_pos = ImVec2(pos.x - window->WindowPadding.x, pos.y - style.FramePadding.y + window->MenuBarHeight());
   8880         window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
   8881         PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f);
   8882         float w = label_size.x;
   8883         pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
   8884         PopStyleVar();
   8885         SameLine();
   8886         window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
   8887     }
   8888     else
   8889     {
   8890         popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
   8891         float w = window->MenuColumns.DeclColumns(label_size.x, 0.0f, (float)(int)(g.FontSize * 1.20f)); // Feedback to next frame
   8892         float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
   8893         pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
   8894         if (!enabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
   8895         RenderCollapseTriangle(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.20f, 0.0f), false);
   8896         if (!enabled) PopStyleColor();
   8897     }
   8898 
   8899     bool hovered = enabled && IsHovered(window->DC.LastItemRect, id);
   8900     if (menuset_is_open)
   8901         g.FocusedWindow = backed_focused_window;
   8902 
   8903     bool want_open = false, want_close = false;
   8904     if (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
   8905     {
   8906         // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.
   8907         bool moving_within_opened_triangle = false;
   8908         if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentWindow == window)
   8909         {
   8910             if (ImGuiWindow* next_window = g.OpenPopupStack[g.CurrentPopupStack.Size].Window)
   8911             {
   8912                 ImRect next_window_rect = next_window->Rect();
   8913                 ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta;
   8914                 ImVec2 tb = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR();
   8915                 ImVec2 tc = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR();
   8916                 float extra = ImClamp(fabsf(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack.
   8917                 ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f;   // to avoid numerical issues
   8918                 tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f);            // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale?
   8919                 tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f);
   8920                 moving_within_opened_triangle = ImIsPointInTriangle(g.IO.MousePos, ta, tb, tc);
   8921                 //window->DrawList->PushClipRectFullScreen(); window->DrawList->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); window->DrawList->PopClipRect(); // Debug
   8922             }
   8923         }
   8924 
   8925         want_close = (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_within_opened_triangle);
   8926         want_open = (!menu_is_open && hovered && !moving_within_opened_triangle) || (!menu_is_open && hovered && pressed);
   8927     }
   8928     else if (menu_is_open && pressed && menuset_is_open) // menu-bar: click open menu to close
   8929     {
   8930         want_close = true;
   8931         want_open = menu_is_open = false;
   8932     }
   8933     else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // menu-bar: first click to open, then hover to open others
   8934         want_open = true;
   8935     if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }'
   8936         want_close = true;
   8937     if (want_close && IsPopupOpen(id))
   8938         ClosePopupToLevel(GImGui->CurrentPopupStack.Size);
   8939 
   8940     if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.CurrentPopupStack.Size)
   8941     {
   8942         // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame.
   8943         OpenPopup(label);
   8944         return false;
   8945     }
   8946 
   8947     menu_is_open |= want_open;
   8948     if (want_open)
   8949         OpenPopup(label);
   8950 
   8951     if (menu_is_open)
   8952     {
   8953         SetNextWindowPos(popup_pos, ImGuiSetCond_Always);
   8954         ImGuiWindowFlags flags = ImGuiWindowFlags_ShowBorders | ((window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu|ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu);
   8955         menu_is_open = BeginPopupEx(label, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
   8956     }
   8957 
   8958     return menu_is_open;
   8959 }
   8960 
   8961 void ImGui::EndMenu()
   8962 {
   8963     EndPopup();
   8964 }
   8965 
   8966 // A little colored square. Return true when clicked.
   8967 // FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip.
   8968 bool ImGui::ColorButton(const ImVec4& col, bool small_height, bool outline_border)
   8969 {
   8970     ImGuiWindow* window = GetCurrentWindow();
   8971     if (window->SkipItems)
   8972         return false;
   8973 
   8974     ImGuiContext& g = *GImGui;
   8975     const ImGuiStyle& style = g.Style;
   8976     const ImGuiID id = window->GetID("#colorbutton");
   8977     const float square_size = g.FontSize;
   8978     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(square_size + style.FramePadding.y*2, square_size + (small_height ? 0 : style.FramePadding.y*2)));
   8979     ItemSize(bb, small_height ? 0.0f : style.FramePadding.y);
   8980     if (!ItemAdd(bb, &id))
   8981         return false;
   8982 
   8983     bool hovered, held;
   8984     bool pressed = ButtonBehavior(bb, id, &hovered, &held);
   8985     RenderFrame(bb.Min, bb.Max, GetColorU32(col), outline_border, style.FrameRounding);
   8986 
   8987     if (hovered)
   8988         SetTooltip("Color:\n(%.2f,%.2f,%.2f,%.2f)\n#%02X%02X%02X%02X", col.x, col.y, col.z, col.w, IM_F32_TO_INT8_SAT(col.x), IM_F32_TO_INT8_SAT(col.y), IM_F32_TO_INT8_SAT(col.z), IM_F32_TO_INT8_SAT(col.w));
   8989 
   8990     return pressed;
   8991 }
   8992 
   8993 bool ImGui::ColorEdit3(const char* label, float col[3])
   8994 {
   8995     float col4[4];
   8996     col4[0] = col[0];
   8997     col4[1] = col[1];
   8998     col4[2] = col[2];
   8999     col4[3] = 1.0f;
   9000     const bool value_changed = ColorEdit4(label, col4, false);
   9001     col[0] = col4[0];
   9002     col[1] = col4[1];
   9003     col[2] = col4[2];
   9004     return value_changed;
   9005 }
   9006 
   9007 // Edit colors components (each component in 0.0f..1.0f range
   9008 // Use CTRL-Click to input value and TAB to go to next item.
   9009 bool ImGui::ColorEdit4(const char* label, float col[4], bool alpha)
   9010 {
   9011     ImGuiWindow* window = GetCurrentWindow();
   9012     if (window->SkipItems)
   9013         return false;
   9014 
   9015     ImGuiContext& g = *GImGui;
   9016     const ImGuiStyle& style = g.Style;
   9017     const ImGuiID id = window->GetID(label);
   9018     const float w_full = CalcItemWidth();
   9019     const float square_sz = (g.FontSize + style.FramePadding.y * 2.0f);
   9020 
   9021     ImGuiColorEditMode edit_mode = window->DC.ColorEditMode;
   9022     if (edit_mode == ImGuiColorEditMode_UserSelect || edit_mode == ImGuiColorEditMode_UserSelectShowButton)
   9023         edit_mode = g.ColorEditModeStorage.GetInt(id, 0) % 3;
   9024 
   9025     float f[4] = { col[0], col[1], col[2], col[3] };
   9026     if (edit_mode == ImGuiColorEditMode_HSV)
   9027         ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]);
   9028 
   9029     int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) };
   9030 
   9031     int components = alpha ? 4 : 3;
   9032     bool value_changed = false;
   9033 
   9034     BeginGroup();
   9035     PushID(label);
   9036 
   9037     const bool hsv = (edit_mode == 1);
   9038     switch (edit_mode)
   9039     {
   9040     case ImGuiColorEditMode_RGB:
   9041     case ImGuiColorEditMode_HSV:
   9042         {
   9043             // RGB/HSV 0..255 Sliders
   9044             const float w_items_all = w_full - (square_sz + style.ItemInnerSpacing.x);
   9045             const float w_item_one  = ImMax(1.0f, (float)(int)((w_items_all - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
   9046             const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
   9047 
   9048             const bool hide_prefix = (w_item_one <= CalcTextSize("M:999").x);
   9049             const char* ids[4] = { "##X", "##Y", "##Z", "##W" };
   9050             const char* fmt_table[3][4] =
   9051             {
   9052                 {   "%3.0f",   "%3.0f",   "%3.0f",   "%3.0f" },
   9053                 { "R:%3.0f", "G:%3.0f", "B:%3.0f", "A:%3.0f" },
   9054                 { "H:%3.0f", "S:%3.0f", "V:%3.0f", "A:%3.0f" }
   9055             };
   9056             const char** fmt = hide_prefix ? fmt_table[0] : hsv ? fmt_table[2] : fmt_table[1];
   9057 
   9058             PushItemWidth(w_item_one);
   9059             for (int n = 0; n < components; n++)
   9060             {
   9061                 if (n > 0)
   9062                     SameLine(0, style.ItemInnerSpacing.x);
   9063                 if (n + 1 == components)
   9064                     PushItemWidth(w_item_last);
   9065                 value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, 255, fmt[n]);
   9066             }
   9067             PopItemWidth();
   9068             PopItemWidth();
   9069         }
   9070         break;
   9071     case ImGuiColorEditMode_HEX:
   9072         {
   9073             // RGB Hexadecimal Input
   9074             const float w_slider_all = w_full - square_sz;
   9075             char buf[64];
   9076             if (alpha)
   9077                 ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", i[0], i[1], i[2], i[3]);
   9078             else
   9079                 ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", i[0], i[1], i[2]);
   9080             PushItemWidth(w_slider_all - style.ItemInnerSpacing.x);
   9081             if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase))
   9082             {
   9083                 value_changed |= true;
   9084                 char* p = buf;
   9085                 while (*p == '#' || ImCharIsSpace(*p))
   9086                     p++;
   9087                 i[0] = i[1] = i[2] = i[3] = 0;
   9088                 if (alpha)
   9089                     sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned)
   9090                 else
   9091                     sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]);
   9092             }
   9093             PopItemWidth();
   9094         }
   9095         break;
   9096     }
   9097 
   9098     SameLine(0, style.ItemInnerSpacing.x);
   9099 
   9100     const ImVec4 col_display(col[0], col[1], col[2], 1.0f);
   9101     if (ColorButton(col_display))
   9102         g.ColorEditModeStorage.SetInt(id, (edit_mode + 1) % 3); // Don't set local copy of 'edit_mode' right away!
   9103 
   9104     // Recreate our own tooltip over's ColorButton() one because we want to display correct alpha here
   9105     if (IsItemHovered())
   9106         SetTooltip("Color:\n(%.2f,%.2f,%.2f,%.2f)\n#%02X%02X%02X%02X", col[0], col[1], col[2], col[3], IM_F32_TO_INT8_SAT(col[0]), IM_F32_TO_INT8_SAT(col[1]), IM_F32_TO_INT8_SAT(col[2]), IM_F32_TO_INT8_SAT(col[3]));
   9107 
   9108     if (window->DC.ColorEditMode == ImGuiColorEditMode_UserSelectShowButton)
   9109     {
   9110         SameLine(0, style.ItemInnerSpacing.x);
   9111         const char* button_titles[3] = { "RGB", "HSV", "HEX" };
   9112         if (ButtonEx(button_titles[edit_mode], ImVec2(0,0), ImGuiButtonFlags_DontClosePopups))
   9113             g.ColorEditModeStorage.SetInt(id, (edit_mode + 1) % 3); // Don't set local copy of 'edit_mode' right away!
   9114     }
   9115 
   9116     const char* label_display_end = FindRenderedTextEnd(label);
   9117     if (label != label_display_end)
   9118     {
   9119         SameLine(0, (window->DC.ColorEditMode == ImGuiColorEditMode_UserSelectShowButton) ? -1.0f : style.ItemInnerSpacing.x);
   9120         TextUnformatted(label, label_display_end);
   9121     }
   9122 
   9123     // Convert back
   9124     for (int n = 0; n < 4; n++)
   9125         f[n] = i[n] / 255.0f;
   9126     if (edit_mode == 1)
   9127         ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]);
   9128 
   9129     if (value_changed)
   9130     {
   9131         col[0] = f[0];
   9132         col[1] = f[1];
   9133         col[2] = f[2];
   9134         if (alpha)
   9135             col[3] = f[3];
   9136     }
   9137 
   9138     PopID();
   9139     EndGroup();
   9140 
   9141     return value_changed;
   9142 }
   9143 
   9144 void ImGui::ColorEditMode(ImGuiColorEditMode mode)
   9145 {
   9146     ImGuiWindow* window = GetCurrentWindow();
   9147     window->DC.ColorEditMode = mode;
   9148 }
   9149 
   9150 // Horizontal separating line.
   9151 void ImGui::Separator()
   9152 {
   9153     ImGuiWindow* window = GetCurrentWindow();
   9154     if (window->SkipItems)
   9155         return;
   9156 
   9157     if (window->DC.ColumnsCount > 1)
   9158         PopClipRect();
   9159 
   9160     float x1 = window->Pos.x;
   9161     float x2 = window->Pos.x + window->Size.x;
   9162     if (!window->DC.GroupStack.empty())
   9163         x1 += window->DC.IndentX;
   9164 
   9165     const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y+1.0f));
   9166     ItemSize(ImVec2(0.0f, 0.0f)); // NB: we don't provide our width so that it doesn't get feed back into AutoFit, we don't provide height to not alter layout.
   9167     if (!ItemAdd(bb, NULL))
   9168     {
   9169         if (window->DC.ColumnsCount > 1)
   9170             PushColumnClipRect();
   9171         return;
   9172     }
   9173 
   9174     window->DrawList->AddLine(bb.Min, ImVec2(bb.Max.x,bb.Min.y), GetColorU32(ImGuiCol_Border));
   9175 
   9176     ImGuiContext& g = *GImGui;
   9177     if (g.LogEnabled)
   9178         LogText(IM_NEWLINE "--------------------------------");
   9179 
   9180     if (window->DC.ColumnsCount > 1)
   9181     {
   9182         PushColumnClipRect();
   9183         window->DC.ColumnsCellMinY = window->DC.CursorPos.y;
   9184     }
   9185 }
   9186 
   9187 void ImGui::Spacing()
   9188 {
   9189     ImGuiWindow* window = GetCurrentWindow();
   9190     if (window->SkipItems)
   9191         return;
   9192     ItemSize(ImVec2(0,0));
   9193 }
   9194 
   9195 void ImGui::Dummy(const ImVec2& size)
   9196 {
   9197     ImGuiWindow* window = GetCurrentWindow();
   9198     if (window->SkipItems)
   9199         return;
   9200 
   9201     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
   9202     ItemSize(bb);
   9203     ItemAdd(bb, NULL);
   9204 }
   9205 
   9206 bool ImGui::IsRectVisible(const ImVec2& size)
   9207 {
   9208     ImGuiWindow* window = GetCurrentWindowRead();
   9209     return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
   9210 }
   9211 
   9212 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
   9213 {
   9214     ImGuiWindow* window = GetCurrentWindowRead();
   9215     return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
   9216 }
   9217 
   9218 // 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.)
   9219 void ImGui::BeginGroup()
   9220 {
   9221     ImGuiWindow* window = GetCurrentWindow();
   9222 
   9223     window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
   9224     ImGuiGroupData& group_data = window->DC.GroupStack.back();
   9225     group_data.BackupCursorPos = window->DC.CursorPos;
   9226     group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
   9227     group_data.BackupIndentX = window->DC.IndentX;
   9228     group_data.BackupGroupOffsetX = window->DC.GroupOffsetX;
   9229     group_data.BackupCurrentLineHeight = window->DC.CurrentLineHeight;
   9230     group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset;
   9231     group_data.BackupLogLinePosY = window->DC.LogLinePosY;
   9232     group_data.BackupActiveIdIsAlive = GImGui->ActiveIdIsAlive;
   9233     group_data.AdvanceCursor = true;
   9234 
   9235     window->DC.GroupOffsetX = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffsetX;
   9236     window->DC.IndentX = window->DC.GroupOffsetX;
   9237     window->DC.CursorMaxPos = window->DC.CursorPos;
   9238     window->DC.CurrentLineHeight = 0.0f;
   9239     window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
   9240 }
   9241 
   9242 void ImGui::EndGroup()
   9243 {
   9244     ImGuiContext& g = *GImGui;
   9245     ImGuiWindow* window = GetCurrentWindow();
   9246 
   9247     IM_ASSERT(!window->DC.GroupStack.empty());	// Mismatched BeginGroup()/EndGroup() calls
   9248 
   9249     ImGuiGroupData& group_data = window->DC.GroupStack.back();
   9250 
   9251     ImRect group_bb(group_data.BackupCursorPos, window->DC.CursorMaxPos);
   9252     group_bb.Max.y -= g.Style.ItemSpacing.y;      // Cancel out last vertical spacing because we are adding one ourselves.
   9253     group_bb.Max = ImMax(group_bb.Min, group_bb.Max);
   9254 
   9255     window->DC.CursorPos = group_data.BackupCursorPos;
   9256     window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
   9257     window->DC.CurrentLineHeight = group_data.BackupCurrentLineHeight;
   9258     window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset;
   9259     window->DC.IndentX = group_data.BackupIndentX;
   9260     window->DC.GroupOffsetX = group_data.BackupGroupOffsetX;
   9261     window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
   9262 
   9263     if (group_data.AdvanceCursor)
   9264     {
   9265         window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrentLineTextBaseOffset);      // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now.
   9266         ItemSize(group_bb.GetSize(), group_data.BackupCurrentLineTextBaseOffset);
   9267         ItemAdd(group_bb, NULL);
   9268     }
   9269 
   9270     // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive() will function on the entire group.
   9271     // It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but if you search for LastItemId you'll notice it is only used in that context.
   9272     const bool active_id_within_group = (!group_data.BackupActiveIdIsAlive && g.ActiveIdIsAlive && g.ActiveId && g.ActiveIdWindow->RootWindow == window->RootWindow);
   9273     if (active_id_within_group)
   9274         window->DC.LastItemId = g.ActiveId;
   9275     if (active_id_within_group && g.HoveredId == g.ActiveId)
   9276         window->DC.LastItemHoveredAndUsable = window->DC.LastItemHoveredRect = true;
   9277 
   9278     window->DC.GroupStack.pop_back();
   9279 
   9280     //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255));   // Debug
   9281 }
   9282 
   9283 // Gets back to previous line and continue with horizontal layout
   9284 //      pos_x == 0      : follow right after previous item
   9285 //      pos_x != 0      : align to specified x position (relative to window/group left)
   9286 //      spacing_w < 0   : use default spacing if pos_x == 0, no spacing if pos_x != 0
   9287 //      spacing_w >= 0  : enforce spacing amount
   9288 void ImGui::SameLine(float pos_x, float spacing_w)
   9289 {
   9290     ImGuiWindow* window = GetCurrentWindow();
   9291     if (window->SkipItems)
   9292         return;
   9293 
   9294     ImGuiContext& g = *GImGui;
   9295     if (pos_x != 0.0f)
   9296     {
   9297         if (spacing_w < 0.0f) spacing_w = 0.0f;
   9298         window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + pos_x + spacing_w + window->DC.GroupOffsetX + window->DC.ColumnsOffsetX;
   9299         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
   9300     }
   9301     else
   9302     {
   9303         if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
   9304         window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
   9305         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
   9306     }
   9307     window->DC.CurrentLineHeight = window->DC.PrevLineHeight;
   9308     window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
   9309 }
   9310 
   9311 void ImGui::NewLine()
   9312 {
   9313     ImGuiWindow* window = GetCurrentWindow();
   9314     if (window->SkipItems)
   9315         return;
   9316     if (window->DC.CurrentLineHeight > 0.0f)     // In the event that we are on a line with items that is smaller that FontSize high, we will preserve its height.
   9317         ItemSize(ImVec2(0,0));
   9318     else
   9319         ItemSize(ImVec2(0.0f, GImGui->FontSize));
   9320 }
   9321 
   9322 void ImGui::NextColumn()
   9323 {
   9324     ImGuiWindow* window = GetCurrentWindow();
   9325     if (window->SkipItems || window->DC.ColumnsCount <= 1)
   9326         return;
   9327 
   9328     ImGuiContext& g = *GImGui;
   9329     PopItemWidth();
   9330     PopClipRect();
   9331 
   9332     window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y);
   9333     if (++window->DC.ColumnsCurrent < window->DC.ColumnsCount)
   9334     {
   9335         // Columns 1+ cancel out IndentX
   9336         window->DC.ColumnsOffsetX = GetColumnOffset(window->DC.ColumnsCurrent) - window->DC.IndentX + g.Style.ItemSpacing.x;
   9337         window->DrawList->ChannelsSetCurrent(window->DC.ColumnsCurrent);
   9338     }
   9339     else
   9340     {
   9341         window->DC.ColumnsCurrent = 0;
   9342         window->DC.ColumnsOffsetX = 0.0f;
   9343         window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY;
   9344         window->DrawList->ChannelsSetCurrent(0);
   9345     }
   9346     window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
   9347     window->DC.CursorPos.y = window->DC.ColumnsCellMinY;
   9348     window->DC.CurrentLineHeight = 0.0f;
   9349     window->DC.CurrentLineTextBaseOffset = 0.0f;
   9350 
   9351     PushColumnClipRect();
   9352     PushItemWidth(GetColumnWidth() * 0.65f);  // FIXME: Move on columns setup
   9353 }
   9354 
   9355 int ImGui::GetColumnIndex()
   9356 {
   9357     ImGuiWindow* window = GetCurrentWindowRead();
   9358     return window->DC.ColumnsCurrent;
   9359 }
   9360 
   9361 int ImGui::GetColumnsCount()
   9362 {
   9363     ImGuiWindow* window = GetCurrentWindowRead();
   9364     return window->DC.ColumnsCount;
   9365 }
   9366 
   9367 static float GetDraggedColumnOffset(int column_index)
   9368 {
   9369     // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing
   9370     // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning.
   9371     ImGuiContext& g = *GImGui;
   9372     ImGuiWindow* window = ImGui::GetCurrentWindowRead();
   9373     IM_ASSERT(column_index > 0); // We cannot drag column 0. If you get this assert you may have a conflict between the ID of your columns and another widgets.
   9374     IM_ASSERT(g.ActiveId == window->DC.ColumnsSetId + ImGuiID(column_index));
   9375 
   9376     float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x - window->Pos.x;
   9377     x = ImClamp(x, ImGui::GetColumnOffset(column_index-1)+g.Style.ColumnsMinSpacing, ImGui::GetColumnOffset(column_index+1)-g.Style.ColumnsMinSpacing);
   9378 
   9379     return (float)(int)x;
   9380 }
   9381 
   9382 float ImGui::GetColumnOffset(int column_index)
   9383 {
   9384     ImGuiContext& g = *GImGui;
   9385     ImGuiWindow* window = GetCurrentWindowRead();
   9386     if (column_index < 0)
   9387         column_index = window->DC.ColumnsCurrent;
   9388 
   9389     if (g.ActiveId)
   9390     {
   9391         const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index);
   9392         if (g.ActiveId == column_id)
   9393             return GetDraggedColumnOffset(column_index);
   9394     }
   9395 
   9396     IM_ASSERT(column_index < window->DC.ColumnsData.Size);
   9397     const float t = window->DC.ColumnsData[column_index].OffsetNorm;
   9398     const float x_offset = window->DC.ColumnsMinX + t * (window->DC.ColumnsMaxX - window->DC.ColumnsMinX);
   9399     return (float)(int)x_offset;
   9400 }
   9401 
   9402 void ImGui::SetColumnOffset(int column_index, float offset)
   9403 {
   9404     ImGuiWindow* window = GetCurrentWindow();
   9405     if (column_index < 0)
   9406         column_index = window->DC.ColumnsCurrent;
   9407 
   9408     IM_ASSERT(column_index < window->DC.ColumnsData.Size);
   9409     const float t = (offset - window->DC.ColumnsMinX) / (window->DC.ColumnsMaxX - window->DC.ColumnsMinX);
   9410     window->DC.ColumnsData[column_index].OffsetNorm = t;
   9411 
   9412     const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index);
   9413     window->DC.StateStorage->SetFloat(column_id, t);
   9414 }
   9415 
   9416 float ImGui::GetColumnWidth(int column_index)
   9417 {
   9418     ImGuiWindow* window = GetCurrentWindowRead();
   9419     if (column_index < 0)
   9420         column_index = window->DC.ColumnsCurrent;
   9421 
   9422     float w = GetColumnOffset(column_index+1) - GetColumnOffset(column_index);
   9423     return w;
   9424 }
   9425 
   9426 static void PushColumnClipRect(int column_index)
   9427 {
   9428     ImGuiWindow* window = ImGui::GetCurrentWindow();
   9429     if (column_index < 0)
   9430         column_index = window->DC.ColumnsCurrent;
   9431 
   9432     float x1 = ImFloor(0.5f + window->Pos.x + ImGui::GetColumnOffset(column_index) - 1.0f);
   9433     float x2 = ImFloor(0.5f + window->Pos.x + ImGui::GetColumnOffset(column_index+1) - 1.0f);
   9434     ImGui::PushClipRect(ImVec2(x1,-FLT_MAX), ImVec2(x2,+FLT_MAX), true);
   9435 }
   9436 
   9437 void ImGui::Columns(int columns_count, const char* id, bool border)
   9438 {
   9439     ImGuiContext& g = *GImGui;
   9440     ImGuiWindow* window = GetCurrentWindow();
   9441     IM_ASSERT(columns_count >= 1);
   9442 
   9443     if (window->DC.ColumnsCount != 1)
   9444     {
   9445         if (window->DC.ColumnsCurrent != 0)
   9446             ItemSize(ImVec2(0,0));   // Advance to column 0
   9447         PopItemWidth();
   9448         PopClipRect();
   9449         window->DrawList->ChannelsMerge();
   9450 
   9451         window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y);
   9452         window->DC.CursorPos.y = window->DC.ColumnsCellMaxY;
   9453     }
   9454 
   9455     // Draw columns borders and handle resize at the time of "closing" a columns set
   9456     if (window->DC.ColumnsCount != columns_count && window->DC.ColumnsCount != 1 && window->DC.ColumnsShowBorders && !window->SkipItems)
   9457     {
   9458         const float y1 = window->DC.ColumnsStartPosY;
   9459         const float y2 = window->DC.CursorPos.y;
   9460         for (int i = 1; i < window->DC.ColumnsCount; i++)
   9461         {
   9462             float x = window->Pos.x + GetColumnOffset(i);
   9463             const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(i);
   9464             const ImRect column_rect(ImVec2(x-4,y1),ImVec2(x+4,y2));
   9465             if (IsClippedEx(column_rect, &column_id, false))
   9466                 continue;
   9467 
   9468             bool hovered, held;
   9469             ButtonBehavior(column_rect, column_id, &hovered, &held);
   9470             if (hovered || held)
   9471                 g.MouseCursor = ImGuiMouseCursor_ResizeEW;
   9472 
   9473             // Draw before resize so our items positioning are in sync with the line being drawn
   9474             const ImU32 col = GetColorU32(held ? ImGuiCol_ColumnActive : hovered ? ImGuiCol_ColumnHovered : ImGuiCol_Column);
   9475             const float xi = (float)(int)x;
   9476             window->DrawList->AddLine(ImVec2(xi, y1+1.0f), ImVec2(xi, y2), col);
   9477 
   9478             if (held)
   9479             {
   9480                 if (g.ActiveIdIsJustActivated)
   9481                     g.ActiveIdClickOffset.x -= 4;   // Store from center of column line (we used a 8 wide rect for columns clicking)
   9482                 x = GetDraggedColumnOffset(i);
   9483                 SetColumnOffset(i, x);
   9484             }
   9485         }
   9486     }
   9487 
   9488     // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget.
   9489     // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer.
   9490     PushID(0x11223347 + (id ? 0 : columns_count));
   9491     window->DC.ColumnsSetId = window->GetID(id ? id : "columns");
   9492     PopID();
   9493 
   9494     // Set state for first column
   9495     window->DC.ColumnsCurrent = 0;
   9496     window->DC.ColumnsCount = columns_count;
   9497     window->DC.ColumnsShowBorders = border;
   9498 
   9499     const float content_region_width = (window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : window->Size.x;
   9500     window->DC.ColumnsMinX = window->DC.IndentX; // Lock our horizontal range
   9501     window->DC.ColumnsMaxX = content_region_width - window->Scroll.x - ((window->Flags & ImGuiWindowFlags_NoScrollbar) ? 0 : g.Style.ScrollbarSize);// - window->WindowPadding().x;
   9502     window->DC.ColumnsStartPosY = window->DC.CursorPos.y;
   9503     window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY = window->DC.CursorPos.y;
   9504     window->DC.ColumnsOffsetX = 0.0f;
   9505     window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
   9506 
   9507     if (window->DC.ColumnsCount != 1)
   9508     {
   9509         // Cache column offsets
   9510         window->DC.ColumnsData.resize(columns_count + 1);
   9511         for (int column_index = 0; column_index < columns_count + 1; column_index++)
   9512         {
   9513             const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index);
   9514             KeepAliveID(column_id);
   9515             const float default_t = column_index / (float)window->DC.ColumnsCount;
   9516             const float t = window->DC.StateStorage->GetFloat(column_id, default_t);      // Cheaply store our floating point value inside the integer (could store a union into the map?)
   9517             window->DC.ColumnsData[column_index].OffsetNorm = t;
   9518         }
   9519         window->DrawList->ChannelsSplit(window->DC.ColumnsCount);
   9520         PushColumnClipRect();
   9521         PushItemWidth(GetColumnWidth() * 0.65f);
   9522     }
   9523     else
   9524     {
   9525         window->DC.ColumnsData.resize(0);
   9526     }
   9527 }
   9528 
   9529 void ImGui::Indent(float indent_w)
   9530 {
   9531     ImGuiContext& g = *GImGui;
   9532     ImGuiWindow* window = GetCurrentWindow();
   9533     window->DC.IndentX += (indent_w > 0.0f) ? indent_w : g.Style.IndentSpacing;
   9534     window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX;
   9535 }
   9536 
   9537 void ImGui::Unindent(float indent_w)
   9538 {
   9539     ImGuiContext& g = *GImGui;
   9540     ImGuiWindow* window = GetCurrentWindow();
   9541     window->DC.IndentX -= (indent_w > 0.0f) ? indent_w : g.Style.IndentSpacing;
   9542     window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX;
   9543 }
   9544 
   9545 void ImGui::TreePush(const char* str_id)
   9546 {
   9547     ImGuiWindow* window = GetCurrentWindow();
   9548     Indent();
   9549     window->DC.TreeDepth++;
   9550     PushID(str_id ? str_id : "#TreePush");
   9551 }
   9552 
   9553 void ImGui::TreePush(const void* ptr_id)
   9554 {
   9555     ImGuiWindow* window = GetCurrentWindow();
   9556     Indent();
   9557     window->DC.TreeDepth++;
   9558     PushID(ptr_id ? ptr_id : (const void*)"#TreePush");
   9559 }
   9560 
   9561 void ImGui::TreePushRawID(ImGuiID id)
   9562 {
   9563     ImGuiWindow* window = GetCurrentWindow();
   9564     Indent();
   9565     window->DC.TreeDepth++;
   9566     window->IDStack.push_back(id);
   9567 }
   9568 
   9569 void ImGui::TreePop()
   9570 {
   9571     ImGuiWindow* window = GetCurrentWindow();
   9572     Unindent();
   9573     window->DC.TreeDepth--;
   9574     PopID();
   9575 }
   9576 
   9577 void ImGui::Value(const char* prefix, bool b)
   9578 {
   9579     Text("%s: %s", prefix, (b ? "true" : "false"));
   9580 }
   9581 
   9582 void ImGui::Value(const char* prefix, int v)
   9583 {
   9584     Text("%s: %d", prefix, v);
   9585 }
   9586 
   9587 void ImGui::Value(const char* prefix, unsigned int v)
   9588 {
   9589     Text("%s: %d", prefix, v);
   9590 }
   9591 
   9592 void ImGui::Value(const char* prefix, float v, const char* float_format)
   9593 {
   9594     if (float_format)
   9595     {
   9596         char fmt[64];
   9597         ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format);
   9598         Text(fmt, prefix, v);
   9599     }
   9600     else
   9601     {
   9602         Text("%s: %.3f", prefix, v);
   9603     }
   9604 }
   9605 
   9606 // FIXME: May want to remove those helpers?
   9607 void ImGui::ValueColor(const char* prefix, const ImVec4& v)
   9608 {
   9609     Text("%s: (%.2f,%.2f,%.2f,%.2f)", prefix, v.x, v.y, v.z, v.w);
   9610     SameLine();
   9611     ColorButton(v, true);
   9612 }
   9613 
   9614 void ImGui::ValueColor(const char* prefix, ImU32 v)
   9615 {
   9616     Text("%s: %08X", prefix, v);
   9617     SameLine();
   9618     ColorButton(ColorConvertU32ToFloat4(v), true);
   9619 }
   9620 
   9621 //-----------------------------------------------------------------------------
   9622 // PLATFORM DEPENDENT HELPERS
   9623 //-----------------------------------------------------------------------------
   9624 
   9625 #if defined(_WIN32) && !defined(_WINDOWS_) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS))
   9626 #undef WIN32_LEAN_AND_MEAN
   9627 #define WIN32_LEAN_AND_MEAN
   9628 #include <windows.h>
   9629 #endif
   9630 
   9631 // Win32 API clipboard implementation
   9632 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS)
   9633 
   9634 #ifdef _MSC_VER
   9635 #pragma comment(lib, "user32")
   9636 #endif
   9637 
   9638 static const char* GetClipboardTextFn_DefaultImpl(void*)
   9639 {
   9640     static ImVector<char> buf_local;
   9641     buf_local.clear();
   9642     if (!OpenClipboard(NULL))
   9643         return NULL;
   9644     HANDLE wbuf_handle = GetClipboardData(CF_UNICODETEXT);
   9645     if (wbuf_handle == NULL)
   9646         return NULL;
   9647     if (ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle))
   9648     {
   9649         int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
   9650         buf_local.resize(buf_len);
   9651         ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL);
   9652     }
   9653     GlobalUnlock(wbuf_handle);
   9654     CloseClipboard();
   9655     return buf_local.Data;
   9656 }
   9657 
   9658 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
   9659 {
   9660     if (!OpenClipboard(NULL))
   9661         return;
   9662     const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
   9663     HGLOBAL wbuf_handle = GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
   9664     if (wbuf_handle == NULL)
   9665         return;
   9666     ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle);
   9667     ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
   9668     GlobalUnlock(wbuf_handle);
   9669     EmptyClipboard();
   9670     SetClipboardData(CF_UNICODETEXT, wbuf_handle);
   9671     CloseClipboard();
   9672 }
   9673 
   9674 #else
   9675 
   9676 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
   9677 static const char* GetClipboardTextFn_DefaultImpl(void*)
   9678 {
   9679     return GImGui->PrivateClipboard;
   9680 }
   9681 
   9682 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
   9683 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
   9684 {
   9685     ImGuiContext& g = *GImGui;
   9686     if (g.PrivateClipboard)
   9687     {
   9688         ImGui::MemFree(g.PrivateClipboard);
   9689         g.PrivateClipboard = NULL;
   9690     }
   9691     const char* text_end = text + strlen(text);
   9692     g.PrivateClipboard = (char*)ImGui::MemAlloc((size_t)(text_end - text) + 1);
   9693     memcpy(g.PrivateClipboard, text, (size_t)(text_end - text));
   9694     g.PrivateClipboard[(int)(text_end - text)] = 0;
   9695 }
   9696 
   9697 #endif
   9698 
   9699 // Win32 API IME support (for Asian languages, etc.)
   9700 #if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS)
   9701 
   9702 #include <imm.h>
   9703 #ifdef _MSC_VER
   9704 #pragma comment(lib, "imm32")
   9705 #endif
   9706 
   9707 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
   9708 {
   9709     // Notify OS Input Method Editor of text input position
   9710     if (HWND hwnd = (HWND)GImGui->IO.ImeWindowHandle)
   9711         if (HIMC himc = ImmGetContext(hwnd))
   9712         {
   9713             COMPOSITIONFORM cf;
   9714             cf.ptCurrentPos.x = x;
   9715             cf.ptCurrentPos.y = y;
   9716             cf.dwStyle = CFS_FORCE_POSITION;
   9717             ImmSetCompositionWindow(himc, &cf);
   9718         }
   9719 }
   9720 
   9721 #else
   9722 
   9723 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
   9724 
   9725 #endif
   9726 
   9727 //-----------------------------------------------------------------------------
   9728 // HELP
   9729 //-----------------------------------------------------------------------------
   9730 
   9731 void ImGui::ShowMetricsWindow(bool* p_open)
   9732 {
   9733     if (ImGui::Begin("ImGui Metrics", p_open))
   9734     {
   9735         ImGui::Text("ImGui %s", ImGui::GetVersion());
   9736         ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
   9737         ImGui::Text("%d vertices, %d indices (%d triangles)", ImGui::GetIO().MetricsRenderVertices, ImGui::GetIO().MetricsRenderIndices, ImGui::GetIO().MetricsRenderIndices / 3);
   9738         ImGui::Text("%d allocations", ImGui::GetIO().MetricsAllocs);
   9739         static bool show_clip_rects = true;
   9740         ImGui::Checkbox("Show clipping rectangles when hovering a ImDrawCmd", &show_clip_rects);
   9741         ImGui::Separator();
   9742 
   9743         struct Funcs
   9744         {
   9745             static void NodeDrawList(ImDrawList* draw_list, const char* label)
   9746             {
   9747                 bool node_open = ImGui::TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, draw_list->CmdBuffer.Size);
   9748                 if (draw_list == ImGui::GetWindowDrawList())
   9749                 {
   9750                     ImGui::SameLine();
   9751                     ImGui::TextColored(ImColor(255,100,100), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
   9752                     if (node_open) ImGui::TreePop();
   9753                     return;
   9754                 }
   9755                 if (!node_open)
   9756                     return;
   9757 
   9758                 ImDrawList* overlay_draw_list = &GImGui->OverlayDrawList;   // Render additional visuals into the top-most draw list
   9759                 overlay_draw_list->PushClipRectFullScreen();
   9760                 int elem_offset = 0;
   9761                 for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
   9762                 {
   9763                     if (pcmd->UserCallback)
   9764                     {
   9765                         ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
   9766                         continue;
   9767                     }
   9768                     ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
   9769                     bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "Draw %-4d %s vtx, tex = %p, clip_rect = (%.0f,%.0f)..(%.0f,%.0f)", pcmd->ElemCount, draw_list->IdxBuffer.Size > 0 ? "indexed" : "non-indexed", pcmd->TextureId, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
   9770                     if (show_clip_rects && ImGui::IsItemHovered())
   9771                     {
   9772                         ImRect clip_rect = pcmd->ClipRect;
   9773                         ImRect vtxs_rect;
   9774                         for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++)
   9775                             vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos);
   9776                         clip_rect.Floor(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,255,0,255));
   9777                         vtxs_rect.Floor(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,0,255,255));
   9778                     }
   9779                     if (!pcmd_node_open)
   9780                         continue;
   9781                     ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
   9782                     while (clipper.Step())
   9783                         for (int prim = clipper.DisplayStart, vtx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++)
   9784                         {
   9785                             char buf[300], *buf_p = buf;
   9786                             ImVec2 triangles_pos[3];
   9787                             for (int n = 0; n < 3; n++, vtx_i++)
   9788                             {
   9789                                 ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[vtx_i] : vtx_i];
   9790                                 triangles_pos[n] = v.pos;
   9791                                 buf_p += sprintf(buf_p, "%s %04d { pos = (%8.2f,%8.2f), uv = (%.6f,%.6f), col = %08X }\n", (n == 0) ? "vtx" : "   ", vtx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
   9792                             }
   9793                             ImGui::Selectable(buf, false);
   9794                             if (ImGui::IsItemHovered())
   9795                                 overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f, false);  // Add triangle without AA, more readable for large-thin triangle
   9796                         }
   9797                     ImGui::TreePop();
   9798                 }
   9799                 overlay_draw_list->PopClipRect();
   9800                 ImGui::TreePop();
   9801             }
   9802 
   9803             static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label)
   9804             {
   9805                 if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size))
   9806                     return;
   9807                 for (int i = 0; i < windows.Size; i++)
   9808                     Funcs::NodeWindow(windows[i], "Window");
   9809                 ImGui::TreePop();
   9810             }
   9811 
   9812             static void NodeWindow(ImGuiWindow* window, const char* label)
   9813             {
   9814                 if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window))
   9815                     return;
   9816                 NodeDrawList(window->DrawList, "DrawList");
   9817                 ImGui::BulletText("Pos: (%.1f,%.1f)", window->Pos.x, window->Pos.y);
   9818                 ImGui::BulletText("Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y);
   9819                 ImGui::BulletText("Scroll: (%.2f,%.2f)", window->Scroll.x, window->Scroll.y);
   9820                 if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
   9821                 if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
   9822                 ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair));
   9823                 ImGui::TreePop();
   9824             }
   9825         };
   9826 
   9827         ImGuiContext& g = *GImGui;                // Access private state
   9828         Funcs::NodeWindows(g.Windows, "Windows");
   9829         if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.RenderDrawLists[0].Size))
   9830         {
   9831             for (int i = 0; i < g.RenderDrawLists[0].Size; i++)
   9832                 Funcs::NodeDrawList(g.RenderDrawLists[0][i], "DrawList");
   9833             ImGui::TreePop();
   9834         }
   9835         if (ImGui::TreeNode("Popups", "Open Popups Stack (%d)", g.OpenPopupStack.Size))
   9836         {
   9837             for (int i = 0; i < g.OpenPopupStack.Size; i++)
   9838             {
   9839                 ImGuiWindow* window = g.OpenPopupStack[i].Window;
   9840                 ImGui::BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenPopupStack[i].PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : "");
   9841             }
   9842             ImGui::TreePop();
   9843         }
   9844         if (ImGui::TreeNode("Basic state"))
   9845         {
   9846             ImGui::Text("FocusedWindow: '%s'", g.FocusedWindow ? g.FocusedWindow->Name : "NULL");
   9847             ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
   9848             ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
   9849             ImGui::Text("HoveredID: 0x%08X/0x%08X", g.HoveredId, g.HoveredIdPreviousFrame); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not
   9850             ImGui::Text("ActiveID: 0x%08X/0x%08X", g.ActiveId, g.ActiveIdPreviousFrame);
   9851             ImGui::TreePop();
   9852         }
   9853     }
   9854     ImGui::End();
   9855 }
   9856 
   9857 //-----------------------------------------------------------------------------
   9858 
   9859 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
   9860 // 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.
   9861 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
   9862 #include "imgui_user.inl"
   9863 #endif
   9864 
   9865 //-----------------------------------------------------------------------------