new render target class while working on the exhibit UI
[laserbrain_demo] / src / imgui / imgui.cc
1 // dear imgui, v1.53 WIP
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 Dear ImGui in your codebase.
6 // Get latest version at https://github.com/ocornut/imgui
7 // Releases change-log at https://github.com/ocornut/imgui/releases
8 // Gallery (please post your screenshots/video there!): https://github.com/ocornut/imgui/issues/1269
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    - Read first
20    - How to update to a newer version of Dear ImGui
21    - Getting started with integrating Dear ImGui in your code/engine
22  - API BREAKING CHANGES (read me when you update!)
23  - ISSUES & TODO LIST
24  - FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
25    - How can I help?
26    - What is ImTextureID and how do I display an image?
27    - I integrated Dear ImGui in my engine and the text or lines are blurry..
28    - I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
29    - How can I have multiple widgets with the same label? Can I have widget without a label? (Yes). A primer on labels/IDs.
30    - How can I tell when Dear ImGui wants my mouse/keyboard inputs VS when I can pass them to my application?
31    - How can I load a different font than the default?
32    - How can I easily use icons in my application?
33    - How can I load multiple fonts?
34    - How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic?
35    - How can I preserve my Dear ImGui context across reloading a DLL? (loss of the global/static variables)
36    - How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
37  - ISSUES & TODO-LIST
38  - CODE
39
40
41  MISSION STATEMENT
42  =================
43
44  - Easy to use to create code-driven and data-driven tools
45  - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools
46  - Easy to hack and improve
47  - Minimize screen real-estate usage
48  - Minimize setup and maintenance
49  - Minimize state storage on user side
50  - Portable, minimize dependencies, run on target (consoles, phones, etc.)
51  - Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window, opening a tree node 
52    for the first time, etc. but a typical frame won't allocate anything)
53
54  Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
55  - Doesn't look fancy, doesn't animate
56  - Limited layout features, intricate layouts are typically crafted in code
57
58
59  END-USER GUIDE
60  ==============
61
62  - Double-click on title bar to collapse window.
63  - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin().
64  - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents).
65  - Click and drag on any empty space to move window.
66  - TAB/SHIFT+TAB to cycle through keyboard editable fields.
67  - CTRL+Click on a slider or drag box to input value as text.
68  - Use mouse wheel to scroll.
69  - Text editor:
70    - Hold SHIFT or use mouse to select text.
71    - CTRL+Left/Right to word jump.
72    - CTRL+Shift+Left/Right to select words.
73    - CTRL+A our Double-Click to select all.
74    - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
75    - CTRL+Z,CTRL+Y to undo/redo.
76    - ESCAPE to revert text to its original value.
77    - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
78    - Controls are automatically adjusted for OSX to match standard OSX text editing operations.
79
80
81  PROGRAMMER GUIDE
82  ================
83
84  READ FIRST
85
86  - Read the FAQ below this section!
87  - 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
88    on your side, no state duplication, less sync, less bugs.
89  - Call and read ImGui::ShowTestWindow() for demo code demonstrating most features.
90  - You can learn about immediate-mode gui principles at http://www.johno.se/book/imgui.html or watch http://mollyrocket.com/861
91
92  HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
93
94  - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
95  - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes. 
96    If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed from the public API.
97    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. 
98    Please report any issue to the GitHub page!
99  - Try to keep your copy of dear imgui reasonably up to date.
100
101  GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
102
103  - Add the Dear ImGui source files to your projects, using your preferred build system. 
104    It is recommended you build the .cpp files as part of your project and not as a library.
105  - You can later customize the imconfig.h file to tweak some compilation time behavior, such as integrating imgui types with your own maths types.
106  - See examples/ folder for standalone sample applications.
107  - You may be able to grab and copy a ready made imgui_impl_*** file from the examples/.
108  - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
109
110  - Init: retrieve the ImGuiIO structure with ImGui::GetIO() and fill the fields marked 'Settings': at minimum you need to set io.DisplaySize
111    (application resolution). Later on you will fill your keyboard mapping, clipboard handlers, and other advanced features but for a basic 
112    integration you don't need to worry about it all.
113  - Init: call io.Fonts->GetTexDataAsRGBA32(...), it will build the font atlas texture, then load the texture pixels into graphics memory.
114  - Every frame:
115     - In your main loop as early a possible, fill the IO fields marked 'Input' (e.g. mouse position, buttons, keyboard info, etc.)
116     - Call ImGui::NewFrame() to begin the frame
117     - You can use any ImGui function you want between NewFrame() and Render()
118     - Call ImGui::Render() as late as you can to end the frame and finalize render data. it will call your io.RenderDrawListFn handler.
119        (Even if you don't render, call Render() and ignore the callback, or call EndFrame() instead. Otherwhise some features will break)
120  - All rendering information are stored into command-lists until ImGui::Render() is called.
121  - Dear ImGui never touches or knows about your GPU state. the only function that knows about GPU is the RenderDrawListFn handler that you provide.
122  - Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" phases 
123    of your own application.
124  - Refer to the examples applications in the examples/ folder for instruction on how to setup your code.
125  - A minimal application skeleton may be:
126
127      // Application init
128      ImGuiIO& io = ImGui::GetIO();
129      io.DisplaySize.x = 1920.0f;
130      io.DisplaySize.y = 1280.0f;
131      io.RenderDrawListsFn = MyRenderFunction;  // Setup a render function, or set to NULL and call GetDrawData() after Render() to access render data.
132      // TODO: Fill others settings of the io structure later.
133
134      // Load texture atlas (there is a default font so you don't need to care about choosing a font yet)
135      unsigned char* pixels;
136      int width, height;
137      io.Fonts->GetTexDataAsRGBA32(pixels, &width, &height);
138      // TODO: At this points you've got the texture data and you need to upload that your your graphic system:
139      MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA)
140      // TODO: Store your texture pointer/identifier (whatever your engine uses) in 'io.Fonts->TexID'. This will be passed back to your via the renderer.
141      io.Fonts->TexID = (void*)texture;
142
143      // Application main loop
144      while (true)
145      {
146         // Setup low-level inputs (e.g. on Win32, GetKeyboardState(), or write to those fields from your Windows message loop handlers, etc.)
147         ImGuiIO& io = ImGui::GetIO();
148         io.DeltaTime = 1.0f/60.0f;
149         io.MousePos = mouse_pos;
150         io.MouseDown[0] = mouse_button_0;
151         io.MouseDown[1] = mouse_button_1;
152
153         // Call NewFrame(), after this point you can use ImGui::* functions anytime
154         ImGui::NewFrame();
155
156         // Most of your application code here
157         MyGameUpdate(); // may use any ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
158         MyGameRender(); // may use any ImGui functions as well!
159      
160         // Render & swap video buffers
161         ImGui::Render();
162         SwapBuffers();
163      }
164
165  - A minimal render function skeleton may be:
166
167     void void MyRenderFunction(ImDrawData* draw_data)(ImDrawData* draw_data)
168     {
169        // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
170        // TODO: Setup viewport, orthographic projection matrix
171        // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
172        for (int n = 0; n < draw_data->CmdListsCount; n++)
173        {
174           const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;  // vertex buffer generated by ImGui
175           const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;   // index buffer generated by ImGui
176           for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
177           {
178              const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
179              if (pcmd->UserCallback)
180              {
181                  pcmd->UserCallback(cmd_list, pcmd);
182              }
183              else
184              {
185                  // The texture for the draw call is specified by pcmd->TextureId. 
186                  // The vast majority of draw calls with use the imgui texture atlas, which value you have set yourself during initialization. 
187                  MyEngineBindTexture(pcmd->TextureId);
188
189                  // We are using scissoring to clip some objects. All low-level graphics API supports it.
190                  // If your engine doesn't support scissoring yet, you will get some small glitches (some elements outside their bounds) which you can fix later.
191                  MyEngineScissor((int)pcmd->ClipRect.x, (int)pcmd->ClipRect.y, (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y));
192
193                  // Render 'pcmd->ElemCount/3' indexed triangles.
194                  // By default the indices ImDrawIdx are 16-bits, you can change them to 32-bits if your engine doesn't support 16-bits indices.
195                  MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
196              }
197              idx_buffer += pcmd->ElemCount;
198           }
199        }
200     }
201
202  - The examples/ folders contains many functional implementation of the pseudo-code above.
203  - When calling NewFrame(), the 'io.WantCaptureMouse'/'io.WantCaptureKeyboard'/'io.WantTextInput' flags are updated. 
204    They tell you if ImGui intends to use your inputs. So for example, if 'io.WantCaptureMouse' is set you would typically want to hide 
205    mouse inputs from the rest of your application. Read the FAQ below for more information about those flags.
206
207
208
209  API BREAKING CHANGES
210  ====================
211
212  Occasionally introducing changes that are breaking the API. The breakage are generally minor and easy to fix.
213  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.
214  Also read releases logs https://github.com/ocornut/imgui/releases for more details.
215
216  - 2017/11/18 (1.53) - Style, Begin: removed ImGuiWindowFlags_ShowBorders window flag. Borders are now fully set up in the ImGuiStyle structure (see e.g. style.FrameBorderSize, style.WindowBorderSize). Use ImGui::ShowStyleEditor() to look them up.
217                        Please note that the style system will keep evolving (hopefully stabilizing in Q1 2018), and so custom styles will probably subtly break over time. It is recommended you use the StyleColorsClassic(), StyleColorsDark(), StyleColorsLight() functions.
218  - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
219  - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
220  - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
221  - 2017/11/02 (1.53) - marked IsRootWindowOrAnyChildHovered() as obsolete is favor of using IsWindowHovered(ImGuiHoveredFlags_FlattenChilds);
222  - 2017/10/24 (1.52) - renamed IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS to IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS for consistency.
223  - 2017/10/20 (1.52) - changed IsWindowHovered() default parameters behavior to return false if an item is active in another window (e.g. click-dragging item from another window to this window). You can use the newly introduced IsWindowHovered() flags to requests this specific behavior if you need it.
224  - 2017/10/20 (1.52) - marked IsItemHoveredRect()/IsMouseHoveringWindow() as obsolete, in favor of using the newly introduced flags for IsItemHovered() and IsWindowHovered(). See https://github.com/ocornut/imgui/issues/1382 for details.
225                        removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
226  - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
227  - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
228  - 2017/09/25 (1.52) - removed SetNextWindowPosCenter() because SetNextWindowPos() now has the optional pivot information to do the same and more. Kept redirection function (will obsolete). 
229  - 2017/08/25 (1.52) - io.MousePos needs to be set to ImVec2(-FLT_MAX,-FLT_MAX) when mouse is unavailable/missing. Previously ImVec2(-1,-1) was enough but we now accept negative mouse coordinates. In your binding if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)".
230  - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)! 
231                      - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
232                      - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
233  - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
234  - 2017/08/20 (1.51) - added PushStyleColor(ImGuiCol idx, ImU32 col) overload, which _might_ cause an "ambiguous call" compilation error if you are using ImColor() with implicit cast. Cast to ImU32 or ImVec4 explicily to fix.
235  - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame.
236  - 2017/08/15 (1.51) - changed parameter order for BeginPopupContextWindow() from (const char*,int buttons,bool also_over_items) to (const char*,int buttons,bool also_over_items). Note that most calls relied on default parameters completely.
237  - 2017/08/13 (1.51) - renamed ImGuiCol_Columns*** to ImGuiCol_Separator***. Kept redirection enums (will obsolete).
238  - 2017/08/11 (1.51) - renamed ImGuiSetCond_*** types and flags to ImGuiCond_***. Kept redirection enums (will obsolete).
239  - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
240  - 2017/08/08 (1.51) - removed ColorEditMode() and ImGuiColorEditMode in favor of ImGuiColorEditFlags and parameters to the various Color*() functions. The SetColorEditOptions() allows to initialize default but the user can still change them with right-click context menu.
241                      - changed prototype of 'ColorEdit4(const char* label, float col[4], bool show_alpha = true)' to 'ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0)', where passing flags = 0x01 is a safe no-op (hello dodgy backward compatibility!). - check and run the demo window, under "Color/Picker Widgets", to understand the various new options.
242                      - changed prototype of rarely used 'ColorButton(ImVec4 col, bool small_height = false, bool outline_border = true)' to 'ColorButton(const char* desc_id, ImVec4 col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0,0))'
243  - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
244  - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
245  - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
246  - 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().
247  - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
248  - 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.
249  - 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.
250  - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore. 
251                        If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you. 
252                        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.
253                        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.
254                            ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col)
255                            {
256                                float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a;
257                                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);
258                            }
259                        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.
260  - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
261  - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
262  - 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).
263  - 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.
264  - 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).
265  - 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)
266  - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
267  - 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.
268  - 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.
269  - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
270  - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
271  - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
272                        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.
273                        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!
274  - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
275  - 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.
276  - 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
277  - 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.
278                        you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
279  - 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.
280                        this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
281                      - 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.
282                      - the signature of the io.RenderDrawListsFn handler has changed!
283                             ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
284                        became:
285                             ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
286                               argument   'cmd_lists'        -> 'draw_data->CmdLists'
287                               argument   'cmd_lists_count'  -> 'draw_data->CmdListsCount'
288                               ImDrawList 'commands'         -> 'CmdBuffer'
289                               ImDrawList 'vtx_buffer'       -> 'VtxBuffer'
290                               ImDrawList  n/a               -> 'IdxBuffer' (new)
291                               ImDrawCmd  'vtx_count'        -> 'ElemCount'
292                               ImDrawCmd  'clip_rect'        -> 'ClipRect'
293                               ImDrawCmd  'user_callback'    -> 'UserCallback'
294                               ImDrawCmd  'texture_id'       -> 'TextureId'
295                      - 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.
296                      - 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!
297                      - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
298  - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
299  - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
300  - 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.
301  - 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
302  - 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!
303  - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
304  - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
305  - 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.
306  - 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.
307  - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
308  - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
309  - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
310  - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
311  - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
312  - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
313  - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
314  - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
315  - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
316  - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
317  - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
318  - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
319  - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
320  - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
321  - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
322  - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
323  - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
324               (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
325                        this sequence:
326                            const void* png_data;
327                            unsigned int png_size;
328                            ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size);
329                            // <Copy to GPU>
330                        became:
331                            unsigned char* pixels;
332                            int width, height;
333                            io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
334                            // <Copy to GPU>
335                            io.Fonts->TexID = (your_texture_identifier);
336                        you now have much more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs.
337                        it is now recommended that you sample the font texture with bilinear interpolation.
338               (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
339               (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
340               (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
341  - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
342  - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
343  - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
344  - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
345  - 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)
346  - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
347  - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
348  - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
349  - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
350  - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
351  - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
352
353
354  ISSUES & TODO-LIST
355  ==================
356  See TODO.txt
357
358
359  FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
360  ======================================
361
362  Q: How can I help?
363  A: - If you are experienced enough with Dear ImGui and with C/C++, look at the todo list and see how you want/can help!
364     - Become a Patron/donate! Convince your company to become a Patron or provide serious funding for development time! See http://www.patreon.com/imgui
365
366  Q: What is ImTextureID and how do I display an image?
367  A: ImTextureID is a void* used to pass renderer-agnostic texture references around until it hits your render function.
368     Dear 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!
369     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.
370     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.
371     Refer to examples applications, where each renderer (in a imgui_impl_xxxx.cpp file) is treating ImTextureID as a different thing.
372     (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!)
373     To display a custom image/texture within an ImGui window, you may use ImGui::Image(), ImGui::ImageButton(), ImDrawList::AddImage() functions.
374     Dear ImGui will generate the geometry and draw calls using the ImTextureID that you passed and which your renderer can use.
375     It is your responsibility to get textures uploaded to your GPU.
376
377  Q: I integrated Dear ImGui in my engine and the text or lines are blurry..
378  A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f).
379     Also make sure your orthographic projection matrix and io.DisplaySize matches your actual framebuffer dimension.
380
381  Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
382  A: You are probably mishandling the clipping rectangles in your render function. 
383     Rectangles provided by ImGui are defined as (x1=left,y1=top,x2=right,y2=bottom) and NOT as (x1,y1,width,height).
384
385  Q: Can I have multiple widgets with the same label? Can I have widget without a label?
386  A: Yes. A primer on the use of labels/IDs in Dear ImGui..
387
388    - Elements that are not clickable, such as Text() items don't need an ID.
389
390    - Interactive widgets require state to be carried over multiple frames (most typically Dear ImGui often needs to remember what is 
391      the "active" widget). to do so they need a unique ID. unique ID are typically derived from a string label, an integer index or a pointer.
392
393        Button("OK");        // Label = "OK",     ID = hash of "OK"
394        Button("Cancel");    // Label = "Cancel", ID = hash of "Cancel"
395
396    - ID are uniquely scoped within windows, tree nodes, etc. so no conflict can happen if you have two buttons called "OK"
397      in two different windows or in two different locations of a tree.
398
399    - If you have a same ID twice in the same location, you'll have a conflict:
400
401        Button("OK");
402        Button("OK");           // ID collision! Both buttons will be treated as the same.
403
404      Fear not! this is easy to solve and there are many ways to solve it!
405
406    - When passing a label you can optionally specify extra unique ID information within string itself. 
407      This helps solving the simpler collision cases. Use "##" to pass a complement to the ID that won't be visible to the end-user:
408
409        Button("Play");         // Label = "Play",   ID = hash of "Play"
410        Button("Play##foo1");   // Label = "Play",   ID = hash of "Play##foo1" (different from above)
411        Button("Play##foo2");   // Label = "Play",   ID = hash of "Play##foo2" (different from above)
412
413    - If you want to completely hide the label, but still need an ID:
414
415        Checkbox("##On", &b);   // Label = "",       ID = hash of "##On" (no label!)
416
417    - Occasionally/rarely you might want change a label while preserving a constant ID. This allows you to animate labels.
418      For example you may want to include varying information in a window title bar (and windows are uniquely identified by their ID.. obviously)
419      Use "###" to pass a label that isn't part of ID:
420
421        Button("Hello###ID";   // Label = "Hello",  ID = hash of "ID"
422        Button("World###ID";   // Label = "World",  ID = hash of "ID" (same as above)
423
424        sprintf(buf, "My game (%f FPS)###MyGame");
425        Begin(buf);            // Variable label,   ID = hash of "MyGame"
426
427    - Use PushID() / PopID() to create scopes and avoid ID conflicts within the same Window.
428      This is the most convenient way of distinguishing ID if you are iterating and creating many UI elements.
429      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!
430
431        for (int i = 0; i < 100; i++)
432        {
433          PushID(i);
434          Button("Click");   // Label = "Click",  ID = hash of integer + "label" (unique)
435          PopID();
436        }
437
438        for (int i = 0; i < 100; i++)
439        {
440          MyObject* obj = Objects[i];
441          PushID(obj);
442          Button("Click");   // Label = "Click",  ID = hash of pointer + "label" (unique)
443          PopID();
444        }
445
446        for (int i = 0; i < 100; i++)
447        {
448          MyObject* obj = Objects[i];
449          PushID(obj->Name);
450          Button("Click");   // Label = "Click",  ID = hash of string + "label" (unique)
451          PopID();
452        }
453
454    - More example showing that you can stack multiple prefixes into the ID stack:
455
456        Button("Click");     // Label = "Click",  ID = hash of "Click"
457        PushID("node");
458        Button("Click");     // Label = "Click",  ID = hash of "node" + "Click"
459          PushID(my_ptr);
460            Button("Click"); // Label = "Click",  ID = hash of "node" + ptr + "Click"
461          PopID();
462        PopID();
463
464    - Tree nodes implicitly creates a scope for you by calling PushID().
465
466        Button("Click");     // Label = "Click",  ID = hash of "Click"
467        if (TreeNode("node"))
468        {
469          Button("Click");   // Label = "Click",  ID = hash of "node" + "Click"
470          TreePop();
471        }
472
473    - When working with trees, ID are used to preserve the open/close state of each tree node.
474      Depending on your use cases you may want to use strings, indices or pointers as ID.
475       e.g. when displaying a single object that may change over time (dynamic 1-1 relationship), using a static string as ID will preserve your
476        node open/closed state when the targeted object change.
477       e.g. when displaying a list of objects, using indices or pointers as ID will preserve the node open/closed state differently. 
478        experiment and see what makes more sense!
479
480  Q: How can I tell when Dear ImGui wants my mouse/keyboard inputs VS when I can pass them to my application?
481  A: You can read the 'io.WantCaptureMouse'/'io.WantCaptureKeyboard'/'ioWantTextInput' flags from the ImGuiIO structure. 
482     - When 'io.WantCaptureMouse' or 'io.WantCaptureKeyboard' flags are set you may want to discard/hide the inputs from the rest of your application.
483     - When 'io.WantTextInput' is set to may want to notify your OS to popup an on-screen keyboard, if available (e.g. on a mobile phone, or console OS).
484     Preferably read the flags after calling ImGui::NewFrame() to avoid them lagging by one frame. But reading those flags before calling NewFrame() is
485     also generally ok, as the bool toggles fairly rarely and you don't generally expect to interact with either Dear ImGui or your application during
486     the same frame when that transition occurs. Dear ImGui is tracking dragging and widget activity that may occur outside the boundary of a window, 
487     so 'io.WantCaptureMouse' is more accurate and correct than checking if a window is hovered. 
488     (Advanced note: text input releases focus on Return 'KeyDown', so the following Return 'KeyUp' event that your application receive will typically 
489      have 'io.WantCaptureKeyboard=false'. Depending on your application logic it may or not be inconvenient. You might want to track which key-downs
490      were for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.)
491
492  Q: How can I load a different font than the default? (default is an embedded version of ProggyClean.ttf, rendered at size 13)
493  A: Use the font atlas to load the TTF/OTF file you want:
494
495       ImGuiIO& io = ImGui::GetIO();
496       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
497       io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
498
499  Q: How can I easily use icons in my application?
500  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 
501     strings. Read 'How can I load multiple fonts?' and the file 'extra_fonts/README.txt' for instructions and useful header files.
502
503  Q: How can I load multiple fonts?
504  A: Use the font atlas to pack them into a single texture:
505     (Read extra_fonts/README.txt and the code in ImFontAtlas for more details.)
506
507       ImGuiIO& io = ImGui::GetIO();
508       ImFont* font0 = io.Fonts->AddFontDefault();
509       ImFont* font1 = io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
510       ImFont* font2 = io.Fonts->AddFontFromFileTTF("myfontfile2.ttf", size_in_pixels);
511       io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
512       // the first loaded font gets used by default
513       // use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime
514
515       // Options
516       ImFontConfig config;
517       config.OversampleH = 3;
518       config.OversampleV = 1;
519       config.GlyphOffset.y -= 2.0f;      // Move everything by 2 pixels up
520       config.GlyphExtraSpacing.x = 1.0f; // Increase spacing between characters
521       io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, &config);
522
523       // Combine multiple fonts into one (e.g. for icon fonts)
524       ImWchar ranges[] = { 0xf000, 0xf3ff, 0 };
525       ImFontConfig config;
526       config.MergeMode = true;
527       io.Fonts->AddFontDefault();
528       io.Fonts->LoadFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); // Merge icon font
529       io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge japanese glyphs
530
531  Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
532  A: When loading a font, pass custom Unicode ranges to specify the glyphs to load. 
533
534       // Add default Japanese ranges
535       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese());
536    
537       // Or create your own custom ranges (e.g. for a game you can feed your entire game script and only build the characters the game need)
538       ImVector<ImWchar> ranges;
539       ImFontAtlas::GlyphRangesBuilder builder;
540       builder.AddText("Hello world");                        // Add a string (here "Hello world" contains 7 unique characters)
541       builder.AddChar(0x7262);                               // Add a specific character
542       builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Add one of the default ranges
543       builder.BuildRanges(&ranges);                          // Build the final result (ordered ranges with all the unique characters submitted)
544       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, ranges.Data);
545
546     All your strings needs to use UTF-8 encoding. In C++11 you can encode a string literal in UTF-8 by using the u8"hello" syntax. 
547     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!
548     Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8.
549
550     Text input: it is up to your application to pass the right character code to io.AddInputCharacter(). The applications in examples/ are doing that.
551     For languages using IME, on Windows you can copy the Hwnd of your application to io.ImeWindowHandle.
552     The default implementation of io.ImeSetInputScreenPosFn() on Windows will set your IME position correctly.
553
554  Q: How can I preserve my Dear ImGui context across reloading a DLL? (loss of the global/static variables)
555  A: Create your own context 'ctx = CreateContext()' + 'SetCurrentContext(ctx)' and your own font atlas 'ctx->GetIO().Fonts = new ImFontAtlas()' 
556     so you don't rely on the default globals.
557
558  Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
559  A: The easiest way is to create a dummy window. Call Begin() with NoTitleBar|NoResize|NoMove|NoScrollbar|NoSavedSettings|NoInputs flag, 
560     zero background alpha, then retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like.
561     You can also perfectly create a standalone ImDrawList instance _but_ you need ImGui to be initialized because ImDrawList pulls from ImGui 
562     data to retrieve the coordinates of the white pixel.
563
564  - tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window. 
565    this is also useful to set yourself in the context of another window (to get/set other settings)
566  - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug".
567  - tip: the ImGuiOnceUponAFrame helper will allow run the block of code only once a frame. You can use it to quickly add custom UI in the middle
568    of a deep nested inner loop in your code.
569  - tip: you can call Render() multiple times (e.g for VR renders).
570  - tip: call and read the ShowTestWindow() code in imgui_demo.cpp for more example of how to use ImGui!
571
572 */
573
574 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
575 #define _CRT_SECURE_NO_WARNINGS
576 #endif
577
578 #include "imgui.h"
579 #define IMGUI_DEFINE_MATH_OPERATORS
580 #define IMGUI_DEFINE_PLACEMENT_NEW
581 #include "imgui_internal.h"
582
583 #include <ctype.h>      // toupper, isprint
584 #include <stdlib.h>     // NULL, malloc, free, qsort, atoi
585 #include <stdio.h>      // vsnprintf, sscanf, printf
586 #include <limits.h>     // INT_MIN, INT_MAX
587 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
588 #include <stddef.h>     // intptr_t
589 #else
590 #include <stdint.h>     // intptr_t
591 #endif
592
593 #ifdef _MSC_VER
594 #pragma warning (disable: 4127) // condition expression is constant
595 #pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
596 #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
597 #endif
598
599 // Clang warnings with -Weverything
600 #ifdef __clang__
601 #pragma clang diagnostic ignored "-Wunknown-pragmas"        // warning : unknown warning group '-Wformat-pedantic *'        // not all warnings are known by all clang versions.. so ignoring warnings triggers new warnings on some configuration. great!
602 #pragma clang diagnostic ignored "-Wold-style-cast"         // warning : use of old-style cast                              // yes, they are more terse.
603 #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.
604 #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.
605 #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.
606 #pragma clang diagnostic ignored "-Wglobal-constructors"    // warning : declaration requires a global destructor           // similar to above, not sure what the exact difference it.
607 #pragma clang diagnostic ignored "-Wsign-conversion"        // warning : implicit conversion changes signedness             //
608 #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. 
609 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int' //
610 #elif defined(__GNUC__)
611 #pragma GCC diagnostic ignored "-Wunused-function"          // warning: 'xxxx' defined but not used
612 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"      // warning: cast to pointer from integer of different size
613 #pragma GCC diagnostic ignored "-Wformat"                   // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
614 #pragma GCC diagnostic ignored "-Wdouble-promotion"         // warning: implicit conversion from 'float' to 'double' when passing argument to function
615 #pragma GCC diagnostic ignored "-Wconversion"               // warning: conversion to 'xxxx' from 'xxxx' may alter its value
616 #pragma GCC diagnostic ignored "-Wcast-qual"                // warning: cast from type 'xxxx' to type 'xxxx' casts away qualifiers
617 #pragma GCC diagnostic ignored "-Wformat-nonliteral"        // warning: format not a string literal, format string not checked
618 #endif
619
620 //-------------------------------------------------------------------------
621 // Forward Declarations
622 //-------------------------------------------------------------------------
623
624 static float            GetDraggedColumnOffset(int column_index);
625
626 static bool             IsKeyPressedMap(ImGuiKey key, bool repeat = true);
627
628 static ImFont*          GetDefaultFont();
629 static void             SetCurrentFont(ImFont* font);
630 static void             SetCurrentWindow(ImGuiWindow* window);
631 static void             SetWindowScrollY(ImGuiWindow* window, float new_scroll_y);
632 static void             SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond);
633 static void             SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond);
634 static void             SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond);
635 static ImGuiWindow*     FindHoveredWindow(ImVec2 pos);
636 static ImGuiWindow*     CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
637 static void             ClearSetNextWindowData();
638 static void             CheckStacksSize(ImGuiWindow* window, bool write);
639 static ImVec2           CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);
640
641 static void             AddDrawListToRenderList(ImVector<ImDrawList*>& out_render_list, ImDrawList* draw_list);
642 static void             AddWindowToRenderList(ImVector<ImDrawList*>& out_render_list, ImGuiWindow* window);
643 static void             AddWindowToSortedBuffer(ImVector<ImGuiWindow*>& out_sorted_windows, ImGuiWindow* window);
644
645 static ImGuiIniData*    FindWindowSettings(const char* name);
646 static ImGuiIniData*    AddWindowSettings(const char* name);
647 static void             LoadIniSettingsFromDisk(const char* ini_filename);
648 static void             SaveIniSettingsToDisk(const char* ini_filename);
649 static void             MarkIniSettingsDirty(ImGuiWindow* window);
650
651 static ImRect           GetVisibleRect();
652
653 static void             CloseInactivePopups(ImGuiWindow* ref_window);
654 static void             ClosePopupToLevel(int remaining);
655 static ImGuiWindow*     GetFrontMostModalRootWindow();
656 static ImVec2           FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& rect_to_avoid);
657
658 static bool             InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data);
659 static int              InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end);
660 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);
661
662 static inline void      DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size);
663 static inline void      DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size);
664 static void             DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2);
665 static bool             DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format);
666
667 namespace ImGui
668 {
669 static void             FocusPreviousWindow();
670 }
671
672 //-----------------------------------------------------------------------------
673 // Platform dependent default implementations
674 //-----------------------------------------------------------------------------
675
676 static const char*      GetClipboardTextFn_DefaultImpl(void* user_data);
677 static void             SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
678 static void             ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
679
680 //-----------------------------------------------------------------------------
681 // Context
682 //-----------------------------------------------------------------------------
683
684 // Default font atlas storage.
685 // New contexts always point by default to this font atlas. It can be changed by reassigning the GetIO().Fonts variable.
686 static ImFontAtlas      GImDefaultFontAtlas;
687
688 // Default context storage + current context pointer.
689 // Implicitely used by all ImGui functions. Always assumed to be != NULL. Change to a different context by calling ImGui::SetCurrentContext()
690 // If you are hot-reloading this code in a DLL you will lose the static/global variables. Create your own context+font atlas instead of relying on those default (see FAQ entry "How can I preserve my ImGui context across reloading a DLL?").
691 // 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:
692 // - Having multiple instances of the ImGui code compiled inside different namespace (easiest/safest, if you have a finite number of contexts)
693 // - 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
694 #ifndef GImGui
695 static ImGuiContext     GImDefaultContext;
696 ImGuiContext*           GImGui = &GImDefaultContext;
697 #endif
698
699 //-----------------------------------------------------------------------------
700 // User facing structures
701 //-----------------------------------------------------------------------------
702
703 ImGuiStyle::ImGuiStyle()
704 {
705     Alpha                   = 1.0f;             // Global alpha applies to everything in ImGui
706     WindowPadding           = ImVec2(8,8);      // Padding within a window
707     WindowRounding          = 9.0f;             // Radius of window corners rounding. Set to 0.0f to have rectangular windows
708     WindowBorderSize        = 0.0f;             // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
709     WindowMinSize           = ImVec2(32,32);    // Minimum window size
710     WindowTitleAlign        = ImVec2(0.0f,0.5f);// Alignment for title bar text
711     ChildRounding           = 0.0f;             // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
712     ChildBorderSize         = 1.0f;             // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
713     PopupRounding           = 0.0f;             // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
714     PopupBorderSize         = 1.0f;             // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
715     FramePadding            = ImVec2(4,3);      // Padding within a framed rectangle (used by most widgets)
716     FrameRounding           = 0.0f;             // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
717     FrameBorderSize         = 0.0f;             // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
718     ItemSpacing             = ImVec2(8,4);      // Horizontal and vertical spacing between widgets/lines
719     ItemInnerSpacing        = ImVec2(4,4);      // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
720     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!
721     IndentSpacing           = 21.0f;            // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
722     ColumnsMinSpacing       = 6.0f;             // Minimum horizontal spacing between two columns
723     ScrollbarSize           = 16.0f;            // Width of the vertical scrollbar, Height of the horizontal scrollbar
724     ScrollbarRounding       = 9.0f;             // Radius of grab corners rounding for scrollbar
725     GrabMinSize             = 10.0f;            // Minimum width/height of a grab box for slider/scrollbar
726     GrabRounding            = 0.0f;             // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
727     ButtonTextAlign         = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
728     DisplayWindowPadding    = ImVec2(22,22);    // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows.
729     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.
730     AntiAliasedLines        = true;             // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
731     AntiAliasedShapes       = true;             // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
732     CurveTessellationTol    = 1.25f;            // Tessellation tolerance. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
733
734     ImGui::StyleColorsClassic(this);
735 }
736
737 // To scale your entire UI (e.g. if you want your app to use High DPI or generally be DPI aware) you may use this helper function. Scaling the fonts is done separately and is up to you.
738 // Important: This operation is lossy because we round all sizes to integer. If you need to change your scale multiples, call this over a freshly initialized ImGuiStyle structure rather than scaling multiple times.
739 void ImGuiStyle::ScaleAllSizes(float scale_factor)
740 {
741     WindowPadding = ImFloor(WindowPadding * scale_factor);
742     WindowRounding = ImFloor(WindowRounding * scale_factor);
743     WindowMinSize = ImFloor(WindowMinSize * scale_factor);
744     ChildRounding = ImFloor(ChildRounding * scale_factor);
745     PopupRounding = ImFloor(PopupRounding * scale_factor);
746     FramePadding = ImFloor(FramePadding * scale_factor);
747     FrameRounding = ImFloor(FrameRounding * scale_factor);
748     ItemSpacing = ImFloor(ItemSpacing * scale_factor);
749     ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
750     TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
751     IndentSpacing = ImFloor(IndentSpacing * scale_factor);
752     ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
753     ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
754     ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
755     GrabMinSize = ImFloor(GrabMinSize * scale_factor);
756     GrabRounding = ImFloor(GrabRounding * scale_factor);
757     DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
758     DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
759 }
760
761 ImGuiIO::ImGuiIO()
762 {
763     // Most fields are initialized with zero
764     memset(this, 0, sizeof(*this));
765
766     // Settings
767     DisplaySize = ImVec2(-1.0f, -1.0f);
768     DeltaTime = 1.0f/60.0f;
769     IniSavingRate = 5.0f;
770     IniFilename = "imgui.ini";
771     LogFilename = "imgui_log.txt";
772     MouseDoubleClickTime = 0.30f;
773     MouseDoubleClickMaxDist = 6.0f;
774     for (int i = 0; i < ImGuiKey_COUNT; i++)
775         KeyMap[i] = -1;
776     KeyRepeatDelay = 0.250f;
777     KeyRepeatRate = 0.050f;
778     UserData = NULL;
779
780     Fonts = &GImDefaultFontAtlas;
781     FontGlobalScale = 1.0f;
782     FontDefault = NULL;
783     FontAllowUserScaling = false;
784     DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
785     DisplayVisibleMin = DisplayVisibleMax = ImVec2(0.0f, 0.0f);
786
787     // Advanced/subtle behaviors
788 #ifdef __APPLE__
789     OptMacOSXBehaviors = true;  // Set Mac OS X style defaults based on __APPLE__ compile time flag
790 #else
791     OptMacOSXBehaviors = false;
792 #endif
793     OptCursorBlink = true;
794                                 
795     // Settings (User Functions)
796     RenderDrawListsFn = NULL;
797     MemAllocFn = malloc;
798     MemFreeFn = free;
799     GetClipboardTextFn = GetClipboardTextFn_DefaultImpl;   // Platform dependent default implementations
800     SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
801     ClipboardUserData = NULL;
802     ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
803     ImeWindowHandle = NULL;
804
805     // Input (NB: we already have memset zero the entire structure)
806     MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
807     MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
808     MouseDragThreshold = 6.0f;
809     for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
810     for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f;
811 }
812
813 // Pass in translated ASCII characters for text input.
814 // - with glfw you can get those from the callback set in glfwSetCharCallback()
815 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
816 void ImGuiIO::AddInputCharacter(ImWchar c)
817 {
818     const int n = ImStrlenW(InputCharacters);
819     if (n + 1 < IM_ARRAYSIZE(InputCharacters))
820     {
821         InputCharacters[n] = c;
822         InputCharacters[n+1] = '\0';
823     }
824 }
825
826 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
827 {
828     // We can't pass more wchars than ImGuiIO::InputCharacters[] can hold so don't convert more
829     const int wchars_buf_len = sizeof(ImGuiIO::InputCharacters) / sizeof(ImWchar);
830     ImWchar wchars[wchars_buf_len];
831     ImTextStrFromUtf8(wchars, wchars_buf_len, utf8_chars, NULL);
832     for (int i = 0; i < wchars_buf_len && wchars[i] != 0; i++)
833         AddInputCharacter(wchars[i]);
834 }
835
836 //-----------------------------------------------------------------------------
837 // HELPERS
838 //-----------------------------------------------------------------------------
839
840 #define IM_F32_TO_INT8_UNBOUND(_VAL)    ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f)))   // Unsaturated, for display purpose 
841 #define IM_F32_TO_INT8_SAT(_VAL)        ((int)(ImSaturate(_VAL) * 255.0f + 0.5f))               // Saturated, always output 0..255
842
843 // Play it nice with Windows users. Notepad in 2015 still doesn't display text data with Unix-style \n.
844 #ifdef _WIN32
845 #define IM_NEWLINE "\r\n"
846 #else
847 #define IM_NEWLINE "\n"
848 #endif
849
850 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
851 {
852     ImVec2 ap = p - a;
853     ImVec2 ab_dir = b - a;
854     float ab_len = sqrtf(ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y);
855     ab_dir *= 1.0f / ab_len;
856     float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
857     if (dot < 0.0f)
858         return a;
859     if (dot > ab_len)
860         return b;
861     return a + ab_dir * dot;
862 }
863
864 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
865 {
866     bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
867     bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
868     bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
869     return ((b1 == b2) && (b2 == b3));
870 }
871
872 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
873 {
874     ImVec2 v0 = b - a;
875     ImVec2 v1 = c - a;
876     ImVec2 v2 = p - a;
877     const float denom = v0.x * v1.y - v1.x * v0.y;
878     out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
879     out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
880     out_u = 1.0f - out_v - out_w;
881 }
882
883 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
884 {
885     ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
886     ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
887     ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
888     float dist2_ab = ImLengthSqr(p - proj_ab);
889     float dist2_bc = ImLengthSqr(p - proj_bc);
890     float dist2_ca = ImLengthSqr(p - proj_ca);
891     float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
892     if (m == dist2_ab)
893         return proj_ab;
894     if (m == dist2_bc)
895         return proj_bc;
896     return proj_ca;
897 }
898
899 int ImStricmp(const char* str1, const char* str2)
900 {
901     int d;
902     while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
903     return d;
904 }
905
906 int ImStrnicmp(const char* str1, const char* str2, int count)
907 {
908     int d = 0;
909     while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
910     return d;
911 }
912
913 void ImStrncpy(char* dst, const char* src, int count)
914 {
915     if (count < 1) return;
916     strncpy(dst, src, (size_t)count);
917     dst[count-1] = 0;
918 }
919
920 char* ImStrdup(const char *str)
921 {
922     size_t len = strlen(str) + 1;
923     void* buff = ImGui::MemAlloc(len);
924     return (char*)memcpy(buff, (const void*)str, len);
925 }
926
927 int ImStrlenW(const ImWchar* str)
928 {
929     int n = 0;
930     while (*str++) n++;
931     return n;
932 }
933
934 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
935 {
936     while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
937         buf_mid_line--;
938     return buf_mid_line;
939 }
940
941 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
942 {
943     if (!needle_end)
944         needle_end = needle + strlen(needle);
945
946     const char un0 = (char)toupper(*needle);
947     while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
948     {
949         if (toupper(*haystack) == un0)
950         {
951             const char* b = needle + 1;
952             for (const char* a = haystack + 1; b < needle_end; a++, b++)
953                 if (toupper(*a) != toupper(*b))
954                     break;
955             if (b == needle_end)
956                 return haystack;
957         }
958         haystack++;
959     }
960     return NULL;
961 }
962
963 static const char* ImAtoi(const char* src, int* output)
964 {
965     int negative = 0;
966     if (*src == '-') { negative = 1; src++; }
967     if (*src == '+') { src++; }
968     int v = 0;
969     while (*src >= '0' && *src <= '9')
970         v = (v * 10) + (*src++ - '0');
971     *output = negative ? -v : v;
972     return src;
973 }
974
975 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size). 
976 // 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.
977 // B) When buf==NULL vsnprintf() will return the output size.
978 #ifndef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
979 int ImFormatString(char* buf, int buf_size, const char* fmt, ...)
980 {
981     va_list args;
982     va_start(args, fmt);
983     int w = vsnprintf(buf, buf_size, fmt, args);
984     va_end(args);
985     if (buf == NULL)
986         return w;
987     if (w == -1 || w >= buf_size)
988         w = buf_size - 1;
989     buf[w] = 0;
990     return w;
991 }
992
993 int ImFormatStringV(char* buf, int buf_size, const char* fmt, va_list args)
994 {
995     int w = vsnprintf(buf, buf_size, fmt, args);
996     if (buf == NULL)
997         return w;
998     if (w == -1 || w >= buf_size)
999         w = buf_size - 1;
1000     buf[w] = 0;
1001     return w;
1002 }
1003 #endif // #ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1004
1005 // Pass data_size==0 for zero-terminated strings
1006 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
1007 ImU32 ImHash(const void* data, int data_size, ImU32 seed)
1008 {
1009     static ImU32 crc32_lut[256] = { 0 };
1010     if (!crc32_lut[1])
1011     {
1012         const ImU32 polynomial = 0xEDB88320;
1013         for (ImU32 i = 0; i < 256; i++)
1014         {
1015             ImU32 crc = i;
1016             for (ImU32 j = 0; j < 8; j++)
1017                 crc = (crc >> 1) ^ (ImU32(-int(crc & 1)) & polynomial);
1018             crc32_lut[i] = crc;
1019         }
1020     }
1021
1022     seed = ~seed;
1023     ImU32 crc = seed;
1024     const unsigned char* current = (const unsigned char*)data;
1025
1026     if (data_size > 0)
1027     {
1028         // Known size
1029         while (data_size--)
1030             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *current++];
1031     }
1032     else
1033     {
1034         // Zero-terminated string
1035         while (unsigned char c = *current++)
1036         {
1037             // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1038             // Because this syntax is rarely used we are optimizing for the common case.
1039             // - If we reach ### in the string we discard the hash so far and reset to the seed.
1040             // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller.
1041             if (c == '#' && current[0] == '#' && current[1] == '#')
1042                 crc = seed;
1043             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1044         }
1045     }
1046     return ~crc;
1047 }
1048
1049 //-----------------------------------------------------------------------------
1050 // ImText* helpers
1051 //-----------------------------------------------------------------------------
1052
1053 // Convert UTF-8 to 32-bits character, process single character input.
1054 // Based on stb_from_utf8() from github.com/nothings/stb/
1055 // We handle UTF-8 decoding error by skipping forward.
1056 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1057 {
1058     unsigned int c = (unsigned int)-1;
1059     const unsigned char* str = (const unsigned char*)in_text;
1060     if (!(*str & 0x80))
1061     {
1062         c = (unsigned int)(*str++);
1063         *out_char = c;
1064         return 1;
1065     }
1066     if ((*str & 0xe0) == 0xc0)
1067     {
1068         *out_char = 0xFFFD; // will be invalid but not end of string
1069         if (in_text_end && in_text_end - (const char*)str < 2) return 1;
1070         if (*str < 0xc2) return 2;
1071         c = (unsigned int)((*str++ & 0x1f) << 6);
1072         if ((*str & 0xc0) != 0x80) return 2;
1073         c += (*str++ & 0x3f);
1074         *out_char = c;
1075         return 2;
1076     }
1077     if ((*str & 0xf0) == 0xe0)
1078     {
1079         *out_char = 0xFFFD; // will be invalid but not end of string
1080         if (in_text_end && in_text_end - (const char*)str < 3) return 1;
1081         if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3;
1082         if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below
1083         c = (unsigned int)((*str++ & 0x0f) << 12);
1084         if ((*str & 0xc0) != 0x80) return 3;
1085         c += (unsigned int)((*str++ & 0x3f) << 6);
1086         if ((*str & 0xc0) != 0x80) return 3;
1087         c += (*str++ & 0x3f);
1088         *out_char = c;
1089         return 3;
1090     }
1091     if ((*str & 0xf8) == 0xf0)
1092     {
1093         *out_char = 0xFFFD; // will be invalid but not end of string
1094         if (in_text_end && in_text_end - (const char*)str < 4) return 1;
1095         if (*str > 0xf4) return 4;
1096         if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4;
1097         if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below
1098         c = (unsigned int)((*str++ & 0x07) << 18);
1099         if ((*str & 0xc0) != 0x80) return 4;
1100         c += (unsigned int)((*str++ & 0x3f) << 12);
1101         if ((*str & 0xc0) != 0x80) return 4;
1102         c += (unsigned int)((*str++ & 0x3f) << 6);
1103         if ((*str & 0xc0) != 0x80) return 4;
1104         c += (*str++ & 0x3f);
1105         // utf-8 encodings of values used in surrogate pairs are invalid
1106         if ((c & 0xFFFFF800) == 0xD800) return 4;
1107         *out_char = c;
1108         return 4;
1109     }
1110     *out_char = 0;
1111     return 0;
1112 }
1113
1114 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1115 {
1116     ImWchar* buf_out = buf;
1117     ImWchar* buf_end = buf + buf_size;
1118     while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1119     {
1120         unsigned int c;
1121         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1122         if (c == 0)
1123             break;
1124         if (c < 0x10000)    // FIXME: Losing characters that don't fit in 2 bytes
1125             *buf_out++ = (ImWchar)c;
1126     }
1127     *buf_out = 0;
1128     if (in_text_remaining)
1129         *in_text_remaining = in_text;
1130     return (int)(buf_out - buf);
1131 }
1132
1133 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1134 {
1135     int char_count = 0;
1136     while ((!in_text_end || in_text < in_text_end) && *in_text)
1137     {
1138         unsigned int c;
1139         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1140         if (c == 0)
1141             break;
1142         if (c < 0x10000)
1143             char_count++;
1144     }
1145     return char_count;
1146 }
1147
1148 // Based on stb_to_utf8() from github.com/nothings/stb/
1149 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1150 {
1151     if (c < 0x80)
1152     {
1153         buf[0] = (char)c;
1154         return 1;
1155     }
1156     if (c < 0x800)
1157     {
1158         if (buf_size < 2) return 0;
1159         buf[0] = (char)(0xc0 + (c >> 6));
1160         buf[1] = (char)(0x80 + (c & 0x3f));
1161         return 2;
1162     }
1163     if (c >= 0xdc00 && c < 0xe000)
1164     {
1165         return 0;
1166     }
1167     if (c >= 0xd800 && c < 0xdc00)
1168     {
1169         if (buf_size < 4) return 0;
1170         buf[0] = (char)(0xf0 + (c >> 18));
1171         buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1172         buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1173         buf[3] = (char)(0x80 + ((c ) & 0x3f));
1174         return 4;
1175     }
1176     //else if (c < 0x10000)
1177     {
1178         if (buf_size < 3) return 0;
1179         buf[0] = (char)(0xe0 + (c >> 12));
1180         buf[1] = (char)(0x80 + ((c>> 6) & 0x3f));
1181         buf[2] = (char)(0x80 + ((c ) & 0x3f));
1182         return 3;
1183     }
1184 }
1185
1186 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1187 {
1188     if (c < 0x80) return 1;
1189     if (c < 0x800) return 2;
1190     if (c >= 0xdc00 && c < 0xe000) return 0;
1191     if (c >= 0xd800 && c < 0xdc00) return 4;
1192     return 3;
1193 }
1194
1195 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1196 {
1197     char* buf_out = buf;
1198     const char* buf_end = buf + buf_size;
1199     while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1200     {
1201         unsigned int c = (unsigned int)(*in_text++);
1202         if (c < 0x80)
1203             *buf_out++ = (char)c;
1204         else
1205             buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c);
1206     }
1207     *buf_out = 0;
1208     return (int)(buf_out - buf);
1209 }
1210
1211 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1212 {
1213     int bytes_count = 0;
1214     while ((!in_text_end || in_text < in_text_end) && *in_text)
1215     {
1216         unsigned int c = (unsigned int)(*in_text++);
1217         if (c < 0x80)
1218             bytes_count++;
1219         else
1220             bytes_count += ImTextCountUtf8BytesFromChar(c);
1221     }
1222     return bytes_count;
1223 }
1224
1225 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1226 {
1227     float s = 1.0f/255.0f;
1228     return ImVec4(
1229         ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1230         ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1231         ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1232         ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1233 }
1234
1235 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1236 {
1237     ImU32 out;
1238     out  = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1239     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1240     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1241     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1242     return out;
1243 }
1244
1245 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)  
1246
1247     ImGuiStyle& style = GImGui->Style;
1248     ImVec4 c = style.Colors[idx]; 
1249     c.w *= style.Alpha * alpha_mul; 
1250     return ColorConvertFloat4ToU32(c); 
1251 }
1252
1253 ImU32 ImGui::GetColorU32(const ImVec4& col)
1254
1255     ImGuiStyle& style = GImGui->Style;
1256     ImVec4 c = col; 
1257     c.w *= style.Alpha; 
1258     return ColorConvertFloat4ToU32(c); 
1259 }
1260
1261 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
1262
1263     ImGuiStyle& style = GImGui->Style;
1264     return style.Colors[idx];
1265 }
1266
1267 ImU32 ImGui::GetColorU32(ImU32 col)
1268
1269     float style_alpha = GImGui->Style.Alpha;
1270     if (style_alpha >= 1.0f)
1271         return col;
1272     int a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
1273     a = (int)(a * style_alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
1274     return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
1275 }
1276
1277 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1278 // Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
1279 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1280 {
1281     float K = 0.f;
1282     if (g < b)
1283     {
1284         ImSwap(g, b);
1285         K = -1.f;
1286     }
1287     if (r < g)
1288     {
1289         ImSwap(r, g);
1290         K = -2.f / 6.f - K;
1291     }
1292
1293     const float chroma = r - (g < b ? g : b);
1294     out_h = fabsf(K + (g - b) / (6.f * chroma + 1e-20f));
1295     out_s = chroma / (r + 1e-20f);
1296     out_v = r;
1297 }
1298
1299 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1300 // also http://en.wikipedia.org/wiki/HSL_and_HSV
1301 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1302 {
1303     if (s == 0.0f)
1304     {
1305         // gray
1306         out_r = out_g = out_b = v;
1307         return;
1308     }
1309
1310     h = fmodf(h, 1.0f) / (60.0f/360.0f);
1311     int   i = (int)h;
1312     float f = h - (float)i;
1313     float p = v * (1.0f - s);
1314     float q = v * (1.0f - s * f);
1315     float t = v * (1.0f - s * (1.0f - f));
1316
1317     switch (i)
1318     {
1319     case 0: out_r = v; out_g = t; out_b = p; break;
1320     case 1: out_r = q; out_g = v; out_b = p; break;
1321     case 2: out_r = p; out_g = v; out_b = t; break;
1322     case 3: out_r = p; out_g = q; out_b = v; break;
1323     case 4: out_r = t; out_g = p; out_b = v; break;
1324     case 5: default: out_r = v; out_g = p; out_b = q; break;
1325     }
1326 }
1327
1328 FILE* ImFileOpen(const char* filename, const char* mode)
1329 {
1330 #if defined(_WIN32) && !defined(__CYGWIN__)
1331     // 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)
1332     const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1;
1333     const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1;
1334     ImVector<ImWchar> buf;
1335     buf.resize(filename_wsize + mode_wsize);
1336     ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL);
1337     ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL);
1338     return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]);
1339 #else
1340     return fopen(filename, mode);
1341 #endif
1342 }
1343
1344 // Load file content into memory
1345 // Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree()
1346 void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, int* out_file_size, int padding_bytes)
1347 {
1348     IM_ASSERT(filename && file_open_mode);
1349     if (out_file_size)
1350         *out_file_size = 0;
1351
1352     FILE* f;
1353     if ((f = ImFileOpen(filename, file_open_mode)) == NULL)
1354         return NULL;
1355
1356     long file_size_signed;
1357     if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET))
1358     {
1359         fclose(f);
1360         return NULL;
1361     }
1362
1363     int file_size = (int)file_size_signed;
1364     void* file_data = ImGui::MemAlloc(file_size + padding_bytes);
1365     if (file_data == NULL)
1366     {
1367         fclose(f);
1368         return NULL;
1369     }
1370     if (fread(file_data, 1, (size_t)file_size, f) != (size_t)file_size)
1371     {
1372         fclose(f);
1373         ImGui::MemFree(file_data);
1374         return NULL;
1375     }
1376     if (padding_bytes > 0)
1377         memset((void *)(((char*)file_data) + file_size), 0, padding_bytes);
1378
1379     fclose(f);
1380     if (out_file_size)
1381         *out_file_size = file_size;
1382
1383     return file_data;
1384 }
1385
1386 //-----------------------------------------------------------------------------
1387 // ImGuiStorage
1388 // Helper: Key->value storage
1389 //-----------------------------------------------------------------------------
1390
1391 // std::lower_bound but without the bullshit
1392 static ImVector<ImGuiStorage::Pair>::iterator LowerBound(ImVector<ImGuiStorage::Pair>& data, ImGuiID key)
1393 {
1394     ImVector<ImGuiStorage::Pair>::iterator first = data.begin();
1395     ImVector<ImGuiStorage::Pair>::iterator last = data.end();
1396     size_t count = (size_t)(last - first);
1397     while (count > 0)
1398     {
1399         size_t count2 = count >> 1;
1400         ImVector<ImGuiStorage::Pair>::iterator mid = first + count2;
1401         if (mid->key < key)
1402         {
1403             first = ++mid;
1404             count -= count2 + 1;
1405         }
1406         else
1407         {
1408             count = count2;
1409         }
1410     }
1411     return first;
1412 }
1413
1414 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
1415 void ImGuiStorage::BuildSortByKey()
1416 {
1417     struct StaticFunc 
1418     { 
1419         static int PairCompareByID(const void* lhs, const void* rhs) 
1420         {
1421             // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1422             if (((const Pair*)lhs)->key > ((const Pair*)rhs)->key) return +1;
1423             if (((const Pair*)lhs)->key < ((const Pair*)rhs)->key) return -1;
1424             return 0;
1425         }
1426     };
1427     if (Data.Size > 1)
1428         qsort(Data.Data, (size_t)Data.Size, sizeof(Pair), StaticFunc::PairCompareByID);
1429 }
1430
1431 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1432 {
1433     ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1434     if (it == Data.end() || it->key != key)
1435         return default_val;
1436     return it->val_i;
1437 }
1438
1439 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1440 {
1441     return GetInt(key, default_val ? 1 : 0) != 0;
1442 }
1443
1444 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1445 {
1446     ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1447     if (it == Data.end() || it->key != key)
1448         return default_val;
1449     return it->val_f;
1450 }
1451
1452 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1453 {
1454     ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1455     if (it == Data.end() || it->key != key)
1456         return NULL;
1457     return it->val_p;
1458 }
1459
1460 // 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.
1461 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1462 {
1463     ImVector<Pair>::iterator it = LowerBound(Data, key);
1464     if (it == Data.end() || it->key != key)
1465         it = Data.insert(it, Pair(key, default_val));
1466     return &it->val_i;
1467 }
1468
1469 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1470 {
1471     return (bool*)GetIntRef(key, default_val ? 1 : 0);
1472 }
1473
1474 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1475 {
1476     ImVector<Pair>::iterator it = LowerBound(Data, key);
1477     if (it == Data.end() || it->key != key)
1478         it = Data.insert(it, Pair(key, default_val));
1479     return &it->val_f;
1480 }
1481
1482 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1483 {
1484     ImVector<Pair>::iterator it = LowerBound(Data, key);
1485     if (it == Data.end() || it->key != key)
1486         it = Data.insert(it, Pair(key, default_val));
1487     return &it->val_p;
1488 }
1489
1490 // 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)
1491 void ImGuiStorage::SetInt(ImGuiID key, int val)
1492 {
1493     ImVector<Pair>::iterator it = LowerBound(Data, key);
1494     if (it == Data.end() || it->key != key)
1495     {
1496         Data.insert(it, Pair(key, val));
1497         return;
1498     }
1499     it->val_i = val;
1500 }
1501
1502 void ImGuiStorage::SetBool(ImGuiID key, bool val)
1503 {
1504     SetInt(key, val ? 1 : 0);
1505 }
1506
1507 void ImGuiStorage::SetFloat(ImGuiID key, float val)
1508 {
1509     ImVector<Pair>::iterator it = LowerBound(Data, key);
1510     if (it == Data.end() || it->key != key)
1511     {
1512         Data.insert(it, Pair(key, val));
1513         return;
1514     }
1515     it->val_f = val;
1516 }
1517
1518 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1519 {
1520     ImVector<Pair>::iterator it = LowerBound(Data, key);
1521     if (it == Data.end() || it->key != key)
1522     {
1523         Data.insert(it, Pair(key, val));
1524         return;
1525     }
1526     it->val_p = val;
1527 }
1528
1529 void ImGuiStorage::SetAllInt(int v)
1530 {
1531     for (int i = 0; i < Data.Size; i++)
1532         Data[i].val_i = v;
1533 }
1534
1535 //-----------------------------------------------------------------------------
1536 // ImGuiTextFilter
1537 //-----------------------------------------------------------------------------
1538
1539 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
1540 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
1541 {
1542     if (default_filter)
1543     {
1544         ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
1545         Build();
1546     }
1547     else
1548     {
1549         InputBuf[0] = 0;
1550         CountGrep = 0;
1551     }
1552 }
1553
1554 bool ImGuiTextFilter::Draw(const char* label, float width)
1555 {
1556     if (width != 0.0f)
1557         ImGui::PushItemWidth(width);
1558     bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
1559     if (width != 0.0f)
1560         ImGui::PopItemWidth();
1561     if (value_changed)
1562         Build();
1563     return value_changed;
1564 }
1565
1566 void ImGuiTextFilter::TextRange::split(char separator, ImVector<TextRange>& out)
1567 {
1568     out.resize(0);
1569     const char* wb = b;
1570     const char* we = wb;
1571     while (we < e)
1572     {
1573         if (*we == separator)
1574         {
1575             out.push_back(TextRange(wb, we));
1576             wb = we + 1;
1577         }
1578         we++;
1579     }
1580     if (wb != we)
1581         out.push_back(TextRange(wb, we));
1582 }
1583
1584 void ImGuiTextFilter::Build()
1585 {
1586     Filters.resize(0);
1587     TextRange input_range(InputBuf, InputBuf+strlen(InputBuf));
1588     input_range.split(',', Filters);
1589
1590     CountGrep = 0;
1591     for (int i = 0; i != Filters.Size; i++)
1592     {
1593         Filters[i].trim_blanks();
1594         if (Filters[i].empty())
1595             continue;
1596         if (Filters[i].front() != '-')
1597             CountGrep += 1;
1598     }
1599 }
1600
1601 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
1602 {
1603     if (Filters.empty())
1604         return true;
1605
1606     if (text == NULL)
1607         text = "";
1608
1609     for (int i = 0; i != Filters.Size; i++)
1610     {
1611         const TextRange& f = Filters[i];
1612         if (f.empty())
1613             continue;
1614         if (f.front() == '-')
1615         {
1616             // Subtract
1617             if (ImStristr(text, text_end, f.begin()+1, f.end()) != NULL)
1618                 return false;
1619         }
1620         else
1621         {
1622             // Grep
1623             if (ImStristr(text, text_end, f.begin(), f.end()) != NULL)
1624                 return true;
1625         }
1626     }
1627
1628     // Implicit * grep
1629     if (CountGrep == 0)
1630         return true;
1631
1632     return false;
1633 }
1634
1635 //-----------------------------------------------------------------------------
1636 // ImGuiTextBuffer
1637 //-----------------------------------------------------------------------------
1638
1639 // On some platform vsnprintf() takes va_list by reference and modifies it.
1640 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
1641 #ifndef va_copy
1642 #define va_copy(dest, src) (dest = src)
1643 #endif
1644
1645 // Helper: Text buffer for logging/accumulating text
1646 void ImGuiTextBuffer::appendv(const char* fmt, va_list args)
1647 {
1648     va_list args_copy;
1649     va_copy(args_copy, args);
1650
1651     int len = ImFormatStringV(NULL, 0, fmt, args);         // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
1652     if (len <= 0)
1653         return;
1654
1655     const int write_off = Buf.Size;
1656     const int needed_sz = write_off + len;
1657     if (write_off + len >= Buf.Capacity)
1658     {
1659         int double_capacity = Buf.Capacity * 2;
1660         Buf.reserve(needed_sz > double_capacity ? needed_sz : double_capacity);
1661     }
1662
1663     Buf.resize(needed_sz);
1664     ImFormatStringV(&Buf[write_off - 1], len + 1, fmt, args_copy);
1665 }
1666
1667 void ImGuiTextBuffer::append(const char* fmt, ...)
1668 {
1669     va_list args;
1670     va_start(args, fmt);
1671     appendv(fmt, args);
1672     va_end(args);
1673 }
1674
1675 //-----------------------------------------------------------------------------
1676 // ImGuiSimpleColumns (internal use only)
1677 //-----------------------------------------------------------------------------
1678
1679 ImGuiSimpleColumns::ImGuiSimpleColumns()
1680 {
1681     Count = 0;
1682     Spacing = Width = NextWidth = 0.0f;
1683     memset(Pos, 0, sizeof(Pos));
1684     memset(NextWidths, 0, sizeof(NextWidths));
1685 }
1686
1687 void ImGuiSimpleColumns::Update(int count, float spacing, bool clear)
1688 {
1689     IM_ASSERT(Count <= IM_ARRAYSIZE(Pos));
1690     Count = count;
1691     Width = NextWidth = 0.0f;
1692     Spacing = spacing;
1693     if (clear) memset(NextWidths, 0, sizeof(NextWidths));
1694     for (int i = 0; i < Count; i++)
1695     {
1696         if (i > 0 && NextWidths[i] > 0.0f)
1697             Width += Spacing;
1698         Pos[i] = (float)(int)Width;
1699         Width += NextWidths[i];
1700         NextWidths[i] = 0.0f;
1701     }
1702 }
1703
1704 float ImGuiSimpleColumns::DeclColumns(float w0, float w1, float w2) // not using va_arg because they promote float to double
1705 {
1706     NextWidth = 0.0f;
1707     NextWidths[0] = ImMax(NextWidths[0], w0);
1708     NextWidths[1] = ImMax(NextWidths[1], w1);
1709     NextWidths[2] = ImMax(NextWidths[2], w2);
1710     for (int i = 0; i < 3; i++)
1711         NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f);
1712     return ImMax(Width, NextWidth);
1713 }
1714
1715 float ImGuiSimpleColumns::CalcExtraSpace(float avail_w)
1716 {
1717     return ImMax(0.0f, avail_w - Width);
1718 }
1719
1720 //-----------------------------------------------------------------------------
1721 // ImGuiListClipper
1722 //-----------------------------------------------------------------------------
1723
1724 static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height)
1725 {
1726     // Set cursor position and a few other things so that SetScrollHere() and Columns() can work when seeking cursor. 
1727     // 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?
1728     ImGui::SetCursorPosY(pos_y);
1729     ImGuiWindow* window = ImGui::GetCurrentWindow();
1730     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.
1731     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.
1732     if (window->DC.ColumnsCount > 1)
1733         window->DC.ColumnsCellMinY = window->DC.CursorPos.y;                    // Setting this so that cell Y position are set properly
1734 }
1735
1736 // Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
1737 // Use case B: Begin() called from constructor with items_height>0
1738 // 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.
1739 void ImGuiListClipper::Begin(int count, float items_height)
1740 {
1741     StartPosY = ImGui::GetCursorPosY();
1742     ItemsHeight = items_height;
1743     ItemsCount = count;
1744     StepNo = 0;
1745     DisplayEnd = DisplayStart = -1;
1746     if (ItemsHeight > 0.0f)
1747     {
1748         ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display
1749         if (DisplayStart > 0)
1750             SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor
1751         StepNo = 2;
1752     }
1753 }
1754
1755 void ImGuiListClipper::End()
1756 {
1757     if (ItemsCount < 0)
1758         return;
1759     // 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.
1760     if (ItemsCount < INT_MAX)
1761         SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor
1762     ItemsCount = -1;
1763     StepNo = 3;
1764 }
1765
1766 bool ImGuiListClipper::Step()
1767 {
1768     if (ItemsCount == 0 || ImGui::GetCurrentWindowRead()->SkipItems)
1769     {
1770         ItemsCount = -1; 
1771         return false; 
1772     }
1773     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.
1774     {
1775         DisplayStart = 0;
1776         DisplayEnd = 1;
1777         StartPosY = ImGui::GetCursorPosY();
1778         StepNo = 1;
1779         return true;
1780     }
1781     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.
1782     {
1783         if (ItemsCount == 1) { ItemsCount = -1; return false; }
1784         float items_height = ImGui::GetCursorPosY() - StartPosY;
1785         IM_ASSERT(items_height > 0.0f);   // If this triggers, it means Item 0 hasn't moved the cursor vertically
1786         Begin(ItemsCount-1, items_height);
1787         DisplayStart++;
1788         DisplayEnd++;
1789         StepNo = 3;
1790         return true;
1791     }
1792     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.
1793     {
1794         IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0);
1795         StepNo = 3;
1796         return true;
1797     }
1798     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.
1799         End();
1800     return false;
1801 }
1802
1803 //-----------------------------------------------------------------------------
1804 // ImGuiWindow
1805 //-----------------------------------------------------------------------------
1806
1807 ImGuiWindow::ImGuiWindow(const char* name)
1808 {
1809     Name = ImStrdup(name);
1810     ID = ImHash(name, 0);
1811     IDStack.push_back(ID);
1812     Flags = 0;
1813     OrderWithinParent = 0;
1814     PosFloat = Pos = ImVec2(0.0f, 0.0f);
1815     Size = SizeFull = ImVec2(0.0f, 0.0f);
1816     SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f);
1817     WindowPadding = ImVec2(0.0f, 0.0f);
1818     WindowRounding = 0.0f;
1819     WindowBorderSize = 0.0f;
1820     MoveId = GetID("#MOVE");
1821     Scroll = ImVec2(0.0f, 0.0f);
1822     ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
1823     ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
1824     ScrollbarX = ScrollbarY = false;
1825     ScrollbarSizes = ImVec2(0.0f, 0.0f);
1826     Active = WasActive = false;
1827     WriteAccessed = false;
1828     Collapsed = false;
1829     SkipItems = false;
1830     Appearing = false;
1831     CloseButton = false;
1832     BeginCount = 0;
1833     PopupId = 0;
1834     AutoFitFramesX = AutoFitFramesY = -1;
1835     AutoFitOnlyGrows = false;
1836     AutoFitChildAxises = 0x00;
1837     AutoPosLastDirection = -1;
1838     HiddenFrames = 0;
1839     SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
1840     SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
1841
1842     LastFrameActive = -1;
1843     ItemWidthDefault = 0.0f;
1844     FontWindowScale = 1.0f;
1845
1846     DrawList = (ImDrawList*)ImGui::MemAlloc(sizeof(ImDrawList));
1847     IM_PLACEMENT_NEW(DrawList) ImDrawList();
1848     DrawList->_OwnerName = Name;
1849     ParentWindow = NULL;
1850     RootWindow = NULL;
1851     RootNonPopupWindow = NULL;
1852
1853     FocusIdxAllCounter = FocusIdxTabCounter = -1;
1854     FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX;
1855     FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX;
1856 }
1857
1858 ImGuiWindow::~ImGuiWindow()
1859 {
1860     DrawList->~ImDrawList();
1861     ImGui::MemFree(DrawList);
1862     DrawList = NULL;
1863     ImGui::MemFree(Name);
1864     Name = NULL;
1865 }
1866
1867 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
1868 {
1869     ImGuiID seed = IDStack.back();
1870     ImGuiID id = ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
1871     ImGui::KeepAliveID(id);
1872     return id;
1873 }
1874
1875 ImGuiID ImGuiWindow::GetID(const void* ptr)
1876 {
1877     ImGuiID seed = IDStack.back();
1878     ImGuiID id = ImHash(&ptr, sizeof(void*), seed);
1879     ImGui::KeepAliveID(id);
1880     return id;
1881 }
1882
1883 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
1884 {
1885     ImGuiID seed = IDStack.back();
1886     return ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
1887 }
1888
1889 //-----------------------------------------------------------------------------
1890 // Internal API exposed in imgui_internal.h
1891 //-----------------------------------------------------------------------------
1892
1893 static void SetCurrentWindow(ImGuiWindow* window)
1894 {
1895     ImGuiContext& g = *GImGui;
1896     g.CurrentWindow = window;
1897     if (window)
1898         g.FontSize = window->CalcFontSize();
1899 }
1900
1901 ImGuiWindow* ImGui::GetParentWindow()
1902 {
1903     ImGuiContext& g = *GImGui;
1904     IM_ASSERT(g.CurrentWindowStack.Size >= 2);
1905     return g.CurrentWindowStack[(unsigned int)g.CurrentWindowStack.Size - 2];
1906 }
1907
1908 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
1909 {
1910     ImGuiContext& g = *GImGui;
1911     g.ActiveIdIsJustActivated = (g.ActiveId != id);
1912     if (g.ActiveIdIsJustActivated)
1913         g.ActiveIdTimer = 0.0f;
1914     g.ActiveId = id;
1915     g.ActiveIdAllowOverlap = false;
1916     g.ActiveIdIsAlive |= (id != 0);
1917     g.ActiveIdWindow = window;
1918 }
1919
1920 void ImGui::ClearActiveID()
1921 {
1922     SetActiveID(0, NULL);
1923 }
1924
1925 void ImGui::SetHoveredID(ImGuiID id)
1926 {
1927     ImGuiContext& g = *GImGui;
1928     g.HoveredId = id;
1929     g.HoveredIdAllowOverlap = false;
1930     g.HoveredIdTimer = (id != 0 && g.HoveredIdPreviousFrame == id) ? (g.HoveredIdTimer + g.IO.DeltaTime) : 0.0f;
1931 }
1932
1933 void ImGui::KeepAliveID(ImGuiID id)
1934 {
1935     ImGuiContext& g = *GImGui;
1936     if (g.ActiveId == id)
1937         g.ActiveIdIsAlive = true;
1938 }
1939
1940 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
1941 {
1942     // An active popup disable hovering on other windows (apart from its own children)
1943     // FIXME-OPT: This could be cached/stored within the window.
1944     ImGuiContext& g = *GImGui;
1945     if (g.NavWindow)
1946         if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
1947             if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
1948             {
1949                 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
1950                 // NB: The order of those two tests is important because Modal windows are also Popups.
1951                 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
1952                     return false;
1953                 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
1954                     return false;
1955             }
1956
1957     return true;
1958 }
1959
1960 // Advance cursor given item size for layout.
1961 void ImGui::ItemSize(const ImVec2& size, float text_offset_y)
1962 {
1963     ImGuiContext& g = *GImGui;
1964     ImGuiWindow* window = g.CurrentWindow;
1965     if (window->SkipItems)
1966         return;
1967
1968     // Always align ourselves on pixel boundaries
1969     const float line_height = ImMax(window->DC.CurrentLineHeight, size.y);
1970     const float text_base_offset = ImMax(window->DC.CurrentLineTextBaseOffset, text_offset_y);
1971     //if (g.IO.KeyAlt) window->DrawList->AddRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(size.x, line_height), IM_COL32(255,0,0,200)); // [DEBUG]
1972     window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y);
1973     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));
1974     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
1975     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
1976     //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
1977
1978     window->DC.PrevLineHeight = line_height;
1979     window->DC.PrevLineTextBaseOffset = text_base_offset;
1980     window->DC.CurrentLineHeight = window->DC.CurrentLineTextBaseOffset = 0.0f;
1981
1982     // Horizontal layout mode
1983     if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
1984         SameLine();
1985 }
1986
1987 void ImGui::ItemSize(const ImRect& bb, float text_offset_y)
1988 {
1989     ItemSize(bb.GetSize(), text_offset_y);
1990 }
1991
1992 // Declare item bounding box for clipping and interaction.
1993 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
1994 // declares their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd().
1995 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id)
1996 {
1997     ImGuiContext& g = *GImGui;
1998     ImGuiWindow* window = g.CurrentWindow;
1999     const bool is_clipped = IsClippedEx(bb, id, false);
2000     window->DC.LastItemId = id;
2001     window->DC.LastItemRect = bb;
2002     window->DC.LastItemRectHoveredRect = false;
2003     if (is_clipped)
2004         return false;
2005     //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
2006
2007     // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
2008     window->DC.LastItemRectHoveredRect = IsMouseHoveringRect(bb.Min, bb.Max);
2009     return true;
2010 }
2011
2012 // This is roughly matching the behavior of internal-facing ItemHoverable()
2013 // - we allow hovering to be true when ActiveId==window->MoveID, so that clicking on non-interactive items such as a Text() item still returns true with IsItemHovered()
2014 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
2015 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
2016 {
2017     ImGuiContext& g = *GImGui;
2018
2019     ImGuiWindow* window = g.CurrentWindow;
2020     if (!window->DC.LastItemRectHoveredRect)
2021         return false;
2022     IM_ASSERT((flags & ImGuiHoveredFlags_FlattenChilds) == 0);   // Flags not supported by this function
2023
2024     // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable to use IsItemHovered() after EndChild() itself.
2025     // Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was the test that has been running for a long while.
2026     //if (g.HoveredWindow != window)
2027     //    return false;
2028     if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
2029         return false;
2030     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
2031         if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
2032             return false;
2033     if (!IsWindowContentHoverable(window, flags))
2034         return false;
2035     if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
2036         return false;
2037     // Special handling for the 1st item after Begin() which represent the title bar. When the window is collapsed (SkipItems==true) that last item will never be overwritten.
2038     if (window->DC.LastItemId == window->MoveId && window->WriteAccessed)
2039         return false;
2040     return true;
2041 }
2042
2043 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
2044 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
2045 {
2046     ImGuiContext& g = *GImGui;
2047     if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
2048         return false;
2049
2050     ImGuiWindow* window = g.CurrentWindow;
2051     if (g.HoveredWindow != window)
2052         return false;
2053     if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
2054         return false;
2055     if (!IsMouseHoveringRect(bb.Min, bb.Max))
2056         return false;
2057     if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_Default))
2058         return false;
2059     if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
2060         return false;
2061
2062     SetHoveredID(id);
2063     return true;
2064 }
2065
2066 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
2067 {
2068     ImGuiContext& g = *GImGui;
2069     ImGuiWindow* window = g.CurrentWindow;
2070     if (!bb.Overlaps(window->ClipRect))
2071         if (id == 0 || id != g.ActiveId)
2072             if (clip_even_when_logged || !g.LogEnabled)
2073                 return true;
2074     return false;
2075 }
2076
2077 bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop)
2078 {
2079     ImGuiContext& g = *GImGui;
2080
2081     const bool allow_keyboard_focus = (window->DC.ItemFlags & (ImGuiItemFlags_AllowKeyboardFocus | ImGuiItemFlags_Disabled)) == ImGuiItemFlags_AllowKeyboardFocus;
2082     window->FocusIdxAllCounter++;
2083     if (allow_keyboard_focus)
2084         window->FocusIdxTabCounter++;
2085
2086     // Process keyboard input at this point: TAB/Shift-TAB to tab out of the currently focused item.
2087     // Note that we can always TAB out of a widget that doesn't allow tabbing in.
2088     if (tab_stop && (g.ActiveId == id) && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab))
2089         window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (allow_keyboard_focus ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items.
2090
2091     if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent)
2092         return true;
2093
2094     if (allow_keyboard_focus)
2095         if (window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent)
2096             return true;
2097
2098     return false;
2099 }
2100
2101 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
2102 {
2103     window->FocusIdxAllCounter--;
2104     window->FocusIdxTabCounter--;
2105 }
2106
2107 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_x, float default_y)
2108 {
2109     ImGuiContext& g = *GImGui;
2110     ImVec2 content_max;
2111     if (size.x < 0.0f || size.y < 0.0f)
2112         content_max = g.CurrentWindow->Pos + GetContentRegionMax();
2113     if (size.x <= 0.0f)
2114         size.x = (size.x == 0.0f) ? default_x : ImMax(content_max.x - g.CurrentWindow->DC.CursorPos.x, 4.0f) + size.x;
2115     if (size.y <= 0.0f)
2116         size.y = (size.y == 0.0f) ? default_y : ImMax(content_max.y - g.CurrentWindow->DC.CursorPos.y, 4.0f) + size.y;
2117     return size;
2118 }
2119
2120 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
2121 {
2122     if (wrap_pos_x < 0.0f)
2123         return 0.0f;
2124
2125     ImGuiWindow* window = GetCurrentWindowRead();
2126     if (wrap_pos_x == 0.0f)
2127         wrap_pos_x = GetContentRegionMax().x + window->Pos.x;
2128     else if (wrap_pos_x > 0.0f)
2129         wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
2130
2131     return ImMax(wrap_pos_x - pos.x, 1.0f);
2132 }
2133
2134 //-----------------------------------------------------------------------------
2135
2136 void* ImGui::MemAlloc(size_t sz)
2137 {
2138     GImGui->IO.MetricsAllocs++;
2139     return GImGui->IO.MemAllocFn(sz);
2140 }
2141
2142 void ImGui::MemFree(void* ptr)
2143 {
2144     if (ptr) GImGui->IO.MetricsAllocs--;
2145     return GImGui->IO.MemFreeFn(ptr);
2146 }
2147
2148 const char* ImGui::GetClipboardText()
2149 {
2150     return GImGui->IO.GetClipboardTextFn ? GImGui->IO.GetClipboardTextFn(GImGui->IO.ClipboardUserData) : "";
2151 }
2152
2153 void ImGui::SetClipboardText(const char* text)
2154 {
2155     if (GImGui->IO.SetClipboardTextFn)
2156         GImGui->IO.SetClipboardTextFn(GImGui->IO.ClipboardUserData, text);
2157 }
2158
2159 const char* ImGui::GetVersion()
2160 {
2161     return IMGUI_VERSION;
2162 }
2163
2164 // Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself
2165 // 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
2166 ImGuiContext* ImGui::GetCurrentContext()
2167 {
2168     return GImGui;
2169 }
2170
2171 void ImGui::SetCurrentContext(ImGuiContext* ctx)
2172 {
2173 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
2174     IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
2175 #else
2176     GImGui = ctx;
2177 #endif
2178 }
2179
2180 ImGuiContext* ImGui::CreateContext(void* (*malloc_fn)(size_t), void (*free_fn)(void*))
2181 {
2182     if (!malloc_fn) malloc_fn = malloc;
2183     ImGuiContext* ctx = (ImGuiContext*)malloc_fn(sizeof(ImGuiContext));
2184     IM_PLACEMENT_NEW(ctx) ImGuiContext();
2185     ctx->IO.MemAllocFn = malloc_fn;
2186     ctx->IO.MemFreeFn = free_fn ? free_fn : free;
2187     return ctx;
2188 }
2189
2190 void ImGui::DestroyContext(ImGuiContext* ctx)
2191 {
2192     void (*free_fn)(void*) = ctx->IO.MemFreeFn;
2193     ctx->~ImGuiContext();
2194     free_fn(ctx);
2195     if (GImGui == ctx)
2196         SetCurrentContext(NULL);
2197 }
2198
2199 ImGuiIO& ImGui::GetIO()
2200 {
2201     return GImGui->IO;
2202 }
2203
2204 ImGuiStyle& ImGui::GetStyle()
2205 {
2206     return GImGui->Style;
2207 }
2208
2209 // Same value as passed to your RenderDrawListsFn() function. valid after Render() and until the next call to NewFrame()
2210 ImDrawData* ImGui::GetDrawData()
2211 {
2212     return GImGui->RenderDrawData.Valid ? &GImGui->RenderDrawData : NULL;
2213 }
2214
2215 float ImGui::GetTime()
2216 {
2217     return GImGui->Time;
2218 }
2219
2220 int ImGui::GetFrameCount()
2221 {
2222     return GImGui->FrameCount;
2223 }
2224
2225 void ImGui::NewFrame()
2226 {
2227     ImGuiContext& g = *GImGui;
2228
2229     // Check user data
2230     // (We pass an error message in the assert expression as a trick to get it visible to programmers who are not using a debugger, as most assert handlers display their argument)
2231     IM_ASSERT(g.IO.DeltaTime >= 0.0f                                    && "Need a positive DeltaTime (zero is tolerated but will cause some timing issues)");
2232     IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f  && "Invalid DisplaySize value");
2233     IM_ASSERT(g.IO.Fonts->Fonts.Size > 0                                && "Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?");
2234     IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded()                          && "Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?");
2235     IM_ASSERT(g.Style.CurveTessellationTol > 0.0f                       && "Invalid style setting");
2236     IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f            && "Invalid style setting. Alpha cannot be negative (allows us to avoid a few clamps in color computations)");
2237     IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount)  && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
2238
2239     // Initialize on first frame
2240     if (!g.Initialized)
2241         ImGui::Initialize();
2242
2243     SetCurrentFont(GetDefaultFont());
2244     IM_ASSERT(g.Font->IsLoaded());
2245
2246     g.Time += g.IO.DeltaTime;
2247     g.FrameCount += 1;
2248     g.TooltipOverrideCount = 0;
2249     g.OverlayDrawList.Clear();
2250     g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID);
2251     g.OverlayDrawList.PushClipRectFullScreen();
2252
2253     // Mark rendering data as invalid to prevent user who may have a handle on it to use it
2254     g.RenderDrawData.Valid = false;
2255     g.RenderDrawData.CmdLists = NULL;
2256     g.RenderDrawData.CmdListsCount = g.RenderDrawData.TotalVtxCount = g.RenderDrawData.TotalIdxCount = 0;
2257
2258     // Clear reference to active widget if the widget isn't alive anymore
2259     if (!g.HoveredIdPreviousFrame)
2260         g.HoveredIdTimer = 0.0f;
2261     g.HoveredIdPreviousFrame = g.HoveredId;
2262     g.HoveredId = 0;
2263     g.HoveredIdAllowOverlap = false;
2264     if (!g.ActiveIdIsAlive && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
2265         ClearActiveID();
2266     if (g.ActiveId)
2267         g.ActiveIdTimer += g.IO.DeltaTime;
2268     g.ActiveIdPreviousFrame = g.ActiveId;
2269     g.ActiveIdIsAlive = false;
2270     g.ActiveIdIsJustActivated = false;
2271     if (g.ScalarAsInputTextId && g.ActiveId != g.ScalarAsInputTextId)
2272         g.ScalarAsInputTextId = 0;
2273
2274     // Update keyboard input state
2275     memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
2276     for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
2277         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;
2278
2279     // Update mouse input state
2280     // If mouse just appeared or disappeared (usually denoted by -FLT_MAX component, but in reality we test for -256000.0f) we cancel out movement in MouseDelta
2281     if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
2282         g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
2283     else
2284         g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
2285     g.IO.MousePosPrev = g.IO.MousePos;
2286     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
2287     {
2288         g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
2289         g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
2290         g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
2291         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;
2292         g.IO.MouseDoubleClicked[i] = false;
2293         if (g.IO.MouseClicked[i])
2294         {
2295             if (g.Time - g.IO.MouseClickedTime[i] < g.IO.MouseDoubleClickTime)
2296             {
2297                 if (ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i]) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
2298                     g.IO.MouseDoubleClicked[i] = true;
2299                 g.IO.MouseClickedTime[i] = -FLT_MAX;    // so the third click isn't turned into a double-click
2300             }
2301             else
2302             {
2303                 g.IO.MouseClickedTime[i] = g.Time;
2304             }
2305             g.IO.MouseClickedPos[i] = g.IO.MousePos;
2306             g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
2307             g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
2308         }
2309         else if (g.IO.MouseDown[i])
2310         {
2311             ImVec2 mouse_delta = g.IO.MousePos - g.IO.MouseClickedPos[i];
2312             g.IO.MouseDragMaxDistanceAbs[i].x = ImMax(g.IO.MouseDragMaxDistanceAbs[i].x, mouse_delta.x < 0.0f ? -mouse_delta.x : mouse_delta.x);
2313             g.IO.MouseDragMaxDistanceAbs[i].y = ImMax(g.IO.MouseDragMaxDistanceAbs[i].y, mouse_delta.y < 0.0f ? -mouse_delta.y : mouse_delta.y);
2314             g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(mouse_delta));
2315         }
2316     }
2317
2318     // Calculate frame-rate for the user, as a purely luxurious feature
2319     g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
2320     g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
2321     g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
2322     g.IO.Framerate = 1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame));
2323
2324     // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering). Only valid for root windows.
2325     if (g.MovingWindowMoveId && g.MovingWindowMoveId == g.ActiveId)
2326     {
2327         KeepAliveID(g.MovingWindowMoveId);
2328         IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
2329         IM_ASSERT(g.MovingWindow->MoveId == g.MovingWindowMoveId);
2330         if (g.IO.MouseDown[0])
2331         {
2332             g.MovingWindow->RootWindow->PosFloat += g.IO.MouseDelta;
2333             if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
2334                 MarkIniSettingsDirty(g.MovingWindow->RootWindow);
2335             FocusWindow(g.MovingWindow);
2336         }
2337         else
2338         {
2339             ClearActiveID();
2340             g.MovingWindow = NULL;
2341             g.MovingWindowMoveId = 0;
2342         }
2343     }
2344     else
2345     {
2346         g.MovingWindow = NULL;
2347         g.MovingWindowMoveId = 0;
2348     }
2349
2350     // Delay saving settings so we don't spam disk too much
2351     if (g.SettingsDirtyTimer > 0.0f)
2352     {
2353         g.SettingsDirtyTimer -= g.IO.DeltaTime;
2354         if (g.SettingsDirtyTimer <= 0.0f)
2355             SaveIniSettingsToDisk(g.IO.IniFilename);
2356     }
2357
2358     // Find the window we are hovering
2359     // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
2360     // - When moving a window we can skip the search, which also conveniently bypasses the fact that window->WindowRectClipped is lagging as this point.
2361     // - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms.
2362     g.HoveredWindow = (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoInputs)) ? g.MovingWindow : FindHoveredWindow(g.IO.MousePos);
2363     g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
2364
2365     if (ImGuiWindow* modal_window = GetFrontMostModalRootWindow())
2366     {
2367         g.ModalWindowDarkeningRatio = ImMin(g.ModalWindowDarkeningRatio + g.IO.DeltaTime * 6.0f, 1.0f);
2368         ImGuiWindow* window = g.HoveredRootWindow;
2369         while (window && window != modal_window)
2370             window = window->ParentWindow;
2371         if (!window)
2372             g.HoveredRootWindow = g.HoveredWindow = NULL;
2373     }
2374     else
2375     {
2376         g.ModalWindowDarkeningRatio = 0.0f;
2377     }
2378
2379     // Update the WantCaptureMouse/WantCAptureKeyboard flags, so user can capture/discard the inputs away from the rest of their application.
2380     // 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.
2381     int mouse_earliest_button_down = -1;
2382     bool mouse_any_down = false;
2383     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
2384     {
2385         if (g.IO.MouseClicked[i])
2386             g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty());
2387         mouse_any_down |= g.IO.MouseDown[i];
2388         if (g.IO.MouseDown[i])
2389             if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
2390                 mouse_earliest_button_down = i;
2391     }
2392     bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
2393     if (g.WantCaptureMouseNextFrame != -1)
2394         g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
2395     else
2396         g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty());
2397     g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != -1) ? (g.WantCaptureKeyboardNextFrame != 0) : (g.ActiveId != 0);
2398     g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : 0;
2399     g.MouseCursor = ImGuiMouseCursor_Arrow;
2400     g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
2401     g.OsImePosRequest = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
2402
2403     // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
2404     // FIXME: For patterns of drag and drop between "application" and "imgui" we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
2405     if (!mouse_avail_to_imgui)
2406         g.HoveredWindow = g.HoveredRootWindow = NULL;
2407
2408     // Scale & Scrolling
2409     if (g.HoveredWindow && g.IO.MouseWheel != 0.0f && !g.HoveredWindow->Collapsed)
2410     {
2411         ImGuiWindow* window = g.HoveredWindow;
2412         if (g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
2413         {
2414             // Zoom / Scale window
2415             const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
2416             const float scale = new_font_scale / window->FontWindowScale;
2417             window->FontWindowScale = new_font_scale;
2418
2419             const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
2420             window->Pos += offset;
2421             window->PosFloat += offset;
2422             window->Size *= scale;
2423             window->SizeFull *= scale;
2424         }
2425         else if (!g.IO.KeyCtrl && !(window->Flags & ImGuiWindowFlags_NoScrollWithMouse))
2426         {
2427             // Scroll
2428             const int scroll_lines = (window->Flags & ImGuiWindowFlags_ComboBox) ? 3 : 5;
2429             SetWindowScrollY(window, window->Scroll.y - g.IO.MouseWheel * window->CalcFontSize() * scroll_lines);
2430         }
2431     }
2432
2433     // Pressing TAB activate widget focus
2434     if (g.ActiveId == 0 && g.NavWindow != NULL && g.NavWindow->Active && IsKeyPressedMap(ImGuiKey_Tab, false))
2435         g.NavWindow->FocusIdxTabRequestNext = 0;
2436
2437     // Mark all windows as not visible
2438     for (int i = 0; i != g.Windows.Size; i++)
2439     {
2440         ImGuiWindow* window = g.Windows[i];
2441         window->WasActive = window->Active;
2442         window->Active = false;
2443         window->WriteAccessed = false;
2444     }
2445
2446     // Closing the focused window restore focus to the first active root window in descending z-order
2447     if (g.NavWindow && !g.NavWindow->WasActive)
2448         FocusPreviousWindow();
2449
2450     // No window should be open at the beginning of the frame.
2451     // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
2452     g.CurrentWindowStack.resize(0);
2453     g.CurrentPopupStack.resize(0);
2454     CloseInactivePopups(g.NavWindow);
2455
2456     // Create implicit window - we will only render it if the user has added something to it.
2457     // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
2458     ImGui::SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver);
2459     ImGui::Begin("Debug##Default");
2460 }
2461
2462 void ImGui::Initialize()
2463 {
2464     ImGuiContext& g = *GImGui;
2465     g.LogClipboard = (ImGuiTextBuffer*)ImGui::MemAlloc(sizeof(ImGuiTextBuffer));
2466     IM_PLACEMENT_NEW(g.LogClipboard) ImGuiTextBuffer();
2467
2468     IM_ASSERT(g.Settings.empty());
2469     LoadIniSettingsFromDisk(g.IO.IniFilename);
2470     g.Initialized = true;
2471 }
2472
2473 // This function is merely here to free heap allocations.
2474 void ImGui::Shutdown()
2475 {
2476     ImGuiContext& g = *GImGui;
2477
2478     // 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)
2479     if (g.IO.Fonts) // Testing for NULL to allow user to NULLify in case of running Shutdown() on multiple contexts. Bit hacky.
2480         g.IO.Fonts->Clear();
2481
2482     // Cleanup of other data are conditional on actually having initialize ImGui.
2483     if (!g.Initialized)
2484         return;
2485
2486     SaveIniSettingsToDisk(g.IO.IniFilename);
2487
2488     for (int i = 0; i < g.Windows.Size; i++)
2489     {
2490         g.Windows[i]->~ImGuiWindow();
2491         ImGui::MemFree(g.Windows[i]);
2492     }
2493     g.Windows.clear();
2494     g.WindowsSortBuffer.clear();
2495     g.CurrentWindow = NULL;
2496     g.CurrentWindowStack.clear();
2497     g.WindowsById.Clear();
2498     g.NavWindow = NULL;
2499     g.HoveredWindow = NULL;
2500     g.HoveredRootWindow = NULL;
2501     g.ActiveIdWindow = NULL;
2502     g.MovingWindow = NULL;
2503     for (int i = 0; i < g.Settings.Size; i++)
2504         ImGui::MemFree(g.Settings[i].Name);
2505     g.Settings.clear();
2506     g.ColorModifiers.clear();
2507     g.StyleModifiers.clear();
2508     g.FontStack.clear();
2509     g.OpenPopupStack.clear();
2510     g.CurrentPopupStack.clear();
2511     g.SetNextWindowSizeConstraintCallback = NULL;
2512     g.SetNextWindowSizeConstraintCallbackUserData = NULL;
2513     for (int i = 0; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
2514         g.RenderDrawLists[i].clear();
2515     g.OverlayDrawList.ClearFreeMemory();
2516     g.PrivateClipboard.clear();
2517     g.InputTextState.Text.clear();
2518     g.InputTextState.InitialText.clear();
2519     g.InputTextState.TempTextBuffer.clear();
2520
2521     if (g.LogFile && g.LogFile != stdout)
2522     {
2523         fclose(g.LogFile);
2524         g.LogFile = NULL;
2525     }
2526     if (g.LogClipboard)
2527     {
2528         g.LogClipboard->~ImGuiTextBuffer();
2529         ImGui::MemFree(g.LogClipboard);
2530     }
2531
2532     g.Initialized = false;
2533 }
2534
2535 static ImGuiIniData* FindWindowSettings(const char* name)
2536 {
2537     ImGuiContext& g = *GImGui;
2538     ImGuiID id = ImHash(name, 0);
2539     for (int i = 0; i != g.Settings.Size; i++)
2540     {
2541         ImGuiIniData* ini = &g.Settings[i];
2542         if (ini->Id == id)
2543             return ini;
2544     }
2545     return NULL;
2546 }
2547
2548 static ImGuiIniData* AddWindowSettings(const char* name)
2549 {
2550     GImGui->Settings.resize(GImGui->Settings.Size + 1);
2551     ImGuiIniData* ini = &GImGui->Settings.back();
2552     ini->Name = ImStrdup(name);
2553     ini->Id = ImHash(name, 0);
2554     ini->Collapsed = false;
2555     ini->Pos = ImVec2(FLT_MAX,FLT_MAX);
2556     ini->Size = ImVec2(0,0);
2557     return ini;
2558 }
2559
2560 // Zero-tolerance, poor-man .ini parsing
2561 // FIXME: Write something less rubbish
2562 static void LoadIniSettingsFromDisk(const char* ini_filename)
2563 {
2564     ImGuiContext& g = *GImGui;
2565     if (!ini_filename)
2566         return;
2567
2568     int file_size;
2569     char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_size, 1);
2570     if (!file_data)
2571         return;
2572
2573     ImGuiIniData* settings = NULL;
2574     const char* buf_end = file_data + file_size;
2575     for (const char* line_start = file_data; line_start < buf_end; )
2576     {
2577         const char* line_end = line_start;
2578         while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
2579             line_end++;
2580
2581         if (line_start[0] == '[' && line_end > line_start && line_end[-1] == ']')
2582         {
2583             char name[64];
2584             ImFormatString(name, IM_ARRAYSIZE(name), "%.*s", (int)(line_end-line_start-2), line_start+1);
2585             settings = FindWindowSettings(name);
2586             if (!settings)
2587                 settings = AddWindowSettings(name);
2588         }
2589         else if (settings)
2590         {
2591             float x, y;
2592             int i;
2593             if (sscanf(line_start, "Pos=%f,%f", &x, &y) == 2)
2594                 settings->Pos = ImVec2(x, y);
2595             else if (sscanf(line_start, "Size=%f,%f", &x, &y) == 2)
2596                 settings->Size = ImMax(ImVec2(x, y), g.Style.WindowMinSize);
2597             else if (sscanf(line_start, "Collapsed=%d", &i) == 1)
2598                 settings->Collapsed = (i != 0);
2599         }
2600
2601         line_start = line_end+1;
2602     }
2603
2604     ImGui::MemFree(file_data);
2605 }
2606
2607 static void SaveIniSettingsToDisk(const char* ini_filename)
2608 {
2609     ImGuiContext& g = *GImGui;
2610     g.SettingsDirtyTimer = 0.0f;
2611     if (!ini_filename)
2612         return;
2613
2614     // Gather data from windows that were active during this session
2615     for (int i = 0; i != g.Windows.Size; i++)
2616     {
2617         ImGuiWindow* window = g.Windows[i];
2618         if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
2619             continue;
2620         ImGuiIniData* settings = FindWindowSettings(window->Name);
2621         if (!settings)  // This will only return NULL in the rare instance where the window was first created with ImGuiWindowFlags_NoSavedSettings then had the flag disabled later on. We don't bind settings in this case (bug #1000).
2622             continue;
2623         settings->Pos = window->Pos;
2624         settings->Size = window->SizeFull;
2625         settings->Collapsed = window->Collapsed;
2626     }
2627
2628     // Write .ini file
2629     // If a window wasn't opened in this session we preserve its settings
2630     FILE* f = ImFileOpen(ini_filename, "wt");
2631     if (!f)
2632         return;
2633     for (int i = 0; i != g.Settings.Size; i++)
2634     {
2635         const ImGuiIniData* settings = &g.Settings[i];
2636         if (settings->Pos.x == FLT_MAX)
2637             continue;
2638         const char* name = settings->Name;
2639         if (const char* p = strstr(name, "###"))  // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
2640             name = p;
2641         fprintf(f, "[%s]\n", name);
2642         fprintf(f, "Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y);
2643         fprintf(f, "Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y);
2644         fprintf(f, "Collapsed=%d\n", settings->Collapsed);
2645         fprintf(f, "\n");
2646     }
2647
2648     fclose(f);
2649 }
2650
2651 static void MarkIniSettingsDirty(ImGuiWindow* window)
2652 {
2653     ImGuiContext& g = *GImGui;
2654     if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
2655         if (g.SettingsDirtyTimer <= 0.0f)
2656             g.SettingsDirtyTimer = g.IO.IniSavingRate;
2657 }
2658
2659 // FIXME: Add a more explicit sort order in the window structure.
2660 static int ChildWindowComparer(const void* lhs, const void* rhs)
2661 {
2662     const ImGuiWindow* a = *(const ImGuiWindow**)lhs;
2663     const ImGuiWindow* b = *(const ImGuiWindow**)rhs;
2664     if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
2665         return d;
2666     if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
2667         return d;
2668     if (int d = (a->Flags & ImGuiWindowFlags_ComboBox) - (b->Flags & ImGuiWindowFlags_ComboBox))
2669         return d;
2670     return (a->OrderWithinParent - b->OrderWithinParent);
2671 }
2672
2673 static void AddWindowToSortedBuffer(ImVector<ImGuiWindow*>& out_sorted_windows, ImGuiWindow* window)
2674 {
2675     out_sorted_windows.push_back(window);
2676     if (window->Active)
2677     {
2678         int count = window->DC.ChildWindows.Size;
2679         if (count > 1)
2680             qsort(window->DC.ChildWindows.begin(), (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
2681         for (int i = 0; i < count; i++)
2682         {
2683             ImGuiWindow* child = window->DC.ChildWindows[i];
2684             if (child->Active)
2685                 AddWindowToSortedBuffer(out_sorted_windows, child);
2686         }
2687     }
2688 }
2689
2690 static void AddDrawListToRenderList(ImVector<ImDrawList*>& out_render_list, ImDrawList* draw_list)
2691 {
2692     if (draw_list->CmdBuffer.empty())
2693         return;
2694
2695     // Remove trailing command if unused
2696     ImDrawCmd& last_cmd = draw_list->CmdBuffer.back();
2697     if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL)
2698     {
2699         draw_list->CmdBuffer.pop_back();
2700         if (draw_list->CmdBuffer.empty())
2701             return;
2702     }
2703
2704     // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. May trigger for you if you are using PrimXXX functions incorrectly.
2705     IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
2706     IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
2707     IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
2708
2709     // Check that draw_list doesn't use more vertices than indexable in a single draw call (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per window)
2710     // If this assert triggers because you are drawing lots of stuff manually, you can:
2711     // A) Add '#define ImDrawIdx unsigned int' in imconfig.h to set the index size to 4 bytes. You'll need to handle the 4-bytes indices to your renderer.
2712     //    For example, the OpenGL example code detect index size at compile-time by doing:
2713     //    'glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);'
2714     //    Your own engine or render API may use different parameters or function calls to specify index sizes. 2 and 4 bytes indices are generally supported by most API.
2715     // B) If for some reason you cannot use 4 bytes indices or don't want to, a workaround is to call BeginChild()/EndChild() before reaching the 64K limit to split your draw commands in multiple draw lists.
2716     IM_ASSERT(((ImU64)draw_list->_VtxCurrentIdx >> (sizeof(ImDrawIdx)*8)) == 0);  // Too many vertices in same ImDrawList. See comment above.
2717     
2718     out_render_list.push_back(draw_list);
2719     GImGui->IO.MetricsRenderVertices += draw_list->VtxBuffer.Size;
2720     GImGui->IO.MetricsRenderIndices += draw_list->IdxBuffer.Size;
2721 }
2722
2723 static void AddWindowToRenderList(ImVector<ImDrawList*>& out_render_list, ImGuiWindow* window)
2724 {
2725     AddDrawListToRenderList(out_render_list, window->DrawList);
2726     for (int i = 0; i < window->DC.ChildWindows.Size; i++)
2727     {
2728         ImGuiWindow* child = window->DC.ChildWindows[i];
2729         if (!child->Active) // clipped children may have been marked not active
2730             continue;
2731         if ((child->Flags & ImGuiWindowFlags_Popup) && child->HiddenFrames > 0)
2732             continue;
2733         AddWindowToRenderList(out_render_list, child);
2734     }
2735 }
2736
2737 static void AddWindowToRenderListSelectLayer(ImGuiWindow* window)
2738 {
2739     // FIXME: Generalize this with a proper layering system so e.g. user can draw in specific layers, below text, ..
2740     ImGuiContext& g = *GImGui;
2741     g.IO.MetricsActiveWindows++;
2742     if (window->Flags & ImGuiWindowFlags_Popup)
2743         AddWindowToRenderList(g.RenderDrawLists[1], window);
2744     else if (window->Flags & ImGuiWindowFlags_Tooltip)
2745         AddWindowToRenderList(g.RenderDrawLists[2], window);
2746     else
2747         AddWindowToRenderList(g.RenderDrawLists[0], window);
2748 }
2749
2750 // 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.
2751 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
2752 {
2753     ImGuiWindow* window = GetCurrentWindow();
2754     window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
2755     window->ClipRect = window->DrawList->_ClipRectStack.back();
2756 }
2757
2758 void ImGui::PopClipRect()
2759 {
2760     ImGuiWindow* window = GetCurrentWindow();
2761     window->DrawList->PopClipRect();
2762     window->ClipRect = window->DrawList->_ClipRectStack.back();
2763 }
2764
2765 // 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.
2766 void ImGui::EndFrame()
2767 {
2768     ImGuiContext& g = *GImGui;
2769     IM_ASSERT(g.Initialized);                       // Forgot to call ImGui::NewFrame()
2770     if (g.FrameCountEnded == g.FrameCount)          // Don't process EndFrame() multiple times.
2771         return;
2772
2773     // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
2774     if (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.OsImePosRequest - g.OsImePosSet) > 0.0001f)
2775     {
2776         g.IO.ImeSetInputScreenPosFn((int)g.OsImePosRequest.x, (int)g.OsImePosRequest.y);
2777         g.OsImePosSet = g.OsImePosRequest;
2778     }
2779
2780     // Hide implicit "Debug" window if it hasn't been used
2781     IM_ASSERT(g.CurrentWindowStack.Size == 1);    // Mismatched Begin()/End() calls
2782     if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
2783         g.CurrentWindow->Active = false;
2784     ImGui::End();
2785
2786     if (g.ActiveId == 0 && g.HoveredId == 0)
2787     {
2788         if (!g.NavWindow || !g.NavWindow->Appearing) // Unless we just made a window/popup appear
2789         {
2790             // Click to focus window and start moving (after we're done with all our widgets)
2791             if (g.IO.MouseClicked[0])
2792             {
2793                 if (g.HoveredRootWindow != NULL)
2794                 {
2795                     FocusWindow(g.HoveredWindow);
2796                     if (!(g.HoveredWindow->Flags & ImGuiWindowFlags_NoMove) && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoMove))
2797                     {
2798                         g.MovingWindow = g.HoveredWindow;
2799                         g.MovingWindowMoveId = g.MovingWindow->MoveId;
2800                         SetActiveID(g.MovingWindowMoveId, g.HoveredRootWindow);
2801                     }
2802                 }
2803                 else if (g.NavWindow != NULL && GetFrontMostModalRootWindow() == NULL)
2804                 {
2805                     // Clicking on void disable focus
2806                     FocusWindow(NULL);
2807                 }
2808             }
2809
2810             // With right mouse button we close popups without changing focus
2811             // (The left mouse button path calls FocusWindow which will lead NewFrame->CloseInactivePopups to trigger)
2812             if (g.IO.MouseClicked[1])
2813             {
2814                 // Find the top-most window between HoveredWindow and the front most Modal Window.
2815                 // This is where we can trim the popup stack.
2816                 ImGuiWindow* modal = GetFrontMostModalRootWindow();
2817                 bool hovered_window_above_modal = false;
2818                 if (modal == NULL)
2819                     hovered_window_above_modal = true;
2820                 for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--)
2821                 {
2822                     ImGuiWindow* window = g.Windows[i];
2823                     if (window == modal)
2824                         break;
2825                     if (window == g.HoveredWindow)
2826                         hovered_window_above_modal = true;
2827                 }
2828                 CloseInactivePopups(hovered_window_above_modal ? g.HoveredWindow : modal);
2829             }
2830         }
2831     }
2832
2833     // Sort the window list so that all child windows are after their parent
2834     // We cannot do that on FocusWindow() because childs may not exist yet
2835     g.WindowsSortBuffer.resize(0);
2836     g.WindowsSortBuffer.reserve(g.Windows.Size);
2837     for (int i = 0; i != g.Windows.Size; i++)
2838     {
2839         ImGuiWindow* window = g.Windows[i];
2840         if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow))       // if a child is active its parent will add it
2841             continue;
2842         AddWindowToSortedBuffer(g.WindowsSortBuffer, window);
2843     }
2844
2845     IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size);  // we done something wrong
2846     g.Windows.swap(g.WindowsSortBuffer);
2847
2848     // Clear Input data for next frame
2849     g.IO.MouseWheel = 0.0f;
2850     memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters));
2851
2852     g.FrameCountEnded = g.FrameCount;
2853 }
2854
2855 void ImGui::Render()
2856 {
2857     ImGuiContext& g = *GImGui;
2858     IM_ASSERT(g.Initialized);   // Forgot to call ImGui::NewFrame()
2859
2860     if (g.FrameCountEnded != g.FrameCount)
2861         ImGui::EndFrame();
2862     g.FrameCountRendered = g.FrameCount;
2863
2864     // Skip render altogether if alpha is 0.0
2865     // 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.
2866     if (g.Style.Alpha > 0.0f)
2867     {
2868         // Gather windows to render
2869         g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsActiveWindows = 0;
2870         for (int i = 0; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
2871             g.RenderDrawLists[i].resize(0);
2872         for (int i = 0; i != g.Windows.Size; i++)
2873         {
2874             ImGuiWindow* window = g.Windows[i];
2875             if (window->Active && window->HiddenFrames <= 0 && (window->Flags & (ImGuiWindowFlags_ChildWindow)) == 0)
2876                 AddWindowToRenderListSelectLayer(window);
2877         }
2878
2879         // Flatten layers
2880         int n = g.RenderDrawLists[0].Size;
2881         int flattened_size = n;
2882         for (int i = 1; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
2883             flattened_size += g.RenderDrawLists[i].Size;
2884         g.RenderDrawLists[0].resize(flattened_size);
2885         for (int i = 1; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
2886         {
2887             ImVector<ImDrawList*>& layer = g.RenderDrawLists[i];
2888             if (layer.empty())
2889                 continue;
2890             memcpy(&g.RenderDrawLists[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
2891             n += layer.Size;
2892         }
2893
2894         // Draw software mouse cursor if requested
2895         if (g.IO.MouseDrawCursor)
2896         {
2897             const ImGuiMouseCursorData& cursor_data = g.MouseCursorData[g.MouseCursor];
2898             const ImVec2 pos = g.IO.MousePos - cursor_data.HotOffset;
2899             const ImVec2 size = cursor_data.Size;
2900             const ImTextureID tex_id = g.IO.Fonts->TexID;
2901             g.OverlayDrawList.PushTextureID(tex_id);
2902             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
2903             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
2904             g.OverlayDrawList.AddImage(tex_id, pos,             pos + size,             cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], IM_COL32(0,0,0,255));       // Black border
2905             g.OverlayDrawList.AddImage(tex_id, pos,             pos + size,             cursor_data.TexUvMin[0], cursor_data.TexUvMax[0], IM_COL32(255,255,255,255)); // White fill
2906             g.OverlayDrawList.PopTextureID();
2907         }
2908         if (!g.OverlayDrawList.VtxBuffer.empty())
2909             AddDrawListToRenderList(g.RenderDrawLists[0], &g.OverlayDrawList);
2910
2911         // Setup draw data
2912         g.RenderDrawData.Valid = true;
2913         g.RenderDrawData.CmdLists = (g.RenderDrawLists[0].Size > 0) ? &g.RenderDrawLists[0][0] : NULL;
2914         g.RenderDrawData.CmdListsCount = g.RenderDrawLists[0].Size;
2915         g.RenderDrawData.TotalVtxCount = g.IO.MetricsRenderVertices;
2916         g.RenderDrawData.TotalIdxCount = g.IO.MetricsRenderIndices;
2917
2918         // Render. If user hasn't set a callback then they may retrieve the draw data via GetDrawData()
2919         if (g.RenderDrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)
2920             g.IO.RenderDrawListsFn(&g.RenderDrawData);
2921     }
2922 }
2923
2924 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2925 {
2926     const char* text_display_end = text;
2927     if (!text_end)
2928         text_end = (const char*)-1;
2929
2930     while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2931         text_display_end++;
2932     return text_display_end;
2933 }
2934
2935 // Pass text data straight to log (without being displayed)
2936 void ImGui::LogText(const char* fmt, ...)
2937 {
2938     ImGuiContext& g = *GImGui;
2939     if (!g.LogEnabled)
2940         return;
2941
2942     va_list args;
2943     va_start(args, fmt);
2944     if (g.LogFile)
2945     {
2946         vfprintf(g.LogFile, fmt, args);
2947     }
2948     else
2949     {
2950         g.LogClipboard->appendv(fmt, args);
2951     }
2952     va_end(args);
2953 }
2954
2955 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
2956 // We split text into individual lines to add current tree level padding
2957 static void LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end = NULL)
2958 {
2959     ImGuiContext& g = *GImGui;
2960     ImGuiWindow* window = g.CurrentWindow;
2961
2962     if (!text_end)
2963         text_end = ImGui::FindRenderedTextEnd(text, text_end);
2964
2965     const bool log_new_line = ref_pos && (ref_pos->y > window->DC.LogLinePosY + 1);
2966     if (ref_pos)
2967         window->DC.LogLinePosY = ref_pos->y;
2968
2969     const char* text_remaining = text;
2970     if (g.LogStartDepth > window->DC.TreeDepth)  // Re-adjust padding if we have popped out of our starting depth
2971         g.LogStartDepth = window->DC.TreeDepth;
2972     const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth);
2973     for (;;)
2974     {
2975         // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
2976         const char* line_end = text_remaining;
2977         while (line_end < text_end)
2978             if (*line_end == '\n')
2979                 break;
2980             else
2981                 line_end++;
2982         if (line_end >= text_end)
2983             line_end = NULL;
2984
2985         const bool is_first_line = (text == text_remaining);
2986         bool is_last_line = false;
2987         if (line_end == NULL)
2988         {
2989             is_last_line = true;
2990             line_end = text_end;
2991         }
2992         if (line_end != NULL && !(is_last_line && (line_end - text_remaining)==0))
2993         {
2994             const int char_count = (int)(line_end - text_remaining);
2995             if (log_new_line || !is_first_line)
2996                 ImGui::LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, text_remaining);
2997             else
2998                 ImGui::LogText(" %.*s", char_count, text_remaining);
2999         }
3000
3001         if (is_last_line)
3002             break;
3003         text_remaining = line_end + 1;
3004     }
3005 }
3006
3007 // Internal ImGui functions to render text
3008 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
3009 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
3010 {
3011     ImGuiContext& g = *GImGui;
3012     ImGuiWindow* window = g.CurrentWindow;
3013
3014     // Hide anything after a '##' string
3015     const char* text_display_end;
3016     if (hide_text_after_hash)
3017     {
3018         text_display_end = FindRenderedTextEnd(text, text_end);
3019     }
3020     else
3021     {
3022         if (!text_end)
3023             text_end = text + strlen(text); // FIXME-OPT
3024         text_display_end = text_end;
3025     }
3026
3027     const int text_len = (int)(text_display_end - text);
3028     if (text_len > 0)
3029     {
3030         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
3031         if (g.LogEnabled)
3032             LogRenderedText(&pos, text, text_display_end);
3033     }
3034 }
3035
3036 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
3037 {
3038     ImGuiContext& g = *GImGui;
3039     ImGuiWindow* window = g.CurrentWindow;
3040
3041     if (!text_end)
3042         text_end = text + strlen(text); // FIXME-OPT
3043
3044     const int text_len = (int)(text_end - text);
3045     if (text_len > 0)
3046     {
3047         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
3048         if (g.LogEnabled)
3049             LogRenderedText(&pos, text, text_end);
3050     }
3051 }
3052
3053 // Default clip_rect uses (pos_min,pos_max)
3054 // Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
3055 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)
3056 {
3057     // Hide anything after a '##' string
3058     const char* text_display_end = FindRenderedTextEnd(text, text_end);
3059     const int text_len = (int)(text_display_end - text);
3060     if (text_len == 0)
3061         return;
3062
3063     ImGuiContext& g = *GImGui;
3064     ImGuiWindow* window = g.CurrentWindow;
3065
3066     // Perform CPU side clipping for single clipped element to avoid using scissor state
3067     ImVec2 pos = pos_min;
3068     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
3069
3070     const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
3071     const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
3072     bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
3073     if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
3074         need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
3075
3076     // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
3077     if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
3078     if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
3079
3080     // Render
3081     if (need_clipping)
3082     {
3083         ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
3084         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
3085     }
3086     else
3087     {
3088         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
3089     }
3090     if (g.LogEnabled)
3091         LogRenderedText(&pos, text, text_display_end);
3092 }
3093
3094 // Render a rectangle shaped with optional rounding and borders
3095 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
3096 {
3097     ImGuiContext& g = *GImGui;
3098     ImGuiWindow* window = g.CurrentWindow;
3099     window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
3100     const float border_size = g.Style.FrameBorderSize;
3101     if (border && border_size > 0.0f)
3102     {
3103         window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
3104         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
3105     }
3106 }
3107
3108 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
3109 {
3110     ImGuiContext& g = *GImGui;
3111     ImGuiWindow* window = g.CurrentWindow;
3112     const float border_size = g.Style.FrameBorderSize;
3113     if (border_size > 0.0f)
3114     {
3115         window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
3116         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
3117     }
3118 }
3119
3120 // Render a triangle to denote expanded/collapsed state
3121 void ImGui::RenderTriangle(ImVec2 p_min, ImGuiDir dir, float scale)
3122 {
3123     ImGuiContext& g = *GImGui;
3124     ImGuiWindow* window = g.CurrentWindow;
3125
3126     const float h = g.FontSize * 1.00f;
3127     float r = h * 0.40f * scale;
3128     ImVec2 center = p_min + ImVec2(h * 0.50f, h * 0.50f * scale);
3129
3130     ImVec2 a, b, c;
3131     switch (dir)
3132     {
3133     case ImGuiDir_Up:
3134         r = -r; // ...fall through, no break!
3135     case ImGuiDir_Down:
3136         center.y -= r * 0.25f;
3137         a = ImVec2(0,1) * r;
3138         b = ImVec2(-0.866f,-0.5f) * r;
3139         c = ImVec2(+0.866f,-0.5f) * r;
3140         break;
3141     case ImGuiDir_Left:
3142         r = -r; // ...fall through, no break!
3143     case ImGuiDir_Right:
3144         center.x -= r * 0.25f;
3145         a = ImVec2(1,0) * r;
3146         b = ImVec2(-0.500f,+0.866f) * r;
3147         c = ImVec2(-0.500f,-0.866f) * r;
3148         break;
3149     default:
3150         IM_ASSERT(0);
3151         break;
3152     }
3153
3154     window->DrawList->AddTriangleFilled(center + a, center + b, center + c, GetColorU32(ImGuiCol_Text));
3155 }
3156
3157 void ImGui::RenderBullet(ImVec2 pos)
3158 {
3159     ImGuiContext& g = *GImGui;
3160     ImGuiWindow* window = g.CurrentWindow;
3161     window->DrawList->AddCircleFilled(pos, GImGui->FontSize*0.20f, GetColorU32(ImGuiCol_Text), 8);
3162 }
3163
3164 void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz)
3165 {
3166     ImGuiContext& g = *GImGui;
3167     ImGuiWindow* window = g.CurrentWindow;
3168
3169     float thickness = ImMax(sz / 5.0f, 1.0f);
3170     sz -= thickness*0.5f;
3171     pos += ImVec2(thickness*0.25f, thickness*0.25f);
3172
3173     float third = sz / 3.0f;
3174     float bx = pos.x + third;
3175     float by = pos.y + sz - third*0.5f;
3176     window->DrawList->PathLineTo(ImVec2(bx - third, by - third));
3177     window->DrawList->PathLineTo(ImVec2(bx, by));
3178     window->DrawList->PathLineTo(ImVec2(bx + third*2, by - third*2));
3179     window->DrawList->PathStroke(col, false, thickness);
3180 }
3181
3182 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
3183 // CalcTextSize("") should return ImVec2(0.0f, GImGui->FontSize)
3184 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
3185 {
3186     ImGuiContext& g = *GImGui;
3187
3188     const char* text_display_end;
3189     if (hide_text_after_double_hash)
3190         text_display_end = FindRenderedTextEnd(text, text_end);      // Hide anything after a '##' string
3191     else
3192         text_display_end = text_end;
3193
3194     ImFont* font = g.Font;
3195     const float font_size = g.FontSize;
3196     if (text == text_display_end)
3197         return ImVec2(0.0f, font_size);
3198     ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
3199
3200     // Cancel out character spacing for the last character of a line (it is baked into glyph->AdvanceX field)
3201     const float font_scale = font_size / font->FontSize;
3202     const float character_spacing_x = 1.0f * font_scale;
3203     if (text_size.x > 0.0f)
3204         text_size.x -= character_spacing_x;
3205     text_size.x = (float)(int)(text_size.x + 0.95f);
3206
3207     return text_size;
3208 }
3209
3210 // Helper to calculate coarse clipping of large list of evenly sized items.
3211 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
3212 // NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX
3213 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
3214 {
3215     ImGuiContext& g = *GImGui;
3216     ImGuiWindow* window = g.CurrentWindow;
3217     if (g.LogEnabled)
3218     {
3219         // If logging is active, do not perform any clipping
3220         *out_items_display_start = 0;
3221         *out_items_display_end = items_count;
3222         return;
3223     }
3224     if (window->SkipItems)
3225     {
3226         *out_items_display_start = *out_items_display_end = 0;
3227         return;
3228     }
3229
3230     const ImVec2 pos = window->DC.CursorPos;
3231     int start = (int)((window->ClipRect.Min.y - pos.y) / items_height);
3232     int end = (int)((window->ClipRect.Max.y - pos.y) / items_height);
3233     start = ImClamp(start, 0, items_count);
3234     end = ImClamp(end + 1, start, items_count);
3235     *out_items_display_start = start;
3236     *out_items_display_end = end;
3237 }
3238
3239 // Find window given position, search front-to-back
3240 // 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.
3241 static ImGuiWindow* FindHoveredWindow(ImVec2 pos)
3242 {
3243     ImGuiContext& g = *GImGui;
3244     for (int i = g.Windows.Size-1; i >= 0; i--)
3245     {
3246         ImGuiWindow* window = g.Windows[i];
3247         if (!window->Active)
3248             continue;
3249         if (window->Flags & ImGuiWindowFlags_NoInputs)
3250             continue;
3251
3252         // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
3253         ImRect bb(window->WindowRectClipped.Min - g.Style.TouchExtraPadding, window->WindowRectClipped.Max + g.Style.TouchExtraPadding);
3254         if (bb.Contains(pos))
3255             return window;
3256     }
3257     return NULL;
3258 }
3259
3260 // Test if mouse cursor is hovering given rectangle
3261 // NB- Rectangle is clipped by our current clip setting
3262 // NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
3263 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
3264 {
3265     ImGuiContext& g = *GImGui;
3266     ImGuiWindow* window = g.CurrentWindow;
3267
3268     // Clip
3269     ImRect rect_clipped(r_min, r_max);
3270     if (clip)
3271         rect_clipped.ClipWith(window->ClipRect);
3272
3273     // Expand for touch input
3274     const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
3275     return rect_for_touch.Contains(g.IO.MousePos);
3276 }
3277
3278 bool ImGui::IsAnyWindowHovered()
3279 {
3280     ImGuiContext& g = *GImGui;
3281     return g.HoveredWindow != NULL;
3282 }
3283
3284 static bool IsKeyPressedMap(ImGuiKey key, bool repeat)
3285 {
3286     const int key_index = GImGui->IO.KeyMap[key];
3287     return (key_index >= 0) ? ImGui::IsKeyPressed(key_index, repeat) : false;
3288 }
3289
3290 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
3291 {
3292     IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
3293     return GImGui->IO.KeyMap[imgui_key];
3294 }
3295
3296 // 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[]!
3297 bool ImGui::IsKeyDown(int user_key_index)
3298 {
3299     if (user_key_index < 0) return false;
3300     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(GImGui->IO.KeysDown));
3301     return GImGui->IO.KeysDown[user_key_index];
3302 }
3303
3304 int ImGui::CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate)
3305 {
3306     if (t == 0.0f)
3307         return 1;
3308     if (t <= repeat_delay || repeat_rate <= 0.0f)
3309         return 0;
3310     const int count = (int)((t - repeat_delay) / repeat_rate) - (int)((t_prev - repeat_delay) / repeat_rate);
3311     return (count > 0) ? count : 0;
3312 }
3313
3314 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
3315 {
3316     ImGuiContext& g = *GImGui;
3317     if (key_index < 0) return false;
3318     IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3319     const float t = g.IO.KeysDownDuration[key_index];
3320     return CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, repeat_delay, repeat_rate);
3321 }
3322
3323 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
3324 {
3325     ImGuiContext& g = *GImGui;
3326     if (user_key_index < 0) return false;
3327     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3328     const float t = g.IO.KeysDownDuration[user_key_index];
3329     if (t == 0.0f)
3330         return true;
3331     if (repeat && t > g.IO.KeyRepeatDelay)
3332         return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
3333     return false;
3334 }
3335
3336 bool ImGui::IsKeyReleased(int user_key_index)
3337 {
3338     ImGuiContext& g = *GImGui;
3339     if (user_key_index < 0) return false;
3340     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3341     if (g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index])
3342         return true;
3343     return false;
3344 }
3345
3346 bool ImGui::IsMouseDown(int button)
3347 {
3348     ImGuiContext& g = *GImGui;
3349     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3350     return g.IO.MouseDown[button];
3351 }
3352
3353 bool ImGui::IsMouseClicked(int button, bool repeat)
3354 {
3355     ImGuiContext& g = *GImGui;
3356     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3357     const float t = g.IO.MouseDownDuration[button];
3358     if (t == 0.0f)
3359         return true;
3360
3361     if (repeat && t > g.IO.KeyRepeatDelay)
3362     {
3363         float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate;
3364         if ((fmodf(t - delay, rate) > rate*0.5f) != (fmodf(t - delay - g.IO.DeltaTime, rate) > rate*0.5f))
3365             return true;
3366     }
3367
3368     return false;
3369 }
3370
3371 bool ImGui::IsMouseReleased(int button)
3372 {
3373     ImGuiContext& g = *GImGui;
3374     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3375     return g.IO.MouseReleased[button];
3376 }
3377
3378 bool ImGui::IsMouseDoubleClicked(int button)
3379 {
3380     ImGuiContext& g = *GImGui;
3381     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3382     return g.IO.MouseDoubleClicked[button];
3383 }
3384
3385 bool ImGui::IsMouseDragging(int button, float lock_threshold)
3386 {
3387     ImGuiContext& g = *GImGui;
3388     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3389     if (!g.IO.MouseDown[button])
3390         return false;
3391     if (lock_threshold < 0.0f)
3392         lock_threshold = g.IO.MouseDragThreshold;
3393     return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
3394 }
3395
3396 ImVec2 ImGui::GetMousePos()
3397 {
3398     return GImGui->IO.MousePos;
3399 }
3400
3401 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
3402 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
3403 {
3404     ImGuiContext& g = *GImGui;
3405     if (g.CurrentPopupStack.Size > 0)
3406         return g.OpenPopupStack[g.CurrentPopupStack.Size-1].MousePosOnOpen;
3407     return g.IO.MousePos;
3408 }
3409
3410 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position
3411 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
3412 {
3413     if (mouse_pos == NULL)
3414         mouse_pos = &GImGui->IO.MousePos;
3415     const float MOUSE_INVALID = -256000.0f;
3416     return mouse_pos->x >= MOUSE_INVALID && mouse_pos->y >= MOUSE_INVALID;
3417 }
3418
3419 ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold)
3420 {
3421     ImGuiContext& g = *GImGui;
3422     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3423     if (lock_threshold < 0.0f)
3424         lock_threshold = g.IO.MouseDragThreshold;
3425     if (g.IO.MouseDown[button])
3426         if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
3427             return g.IO.MousePos - g.IO.MouseClickedPos[button];     // Assume we can only get active with left-mouse button (at the moment).
3428     return ImVec2(0.0f, 0.0f);
3429 }
3430
3431 void ImGui::ResetMouseDragDelta(int button)
3432 {
3433     ImGuiContext& g = *GImGui;
3434     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3435     // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
3436     g.IO.MouseClickedPos[button] = g.IO.MousePos;
3437 }
3438
3439 ImGuiMouseCursor ImGui::GetMouseCursor()
3440 {
3441     return GImGui->MouseCursor;
3442 }
3443
3444 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
3445 {
3446     GImGui->MouseCursor = cursor_type;
3447 }
3448
3449 void ImGui::CaptureKeyboardFromApp(bool capture)
3450 {
3451     GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
3452 }
3453
3454 void ImGui::CaptureMouseFromApp(bool capture)
3455 {
3456     GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
3457 }
3458
3459 bool ImGui::IsItemActive()
3460 {
3461     ImGuiContext& g = *GImGui;
3462     if (g.ActiveId)
3463     {
3464         ImGuiWindow* window = g.CurrentWindow;
3465         return g.ActiveId == window->DC.LastItemId;
3466     }
3467     return false;
3468 }
3469
3470 bool ImGui::IsItemClicked(int mouse_button)
3471 {
3472     return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_Default);
3473 }
3474
3475 bool ImGui::IsAnyItemHovered()
3476 {
3477     return GImGui->HoveredId != 0 || GImGui->HoveredIdPreviousFrame != 0;
3478 }
3479
3480 bool ImGui::IsAnyItemActive()
3481 {
3482     return GImGui->ActiveId != 0;
3483 }
3484
3485 bool ImGui::IsItemVisible()
3486 {
3487     ImGuiWindow* window = GetCurrentWindowRead();
3488     return window->ClipRect.Overlaps(window->DC.LastItemRect);
3489 }
3490
3491 // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
3492 void ImGui::SetItemAllowOverlap()
3493 {
3494     ImGuiContext& g = *GImGui;
3495     if (g.HoveredId == g.CurrentWindow->DC.LastItemId)
3496         g.HoveredIdAllowOverlap = true;
3497     if (g.ActiveId == g.CurrentWindow->DC.LastItemId)
3498         g.ActiveIdAllowOverlap = true;
3499 }
3500
3501 ImVec2 ImGui::GetItemRectMin()
3502 {
3503     ImGuiWindow* window = GetCurrentWindowRead();
3504     return window->DC.LastItemRect.Min;
3505 }
3506
3507 ImVec2 ImGui::GetItemRectMax()
3508 {
3509     ImGuiWindow* window = GetCurrentWindowRead();
3510     return window->DC.LastItemRect.Max;
3511 }
3512
3513 ImVec2 ImGui::GetItemRectSize()
3514 {
3515     ImGuiWindow* window = GetCurrentWindowRead();
3516     return window->DC.LastItemRect.GetSize();
3517 }
3518
3519 ImVec2 ImGui::CalcItemRectClosestPoint(const ImVec2& pos, bool on_edge, float outward)
3520 {
3521     ImGuiWindow* window = GetCurrentWindowRead();
3522     ImRect rect = window->DC.LastItemRect;
3523     rect.Expand(outward);
3524     return rect.GetClosestPoint(pos, on_edge);
3525 }
3526
3527 static ImRect GetVisibleRect()
3528 {
3529     ImGuiContext& g = *GImGui;
3530     if (g.IO.DisplayVisibleMin.x != g.IO.DisplayVisibleMax.x && g.IO.DisplayVisibleMin.y != g.IO.DisplayVisibleMax.y)
3531         return ImRect(g.IO.DisplayVisibleMin, g.IO.DisplayVisibleMax);
3532     return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
3533 }
3534
3535 // Not exposed publicly as BeginTooltip() because bool parameters are evil. Let's see if other needs arise first.
3536 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip)
3537 {
3538     ImGuiContext& g = *GImGui;
3539     char window_name[16];
3540     ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip%02d", g.TooltipOverrideCount);
3541     if (override_previous_tooltip)
3542         if (ImGuiWindow* window = ImGui::FindWindowByName(window_name))
3543             if (window->Active)
3544             {
3545                 // Hide previous tooltips. We can't easily "reset" the content of a window so we create a new one.
3546                 window->HiddenFrames = 1;
3547                 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip%02d", ++g.TooltipOverrideCount);
3548             }
3549     ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
3550     ImGui::Begin(window_name, NULL, flags | extra_flags);
3551 }
3552
3553 void ImGui::SetTooltipV(const char* fmt, va_list args)
3554 {
3555     BeginTooltipEx(0, true);
3556     TextV(fmt, args);
3557     EndTooltip();
3558 }
3559
3560 void ImGui::SetTooltip(const char* fmt, ...)
3561 {
3562     va_list args;
3563     va_start(args, fmt);
3564     SetTooltipV(fmt, args);
3565     va_end(args);
3566 }
3567
3568 void ImGui::BeginTooltip()
3569 {
3570     BeginTooltipEx(0, false);
3571 }
3572
3573 void ImGui::EndTooltip()
3574 {
3575     IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip);   // Mismatched BeginTooltip()/EndTooltip() calls
3576     ImGui::End();
3577 }
3578
3579 // Mark popup as open (toggle toward open state).
3580 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
3581 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
3582 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
3583 void ImGui::OpenPopupEx(ImGuiID id, bool reopen_existing)
3584 {
3585     ImGuiContext& g = *GImGui;
3586     ImGuiWindow* window = g.CurrentWindow;
3587     int current_stack_size = g.CurrentPopupStack.Size;
3588     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)
3589     if (g.OpenPopupStack.Size < current_stack_size + 1)
3590         g.OpenPopupStack.push_back(popup_ref);
3591     else if (reopen_existing || g.OpenPopupStack[current_stack_size].PopupId != id)
3592     {
3593         g.OpenPopupStack.resize(current_stack_size+1);
3594         g.OpenPopupStack[current_stack_size] = popup_ref;
3595
3596         // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by CloseInactivePopups().
3597         // This is equivalent to what ClosePopupToLevel() does.
3598         if (g.OpenPopupStack[current_stack_size].PopupId == id)
3599             FocusWindow(window);
3600     }
3601 }
3602
3603 void ImGui::OpenPopup(const char* str_id)
3604 {
3605     ImGuiContext& g = *GImGui;
3606     OpenPopupEx(g.CurrentWindow->GetID(str_id), false);
3607 }
3608
3609 static void CloseInactivePopups(ImGuiWindow* ref_window)
3610 {
3611     ImGuiContext& g = *GImGui;
3612     if (g.OpenPopupStack.empty())
3613         return;
3614
3615     // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
3616     // Don't close our own child popup windows.
3617     int n = 0;
3618     if (ref_window)
3619     {
3620         for (n = 0; n < g.OpenPopupStack.Size; n++)
3621         {
3622             ImGuiPopupRef& popup = g.OpenPopupStack[n];
3623             if (!popup.Window)
3624                 continue;
3625             IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
3626             if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
3627                 continue;
3628
3629             // Trim the stack if popups are not direct descendant of the reference window (which is often the NavWindow)
3630             bool has_focus = false;
3631             for (int m = n; m < g.OpenPopupStack.Size && !has_focus; m++)
3632                 has_focus = (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == ref_window->RootWindow);
3633             if (!has_focus)
3634                 break;
3635         }
3636     }
3637     if (n < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the block below
3638         ClosePopupToLevel(n);
3639 }
3640
3641 static ImGuiWindow* GetFrontMostModalRootWindow()
3642 {
3643     ImGuiContext& g = *GImGui;
3644     for (int n = g.OpenPopupStack.Size-1; n >= 0; n--)
3645         if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
3646             if (popup->Flags & ImGuiWindowFlags_Modal)
3647                 return popup;
3648     return NULL;
3649 }
3650
3651 static void ClosePopupToLevel(int remaining)
3652 {
3653     ImGuiContext& g = *GImGui;
3654     if (remaining > 0)
3655         ImGui::FocusWindow(g.OpenPopupStack[remaining-1].Window);
3656     else
3657         ImGui::FocusWindow(g.OpenPopupStack[0].ParentWindow);
3658     g.OpenPopupStack.resize(remaining);
3659 }
3660
3661 void ImGui::ClosePopup(ImGuiID id)
3662 {
3663     if (!IsPopupOpen(id))
3664         return;
3665     ImGuiContext& g = *GImGui;
3666     ClosePopupToLevel(g.OpenPopupStack.Size - 1);
3667 }
3668
3669 // Close the popup we have begin-ed into.
3670 void ImGui::CloseCurrentPopup()
3671 {
3672     ImGuiContext& g = *GImGui;
3673     int popup_idx = g.CurrentPopupStack.Size - 1;
3674     if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.CurrentPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
3675         return;
3676     while (popup_idx > 0 && g.OpenPopupStack[popup_idx].Window && (g.OpenPopupStack[popup_idx].Window->Flags & ImGuiWindowFlags_ChildMenu))
3677         popup_idx--;
3678     ClosePopupToLevel(popup_idx);
3679 }
3680
3681 static inline void ClearSetNextWindowData()
3682 {
3683     // FIXME-OPT
3684     ImGuiContext& g = *GImGui;
3685     g.SetNextWindowPosCond = g.SetNextWindowSizeCond = g.SetNextWindowContentSizeCond = g.SetNextWindowCollapsedCond = 0;
3686     g.SetNextWindowSizeConstraint = g.SetNextWindowFocus = false;
3687 }
3688
3689 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags)
3690 {
3691     ImGuiContext& g = *GImGui;
3692     if (!IsPopupOpen(id))
3693     {
3694         ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
3695         return false;
3696     }
3697
3698     ImGuiWindowFlags flags = extra_flags|ImGuiWindowFlags_Popup|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings;
3699
3700     char name[20];
3701     if (flags & ImGuiWindowFlags_ChildMenu)
3702         ImFormatString(name, IM_ARRAYSIZE(name), "##menu_%d", g.CurrentPopupStack.Size);    // Recycle windows based on depth
3703     else
3704         ImFormatString(name, IM_ARRAYSIZE(name), "##popup_%08x", id); // Not recycling, so we can close/open during the same frame
3705
3706     bool is_open = Begin(name, NULL, flags);
3707     if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
3708         EndPopup();
3709
3710     return is_open;
3711 }
3712
3713 bool ImGui::BeginPopup(const char* str_id)
3714 {
3715     ImGuiContext& g = *GImGui;
3716     if (g.OpenPopupStack.Size <= g.CurrentPopupStack.Size) // Early out for performance
3717     {
3718         ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
3719         return false;
3720     }
3721     return BeginPopupEx(g.CurrentWindow->GetID(str_id), ImGuiWindowFlags_AlwaysAutoResize);
3722 }
3723
3724 bool ImGui::IsPopupOpen(ImGuiID id)
3725 {
3726     ImGuiContext& g = *GImGui;
3727     return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == id;
3728 }
3729
3730 bool ImGui::IsPopupOpen(const char* str_id)
3731 {
3732     ImGuiContext& g = *GImGui;
3733     return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id);
3734 }
3735
3736 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags extra_flags)
3737 {
3738     ImGuiContext& g = *GImGui;
3739     ImGuiWindow* window = g.CurrentWindow;
3740     const ImGuiID id = window->GetID(name);
3741     if (!IsPopupOpen(id))
3742     {
3743         ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
3744         return false;
3745     }
3746
3747     // Center modal windows by default
3748     if ((window->SetWindowPosAllowFlags & g.SetNextWindowPosCond) == 0)
3749         SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
3750
3751     ImGuiWindowFlags flags = extra_flags|ImGuiWindowFlags_Popup|ImGuiWindowFlags_Modal|ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoSavedSettings;
3752     bool is_open = Begin(name, p_open, flags);
3753     if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
3754     {
3755         EndPopup();
3756         if (is_open)
3757             ClosePopup(id);
3758         return false;
3759     }
3760
3761     return is_open;
3762 }
3763
3764 void ImGui::EndPopup()
3765 {
3766     ImGuiContext& g = *GImGui; (void)g;
3767     IM_ASSERT(g.CurrentWindow->Flags & ImGuiWindowFlags_Popup);  // Mismatched BeginPopup()/EndPopup() calls
3768     IM_ASSERT(g.CurrentPopupStack.Size > 0);
3769     End();
3770 }
3771
3772 bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button)
3773 {
3774     ImGuiWindow* window = GImGui->CurrentWindow;
3775     if (IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
3776     {
3777         ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
3778         IM_ASSERT(id != 0);                                                  // However, you cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
3779         OpenPopupEx(id, true);
3780         return true;
3781     }
3782     return false;
3783 }
3784
3785 // This is a helper to handle the simplest case of associating one named popup to one given widget.
3786 // You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
3787 // You can pass a NULL str_id to use the identifier of the last item.
3788 bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button)
3789 {
3790     ImGuiWindow* window = GImGui->CurrentWindow;
3791     ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
3792     IM_ASSERT(id != 0);                                                  // However, you cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
3793     if (IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
3794         OpenPopupEx(id, true);
3795     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize);
3796 }
3797
3798 bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items)
3799 {
3800     if (!str_id)
3801         str_id = "window_context";
3802     ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
3803     if (IsMouseClicked(mouse_button))
3804         if (IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
3805             if (also_over_items || !IsAnyItemHovered())
3806                 OpenPopupEx(id, true);
3807     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize);
3808 }
3809
3810 bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
3811 {
3812     if (!str_id) 
3813         str_id = "void_context";
3814     ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
3815     if (!IsAnyWindowHovered() && IsMouseClicked(mouse_button))
3816         OpenPopupEx(id, true);
3817     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize);
3818 }
3819
3820 static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
3821 {
3822     ImGuiContext& g = *GImGui;
3823     ImGuiWindow* parent_window = ImGui::GetCurrentWindow();
3824     ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
3825     flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove);  // Inherit the NoMove flag
3826
3827     const ImVec2 content_avail = ImGui::GetContentRegionAvail();
3828     ImVec2 size = ImFloor(size_arg);
3829     const int auto_fit_axises = ((size.x == 0.0f) ? 0x01 : 0x00) | ((size.y == 0.0f) ? 0x02 : 0x00);
3830     if (size.x <= 0.0f)
3831         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)
3832     if (size.y <= 0.0f)
3833         size.y = ImMax(content_avail.y, 4.0f) - fabsf(size.y);
3834
3835     const float backup_border_size = g.Style.ChildBorderSize;
3836     if (!border)
3837         g.Style.ChildBorderSize = 0.0f;
3838     flags |= extra_flags;
3839
3840     char title[256];
3841     if (name)
3842         ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s_%08X", parent_window->Name, name, id);
3843     else
3844         ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id);
3845
3846     ImGui::SetNextWindowSize(size);
3847     bool ret = ImGui::Begin(title, NULL, flags);
3848     ImGuiWindow* child_window = ImGui::GetCurrentWindow();
3849     child_window->AutoFitChildAxises = auto_fit_axises;
3850     g.Style.ChildBorderSize = backup_border_size;
3851
3852     return ret;
3853 }
3854
3855 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
3856 {
3857     ImGuiWindow* window = GetCurrentWindow();
3858     return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
3859 }
3860
3861 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
3862 {
3863     return BeginChildEx(NULL, id, size_arg, border, extra_flags);
3864 }
3865
3866 void ImGui::EndChild()
3867 {
3868     ImGuiWindow* window = GetCurrentWindow();
3869
3870     IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow);   // Mismatched BeginChild()/EndChild() callss
3871     if ((window->Flags & ImGuiWindowFlags_ComboBox) || window->BeginCount > 1)
3872     {
3873         ImGui::End();
3874     }
3875     else
3876     {
3877         // 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.
3878         ImVec2 sz = GetWindowSize();
3879         if (window->AutoFitChildAxises & 0x01) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
3880             sz.x = ImMax(4.0f, sz.x);
3881         if (window->AutoFitChildAxises & 0x02)
3882             sz.y = ImMax(4.0f, sz.y);
3883         ImGui::End();
3884
3885         ImGuiWindow* parent_window = GetCurrentWindow();
3886         ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
3887         ItemSize(sz);
3888         ItemAdd(bb, 0);
3889     }
3890 }
3891
3892 // Helper to create a child window / scrolling region that looks like a normal widget frame.
3893 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
3894 {
3895     ImGuiContext& g = *GImGui;
3896     const ImGuiStyle& style = g.Style;
3897     PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
3898     PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
3899     PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
3900     PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
3901     return BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
3902 }
3903
3904 void ImGui::EndChildFrame()
3905 {
3906     EndChild();
3907     PopStyleVar(3);
3908     PopStyleColor();
3909 }
3910
3911 // Save and compare stack sizes on Begin()/End() to detect usage errors
3912 static void CheckStacksSize(ImGuiWindow* window, bool write)
3913 {
3914     // 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)
3915     ImGuiContext& g = *GImGui;
3916     int* p_backup = &window->DC.StackSizesBackup[0];
3917     { 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()
3918     { 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()
3919     { 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()
3920     { 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()
3921     { 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()
3922     { 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()
3923     IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
3924 }
3925
3926 static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& r_inner)
3927 {
3928     const ImGuiStyle& style = GImGui->Style;
3929
3930     // Clamp into visible area while not overlapping the cursor. Safety padding is optional if our popup size won't fit without it.
3931     ImVec2 safe_padding = style.DisplaySafeAreaPadding;
3932     ImRect r_outer(GetVisibleRect());
3933     r_outer.Expand(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));
3934     ImVec2 base_pos_clamped = ImClamp(base_pos, r_outer.Min, r_outer.Max - size);
3935
3936     for (int n = (*last_dir != -1) ? -1 : 0; n < 4; n++)   // Last, Right, down, up, left. (Favor last used direction).
3937     {
3938         const int dir = (n == -1) ? *last_dir : n;
3939         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);
3940         if (rect.GetWidth() < size.x || rect.GetHeight() < size.y)
3941             continue;
3942         *last_dir = dir;
3943         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);
3944     }
3945
3946     // Fallback, try to keep within display
3947     *last_dir = -1;
3948     ImVec2 pos = base_pos;
3949     pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
3950     pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
3951     return pos;
3952 }
3953
3954 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
3955 {
3956     window->SetWindowPosAllowFlags       = enabled ? (window->SetWindowPosAllowFlags       | flags) : (window->SetWindowPosAllowFlags       & ~flags);
3957     window->SetWindowSizeAllowFlags      = enabled ? (window->SetWindowSizeAllowFlags      | flags) : (window->SetWindowSizeAllowFlags      & ~flags);
3958     window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
3959 }
3960
3961 ImGuiWindow* ImGui::FindWindowByName(const char* name)
3962 {
3963     ImGuiContext& g = *GImGui;
3964     ImGuiID id = ImHash(name, 0);
3965     return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
3966 }
3967
3968 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
3969 {
3970     ImGuiContext& g = *GImGui;
3971
3972     // Create window the first time
3973     ImGuiWindow* window = (ImGuiWindow*)ImGui::MemAlloc(sizeof(ImGuiWindow));
3974     IM_PLACEMENT_NEW(window) ImGuiWindow(name);
3975     window->Flags = flags;
3976     g.WindowsById.SetVoidPtr(window->ID, window);
3977
3978     if (flags & ImGuiWindowFlags_NoSavedSettings)
3979     {
3980         // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
3981         window->Size = window->SizeFull = size;
3982     }
3983     else
3984     {
3985         // Retrieve settings from .ini file
3986         // Use SetWindowPos() or SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
3987         window->PosFloat = ImVec2(60, 60);
3988         window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
3989
3990         ImGuiIniData* settings = FindWindowSettings(name);
3991         if (!settings)
3992             settings = AddWindowSettings(name);
3993         else
3994             SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
3995
3996         if (settings->Pos.x != FLT_MAX)
3997         {
3998             window->PosFloat = settings->Pos;
3999             window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
4000             window->Collapsed = settings->Collapsed;
4001         }
4002
4003         if (ImLengthSqr(settings->Size) > 0.00001f)
4004             size = settings->Size;
4005         window->Size = window->SizeFull = size;
4006     }
4007
4008     if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
4009     {
4010         window->AutoFitFramesX = window->AutoFitFramesY = 2;
4011         window->AutoFitOnlyGrows = false;
4012     }
4013     else
4014     {
4015         if (window->Size.x <= 0.0f)
4016             window->AutoFitFramesX = 2;
4017         if (window->Size.y <= 0.0f)
4018             window->AutoFitFramesY = 2;
4019         window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
4020     }
4021
4022     if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
4023         g.Windows.insert(g.Windows.begin(), window); // Quite slow but rare and only once
4024     else
4025         g.Windows.push_back(window);
4026     return window;
4027 }
4028
4029 static ImVec2 CalcSizeFullWithConstraint(ImGuiWindow* window, ImVec2 new_size)
4030 {
4031     ImGuiContext& g = *GImGui;
4032     if (g.SetNextWindowSizeConstraint)
4033     {
4034         // Using -1,-1 on either X/Y axis to preserve the current size.
4035         ImRect cr = g.SetNextWindowSizeConstraintRect;
4036         new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
4037         new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
4038         if (g.SetNextWindowSizeConstraintCallback)
4039         {
4040             ImGuiSizeConstraintCallbackData data;
4041             data.UserData = g.SetNextWindowSizeConstraintCallbackUserData;
4042             data.Pos = window->Pos;
4043             data.CurrentSize = window->SizeFull;
4044             data.DesiredSize = new_size;
4045             g.SetNextWindowSizeConstraintCallback(&data);
4046             new_size = data.DesiredSize;
4047         }
4048     }
4049     if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
4050     {
4051         new_size = ImMax(new_size, g.Style.WindowMinSize);
4052         new_size.y = ImMax(new_size.y, window->TitleBarHeight() + window->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows
4053     }
4054     return new_size;
4055 }
4056
4057 static ImVec2 CalcSizeAutoFit(ImGuiWindow* window)
4058 {
4059     ImGuiContext& g = *GImGui;
4060     ImGuiStyle& style = g.Style;
4061     ImGuiWindowFlags flags = window->Flags;
4062     ImVec2 size_auto_fit;
4063     if ((flags & ImGuiWindowFlags_Tooltip) != 0)
4064     {
4065         // Tooltip always resize. We keep the spacing symmetric on both axises for aesthetic purpose.
4066         size_auto_fit = window->SizeContents + window->WindowPadding - ImVec2(0.0f, style.ItemSpacing.y);
4067     }
4068     else
4069     {
4070         // Handling case of auto fit window not fitting on the screen (on either axis): we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding.
4071         size_auto_fit = ImClamp(window->SizeContents + window->WindowPadding, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - g.Style.DisplaySafeAreaPadding));
4072         ImVec2 size_auto_fit_after_constraint = CalcSizeFullWithConstraint(window, size_auto_fit);
4073         if (size_auto_fit_after_constraint.x < window->SizeContents.x && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar))
4074             size_auto_fit.y += style.ScrollbarSize;
4075         if (size_auto_fit_after_constraint.y < window->SizeContents.y && !(flags & ImGuiWindowFlags_NoScrollbar))
4076             size_auto_fit.x += style.ScrollbarSize;
4077         size_auto_fit.y = ImMax(size_auto_fit.y - style.ItemSpacing.y, 0.0f);
4078     }
4079     return size_auto_fit;
4080 }
4081
4082 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
4083 {
4084     ImVec2 scroll = window->Scroll;
4085     float cr_x = window->ScrollTargetCenterRatio.x;
4086     float cr_y = window->ScrollTargetCenterRatio.y;
4087     if (window->ScrollTarget.x < FLT_MAX)
4088         scroll.x = window->ScrollTarget.x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x);
4089     if (window->ScrollTarget.y < FLT_MAX)
4090         scroll.y = window->ScrollTarget.y - (1.0f - cr_y) * (window->TitleBarHeight() + window->MenuBarHeight()) - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y);
4091     scroll = ImMax(scroll, ImVec2(0.0f, 0.0f));
4092     if (!window->Collapsed && !window->SkipItems)
4093     {
4094         scroll.x = ImMin(scroll.x, ImMax(0.0f, window->SizeContents.x - (window->SizeFull.x - window->ScrollbarSizes.x))); // == GetScrollMaxX for that window
4095         scroll.y = ImMin(scroll.y, ImMax(0.0f, window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y))); // == GetScrollMaxY for that window
4096     }
4097     return scroll;
4098 }
4099
4100 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
4101 {
4102     if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
4103         return ImGuiCol_PopupBg;
4104     if (flags & ImGuiWindowFlags_ChildWindow)
4105         return ImGuiCol_ChildBg;
4106     return ImGuiCol_WindowBg;
4107 }
4108
4109 // Push a new ImGui window to add widgets to.
4110 // - 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.
4111 // - Begin/End can be called multiple times during the frame with the same window name to append content.
4112 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
4113 //   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.
4114 // - 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.
4115 // - 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.
4116 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
4117 {
4118     ImGuiContext& g = *GImGui;
4119     const ImGuiStyle& style = g.Style;
4120     IM_ASSERT(name != NULL);                        // Window name required
4121     IM_ASSERT(g.Initialized);                       // Forgot to call ImGui::NewFrame()
4122     IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
4123
4124     if (flags & ImGuiWindowFlags_NoInputs)
4125         flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
4126
4127     // Find or create
4128     bool window_is_new = false;
4129     ImGuiWindow* window = FindWindowByName(name);
4130     if (!window)
4131     {
4132         ImVec2 size_on_first_use = (g.SetNextWindowSizeCond != 0) ? g.SetNextWindowSizeVal : ImVec2(0.0f, 0.0f); // Any condition flag will do since we are creating a new window here.
4133         window = CreateNewWindow(name, size_on_first_use, flags);
4134         window_is_new = true;
4135     }
4136
4137     const int current_frame = g.FrameCount;
4138     const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
4139     if (first_begin_of_the_frame)
4140         window->Flags = (ImGuiWindowFlags)flags;
4141     else
4142         flags = window->Flags;
4143
4144     // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack
4145     ImGuiWindow* parent_window = first_begin_of_the_frame ? (!g.CurrentWindowStack.empty() ? g.CurrentWindowStack.back() : NULL) : window->ParentWindow;
4146     IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
4147
4148     // Add to stack
4149     g.CurrentWindowStack.push_back(window);
4150     SetCurrentWindow(window);
4151     CheckStacksSize(window, true);
4152
4153     bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1);   // Not using !WasActive because the implicit "Debug" window would always toggle off->on
4154     if (flags & ImGuiWindowFlags_Popup)
4155     {
4156         ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size];
4157         window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
4158         window_just_activated_by_user |= (window != popup_ref.Window);
4159         popup_ref.Window = window;
4160         g.CurrentPopupStack.push_back(popup_ref);
4161         window->PopupId = popup_ref.PopupId;
4162     }
4163
4164     const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFrames == 1);
4165     window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
4166     window->CloseButton = (p_open != NULL);
4167
4168     // Process SetNextWindow***() calls
4169     if (window->Appearing)
4170         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
4171     bool window_pos_set_by_api = false, window_size_set_by_api = false;
4172     if (g.SetNextWindowPosCond)
4173     {
4174         window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.SetNextWindowPosCond) != 0;
4175         if (window_pos_set_by_api && ImLengthSqr(g.SetNextWindowPosPivot) > 0.00001f)
4176         {
4177             // May be processed on the next frame if this is our first frame and we are measuring size
4178             // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
4179             window->SetWindowPosVal = g.SetNextWindowPosVal;
4180             window->SetWindowPosPivot = g.SetNextWindowPosPivot;
4181             window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
4182         }
4183         else
4184         {
4185             SetWindowPos(window, g.SetNextWindowPosVal, g.SetNextWindowPosCond);
4186         }
4187         g.SetNextWindowPosCond = 0;
4188     }
4189     if (g.SetNextWindowSizeCond)
4190     {
4191         window_size_set_by_api = (window->SetWindowSizeAllowFlags & g.SetNextWindowSizeCond) != 0;
4192         SetWindowSize(window, g.SetNextWindowSizeVal, g.SetNextWindowSizeCond);
4193         g.SetNextWindowSizeCond = 0;
4194     }
4195     if (g.SetNextWindowContentSizeCond)
4196     {
4197         window->SizeContentsExplicit = g.SetNextWindowContentSizeVal;
4198         g.SetNextWindowContentSizeCond = 0;
4199     }
4200     else if (first_begin_of_the_frame)
4201     {
4202         window->SizeContentsExplicit = ImVec2(0.0f, 0.0f);
4203     }
4204     if (g.SetNextWindowCollapsedCond)
4205     {
4206         SetWindowCollapsed(window, g.SetNextWindowCollapsedVal, g.SetNextWindowCollapsedCond);
4207         g.SetNextWindowCollapsedCond = 0;
4208     }
4209     if (g.SetNextWindowFocus)
4210     {
4211         SetWindowFocus();
4212         g.SetNextWindowFocus = false;
4213     }
4214     if (window->Appearing)
4215         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
4216
4217     // When reusing window again multiple times a frame, just append content (don't need to setup again)
4218     if (first_begin_of_the_frame)
4219     {
4220         // Initialize
4221         window->ParentWindow = parent_window;
4222         window->RootWindow = !(flags & ImGuiWindowFlags_ChildWindow) ? window : parent_window->RootWindow;
4223         window->RootNonPopupWindow = !(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) || (flags & ImGuiWindowFlags_Modal) ? window : parent_window->RootNonPopupWindow; // Used to display TitleBgActive color and for selecting which window to use for NavWindowing
4224         //window->RootNavWindow = window;
4225         //while (window->RootNavWindow->Flags & ImGuiWindowFlags_NavFlattened)
4226         //    window->RootNavWindow = window->RootNavWindow->ParentWindow;
4227
4228         window->Active = true;
4229         window->OrderWithinParent = 0;
4230         window->BeginCount = 0;
4231         window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX);
4232         window->LastFrameActive = current_frame;
4233         window->IDStack.resize(1);
4234
4235         // Clear draw list, setup texture, outer clipping rectangle
4236         window->DrawList->Clear();
4237         window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
4238         ImRect fullscreen_rect(GetVisibleRect());
4239         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_ComboBox|ImGuiWindowFlags_Popup)))
4240             PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true);
4241         else
4242             PushClipRect(fullscreen_rect.Min, fullscreen_rect.Max, true);
4243
4244         if (window_just_activated_by_user)
4245         {
4246             // Popup first latch mouse position, will position itself when it appears next frame
4247             window->AutoPosLastDirection = -1;
4248             if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
4249                 window->PosFloat = g.IO.MousePos;
4250         }
4251
4252         // Collapse window by double-clicking on title bar
4253         // 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
4254         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
4255         {
4256             ImRect title_bar_rect = window->TitleBarRect();
4257             if (g.HoveredWindow == window && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
4258             {
4259                 window->Collapsed = !window->Collapsed;
4260                 MarkIniSettingsDirty(window);
4261                 FocusWindow(window);
4262             }
4263         }
4264         else
4265         {
4266             window->Collapsed = false;
4267         }
4268
4269         // SIZE
4270
4271         // Update contents size from last frame for auto-fitting (unless explicitly specified)
4272         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));
4273         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));
4274
4275         // Hide popup/tooltip window when first appearing while we measure size (because we recycle them)
4276         if (window->HiddenFrames > 0)
4277             window->HiddenFrames--;
4278         if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0 && window_just_activated_by_user)
4279         {
4280             window->HiddenFrames = 1;
4281             if (flags & ImGuiWindowFlags_AlwaysAutoResize)
4282             {
4283                 if (!window_size_set_by_api)
4284                     window->Size = window->SizeFull = ImVec2(0.f, 0.f);
4285                 window->SizeContents = ImVec2(0.f, 0.f);
4286             }
4287         }
4288
4289         // Lock window rounding, border size and rounding so that altering the border sizes for children doesn't have side-effects.
4290         window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
4291         window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
4292         window->WindowPadding = style.WindowPadding;
4293         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_ComboBox | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
4294             window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
4295         const float window_rounding = window->WindowRounding;
4296         const float window_border_size = window->WindowBorderSize;
4297
4298         // Calculate auto-fit size, handle automatic resize
4299         const ImVec2 size_auto_fit = CalcSizeAutoFit(window);
4300         if (window->Collapsed)
4301         {
4302             // We still process initial auto-fit on collapsed windows to get a window width,
4303             // But otherwise we don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
4304             if (window->AutoFitFramesX > 0)
4305                 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
4306             if (window->AutoFitFramesY > 0)
4307                 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
4308         }
4309         else if (!window_size_set_by_api)
4310         {
4311             if (flags & ImGuiWindowFlags_AlwaysAutoResize)
4312             {
4313                 window->SizeFull = size_auto_fit;
4314             }
4315             else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
4316             {
4317                 // Auto-fit only grows during the first few frames
4318                 if (window->AutoFitFramesX > 0)
4319                     window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
4320                 if (window->AutoFitFramesY > 0)
4321                     window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
4322                 MarkIniSettingsDirty(window);
4323             }
4324         }
4325
4326         // Apply minimum/maximum window size constraints and final size
4327         window->SizeFull = CalcSizeFullWithConstraint(window, window->SizeFull);
4328         window->Size = window->Collapsed ? window->TitleBarRect().GetSize() : window->SizeFull;
4329         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup))
4330         {
4331             IM_ASSERT(window_size_set_by_api); // Submitted by BeginChild()
4332             window->Size = window->SizeFull;
4333         }
4334
4335         // SCROLLBAR STATUS
4336
4337         // Update scrollbar status (based on the Size that was effective during last frame or the auto-resized Size). We need to do this before manual resize (below) is effective.
4338         if (!window->Collapsed)
4339         {
4340             window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > window->SizeFull.y + style.ItemSpacing.y) && !(flags & ImGuiWindowFlags_NoScrollbar));
4341             window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > window->SizeFull.x - (window->ScrollbarY ? style.ScrollbarSize : 0.0f) - window->WindowPadding.x) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
4342             if (window->ScrollbarX && !window->ScrollbarY)
4343                 window->ScrollbarY = (window->SizeContents.y > window->SizeFull.y + style.ItemSpacing.y - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);
4344             window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
4345         }
4346
4347         // POSITION
4348
4349         // Position child window
4350         if (flags & ImGuiWindowFlags_ChildWindow)
4351         {
4352             window->OrderWithinParent = parent_window->DC.ChildWindows.Size;
4353             parent_window->DC.ChildWindows.push_back(window);
4354         }
4355         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api)
4356             window->Pos = window->PosFloat = parent_window->DC.CursorPos;
4357
4358         const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFrames == 0);
4359         if (window_pos_with_pivot)
4360         {
4361             // Position given a pivot (e.g. for centering)
4362             SetWindowPos(window, ImMax(style.DisplaySafeAreaPadding, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot), 0);
4363         }
4364         else if (flags & ImGuiWindowFlags_ChildMenu)
4365         {
4366             // 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.
4367             // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
4368             IM_ASSERT(window_pos_set_by_api);
4369             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).
4370             ImRect rect_to_avoid;
4371             if (parent_window->DC.MenuBarAppending)
4372                 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());
4373             else
4374                 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);
4375             window->PosFloat = FindBestPopupWindowPos(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
4376         }
4377         else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
4378         {
4379             ImRect rect_to_avoid(window->PosFloat.x - 1, window->PosFloat.y - 1, window->PosFloat.x + 1, window->PosFloat.y + 1);
4380             window->PosFloat = FindBestPopupWindowPos(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
4381         }
4382
4383         // Position tooltip (always follows mouse)
4384         if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api)
4385         {
4386             ImVec2 ref_pos = g.IO.MousePos;
4387             ImRect rect_to_avoid(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24, ref_pos.y + 24); // FIXME: Completely hard-coded. Perhaps center on cursor hit-point instead?
4388             window->PosFloat = FindBestPopupWindowPos(ref_pos, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
4389             if (window->AutoPosLastDirection == -1)
4390                 window->PosFloat = ref_pos + ImVec2(2,2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
4391         }
4392
4393         // Clamp position so it stays visible
4394         if (!(flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
4395         {
4396             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.
4397             {
4398                 ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
4399                 window->PosFloat = ImMax(window->PosFloat + window->Size, padding) - window->Size;
4400                 window->PosFloat = ImMin(window->PosFloat, g.IO.DisplaySize - padding);
4401             }
4402         }
4403         window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
4404
4405         // Default item width. Make it proportional to window size if window manually resizes
4406         if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
4407             window->ItemWidthDefault = (float)(int)(window->Size.x * 0.65f);
4408         else
4409             window->ItemWidthDefault = (float)(int)(g.FontSize * 16.0f);
4410
4411         // Prepare for focus requests
4412         window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1);
4413         window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter+1)) % (window->FocusIdxTabCounter+1);
4414         window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1;
4415         window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX;
4416
4417         // Apply scrolling
4418         window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);
4419         window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
4420
4421         // Modal window darkens what is behind them
4422         if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostModalRootWindow())
4423             window->DrawList->AddRectFilled(fullscreen_rect.Min, fullscreen_rect.Max, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio));
4424
4425         // Draw window + handle manual resize
4426         ImRect title_bar_rect = window->TitleBarRect();
4427         if (window->Collapsed)
4428         {
4429             // Title bar only
4430             float backup_border_size = style.FrameBorderSize;
4431             g.Style.FrameBorderSize = window->WindowBorderSize;
4432             RenderFrame(title_bar_rect.Min, title_bar_rect.Max, GetColorU32(ImGuiCol_TitleBgCollapsed), true, window_rounding);
4433             g.Style.FrameBorderSize = backup_border_size;
4434         }
4435         else
4436         {
4437             ImU32 resize_col = 0;
4438             const float resize_corner_size = ImMax(g.FontSize * 1.35f, window_rounding + 1.0f + g.FontSize * 0.2f);
4439             if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && !(flags & ImGuiWindowFlags_NoResize))
4440             {
4441                 // Manual resize
4442                 // Using the FlattenChilds button flag, we make the resize button accessible even if we are hovering over a child window
4443                 const ImVec2 br = window->Rect().GetBR();
4444                 const ImRect resize_rect(br - ImFloor(ImVec2(resize_corner_size * 0.75f, resize_corner_size * 0.75f)), br);
4445                 const ImGuiID resize_id = window->GetID("#RESIZE");
4446                 bool hovered, held;
4447                 ButtonBehavior(resize_rect, resize_id, &hovered, &held, ImGuiButtonFlags_FlattenChilds);
4448                 resize_col = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
4449                 if (hovered || held)
4450                     g.MouseCursor = ImGuiMouseCursor_ResizeNWSE;
4451
4452                 ImVec2 size_target(FLT_MAX,FLT_MAX);
4453                 if (g.HoveredWindow == window && held && g.IO.MouseDoubleClicked[0])
4454                 {
4455                     // Manual auto-fit when double-clicking
4456                     size_target = size_auto_fit;
4457                     ClearActiveID();
4458                 }
4459                 else if (held)
4460                 {
4461                     // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
4462                     size_target = (g.IO.MousePos - g.ActiveIdClickOffset - window->Pos) + resize_rect.GetSize();
4463                 }
4464
4465                 if (size_target.x != FLT_MAX && size_target.y != FLT_MAX)
4466                 {
4467                     window->SizeFull = CalcSizeFullWithConstraint(window, size_target);
4468                     MarkIniSettingsDirty(window);
4469                 }
4470                 window->Size = window->SizeFull;
4471                 title_bar_rect = window->TitleBarRect();
4472             }
4473
4474             // Window background, Default Alpha
4475             ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
4476             window->DrawList->AddRectFilled(window->Pos+ImVec2(0,window->TitleBarHeight()), window->Pos+window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot);
4477
4478             // Title bar
4479             const bool window_is_focused = g.NavWindow && window->RootNonPopupWindow == g.NavWindow->RootNonPopupWindow;
4480             if (!(flags & ImGuiWindowFlags_NoTitleBar))
4481                 window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg), window_rounding, ImDrawCornerFlags_Top);
4482
4483             // Menu bar
4484             if (flags & ImGuiWindowFlags_MenuBar)
4485             {
4486                 ImRect menu_bar_rect = window->MenuBarRect();
4487                 menu_bar_rect.ClipWith(window->Rect());  // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them.
4488                 window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top);
4489                 if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
4490                     window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
4491             }
4492
4493             // Scrollbars
4494             if (window->ScrollbarX)
4495                 Scrollbar(ImGuiLayoutType_Horizontal);
4496             if (window->ScrollbarY)
4497                 Scrollbar(ImGuiLayoutType_Vertical);
4498
4499             // Render resize grip
4500             // (after the input handling so we don't have a frame of latency)
4501             if (!(flags & ImGuiWindowFlags_NoResize))
4502             {
4503                 const ImVec2 br = window->Rect().GetBR();
4504                 window->DrawList->PathLineTo(br + ImVec2(-resize_corner_size, -window_border_size));
4505                 window->DrawList->PathLineTo(br + ImVec2(-window_border_size, -resize_corner_size));
4506                 window->DrawList->PathArcToFast(ImVec2(br.x - window_rounding - window_border_size, br.y - window_rounding - window_border_size), window_rounding, 0, 3);
4507                 window->DrawList->PathFillConvex(resize_col);
4508             }
4509
4510             // Borders
4511             if (window_border_size > 0.0f)
4512                 window->DrawList->AddRect(window->Pos, window->Pos+window->Size, GetColorU32(ImGuiCol_Border), window_rounding, ImDrawCornerFlags_All, window_border_size);
4513             if (style.FrameBorderSize > 0 && !(flags & ImGuiWindowFlags_NoTitleBar))
4514                 window->DrawList->AddLine(title_bar_rect.GetBL()+ImVec2(1,-1), title_bar_rect.GetBR()+ImVec2(-1,-1), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
4515         }
4516
4517         // Update ContentsRegionMax. All the variable it depends on are set above in this function.
4518         window->ContentsRegionRect.Min.x = -window->Scroll.x + window->WindowPadding.x;
4519         window->ContentsRegionRect.Min.y = -window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight();
4520         window->ContentsRegionRect.Max.x = -window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x)); 
4521         window->ContentsRegionRect.Max.y = -window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y)); 
4522
4523         // Setup drawing context
4524         // (NB: That term "drawing context / DC" lost its meaning a long time ago. Initially was meant to hold transient data only. Nowadays difference between window-> and window->DC-> is dubious.)
4525         window->DC.IndentX = 0.0f + window->WindowPadding.x - window->Scroll.x;
4526         window->DC.GroupOffsetX = 0.0f;
4527         window->DC.ColumnsOffsetX = 0.0f;
4528         window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.IndentX + window->DC.ColumnsOffsetX, window->TitleBarHeight() + window->MenuBarHeight() + window->WindowPadding.y - window->Scroll.y);
4529         window->DC.CursorPos = window->DC.CursorStartPos;
4530         window->DC.CursorPosPrevLine = window->DC.CursorPos;
4531         window->DC.CursorMaxPos = window->DC.CursorStartPos;
4532         window->DC.CurrentLineHeight = window->DC.PrevLineHeight = 0.0f;
4533         window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
4534         window->DC.MenuBarAppending = false;
4535         window->DC.MenuBarOffsetX = ImMax(window->WindowPadding.x, style.ItemSpacing.x);
4536         window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
4537         window->DC.ChildWindows.resize(0);
4538         window->DC.LayoutType = ImGuiLayoutType_Vertical;
4539         window->DC.ItemFlags = ImGuiItemFlags_Default_;
4540         window->DC.ItemWidth = window->ItemWidthDefault;
4541         window->DC.TextWrapPos = -1.0f; // disabled
4542         window->DC.ItemFlagsStack.resize(0);
4543         window->DC.ItemWidthStack.resize(0);
4544         window->DC.TextWrapPosStack.resize(0);
4545         window->DC.ColumnsCurrent = 0;
4546         window->DC.ColumnsCount = 1;
4547         window->DC.ColumnsStartPosY = window->DC.CursorPos.y;
4548         window->DC.ColumnsStartMaxPosX = window->DC.CursorMaxPos.x;
4549         window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY = window->DC.ColumnsStartPosY;
4550         window->DC.TreeDepth = 0;
4551         window->DC.StateStorage = &window->StateStorage;
4552         window->DC.GroupStack.resize(0);
4553         window->MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user);
4554
4555         if ((flags & ImGuiWindowFlags_ChildWindow) && (window->DC.ItemFlags != parent_window->DC.ItemFlags))
4556         {
4557             window->DC.ItemFlags = parent_window->DC.ItemFlags;
4558             window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
4559         }
4560
4561         if (window->AutoFitFramesX > 0)
4562             window->AutoFitFramesX--;
4563         if (window->AutoFitFramesY > 0)
4564             window->AutoFitFramesY--;
4565
4566         // New windows appears in front (we need to do that AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
4567         if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
4568             if (!(flags & (ImGuiWindowFlags_ChildWindow|ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup))
4569                 FocusWindow(window);
4570
4571         // Title bar
4572         if (!(flags & ImGuiWindowFlags_NoTitleBar))
4573         {
4574             // Collapse button
4575             if (!(flags & ImGuiWindowFlags_NoCollapse))
4576             {
4577                 RenderTriangle(window->Pos + style.FramePadding, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f);
4578             }
4579
4580             // Close button
4581             if (p_open != NULL)
4582             {
4583                 const float PAD = 2.0f;
4584                 const float rad = (window->TitleBarHeight() - PAD*2.0f) * 0.5f;
4585                 if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-PAD - rad, PAD + rad), rad))
4586                     *p_open = false;
4587             }
4588
4589             // Title text (FIXME: refactor text alignment facilities along with RenderText helpers)
4590             const ImVec2 text_size = CalcTextSize(name, NULL, true);
4591             ImVec2 text_min = window->Pos;
4592             ImVec2 text_max = window->Pos + ImVec2(window->Size.x, style.FramePadding.y*2 + text_size.y);
4593             ImRect clip_rect;
4594             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()
4595             float pad_left = (flags & ImGuiWindowFlags_NoCollapse) == 0 ? (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x) : style.FramePadding.x;
4596             float pad_right = (p_open != NULL) ? (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x) : style.FramePadding.x;
4597             if (style.WindowTitleAlign.x > 0.0f) pad_right = ImLerp(pad_right, pad_left, style.WindowTitleAlign.x);
4598             text_min.x += pad_left;
4599             text_max.x -= pad_right;
4600             clip_rect.Min = ImVec2(text_min.x, window->Pos.y);
4601             RenderTextClipped(text_min, text_max, name, NULL, &text_size, style.WindowTitleAlign, &clip_rect);
4602         }
4603
4604         // Save clipped aabb so we can access it in constant-time in FindHoveredWindow()
4605         window->WindowRectClipped = window->Rect();
4606         window->WindowRectClipped.ClipWith(window->ClipRect);
4607
4608         // Pressing CTRL+C while holding on a window copy its content to the clipboard
4609         // 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.
4610         // Maybe we can support CTRL+C on every element?
4611         /*
4612         if (g.ActiveId == move_id)
4613             if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
4614                 ImGui::LogToClipboard();
4615         */
4616
4617         // Inner rectangle
4618         // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame
4619         // Note that if our window is collapsed we will end up with a null clipping rectangle which is the correct behavior.
4620         window->InnerRect.Min.x = title_bar_rect.Min.x;
4621         window->InnerRect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight();
4622         window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x;
4623         window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y;
4624         //window->DrawList->AddRect(window->InnerRect.Min, window->InnerRect.Max, IM_COL32_WHITE);
4625
4626         // After Begin() we fill the last item / hovered data using the title bar data. Make that a standard behavior (to allow usage of context menus on title bar only, etc.).
4627         window->DC.LastItemId = window->MoveId;
4628         window->DC.LastItemRect = title_bar_rect;
4629         window->DC.LastItemRectHoveredRect = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false);
4630     }
4631
4632     // Inner clipping rectangle
4633     // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
4634     const float border_size = window->WindowBorderSize;
4635     ImRect clip_rect;
4636     clip_rect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f)));
4637     clip_rect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + border_size);
4638     clip_rect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f)));
4639     clip_rect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - border_size);
4640     PushClipRect(clip_rect.Min, clip_rect.Max, true);
4641
4642     // Clear 'accessed' flag last thing (After PushClipRect which will set the flag. We want the flag to stay false when the default "Debug" window is unused)
4643     if (first_begin_of_the_frame)
4644         window->WriteAccessed = false;
4645
4646     window->BeginCount++;
4647     g.SetNextWindowSizeConstraint = false;
4648
4649     // Child window can be out of sight and have "negative" clip windows.
4650     // Mark them as collapsed so commands are skipped earlier (we can't manually collapse because they have no title bar).
4651     if (flags & ImGuiWindowFlags_ChildWindow)
4652     {
4653         IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
4654         window->Collapsed = parent_window && parent_window->Collapsed;
4655
4656         if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
4657             window->Collapsed |= (window->WindowRectClipped.Min.x >= window->WindowRectClipped.Max.x || window->WindowRectClipped.Min.y >= window->WindowRectClipped.Max.y);
4658
4659         // We also hide the window from rendering because we've already added its border to the command list.
4660         // (we could perform the check earlier in the function but it is simpler at this point)
4661         if (window->Collapsed)
4662             window->Active = false;
4663     }
4664     if (style.Alpha <= 0.0f)
4665         window->Active = false;
4666
4667     // Return false if we don't intend to display anything to allow user to perform an early out optimization
4668     window->SkipItems = (window->Collapsed || !window->Active) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0;
4669     return !window->SkipItems;
4670 }
4671
4672 // Old Begin() API with 5 parameters, avoid calling this version directly! Use SetNextWindowSize()+Begin() instead.
4673 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
4674 bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_use, float bg_alpha_override, ImGuiWindowFlags flags)
4675 {
4676     // Old API feature: we could pass the initial window size as a parameter, however this was very misleading because in most cases it would only affect the window when it didn't have storage in the .ini file.
4677     if (size_on_first_use.x != 0.0f || size_on_first_use.y != 0.0f)
4678         SetNextWindowSize(size_on_first_use, ImGuiCond_FirstUseEver);
4679
4680     // Old API feature: we could override the window background alpha with a parameter. This is actually tricky to reproduce manually because: 
4681     // (1) there are multiple variants of WindowBg (popup, tooltip, etc.) and (2) you can't call PushStyleColor before Begin and PopStyleColor just after Begin() because of how CheckStackSizes() behave.
4682     // The user-side solution is to do backup = GetStyleColorVec4(ImGuiCol_xxxBG), PushStyleColor(ImGuiCol_xxxBg), Begin, PushStyleColor(ImGuiCol_xxxBg, backup), [...], PopStyleColor(), End(); PopStyleColor() - which is super awkward.
4683     // The alpha override was rarely used but for now we'll leave the Begin() variant around for a bit. We may either lift the constraint on CheckStackSizes() either add a SetNextWindowBgAlpha() helper that does it magically.
4684     ImGuiContext& g = *GImGui;
4685     const ImGuiCol bg_color_idx = GetWindowBgColorIdxFromFlags(flags);
4686     const ImVec4 bg_color_backup = g.Style.Colors[bg_color_idx];
4687     if (bg_alpha_override >= 0.0f)
4688         g.Style.Colors[bg_color_idx].w = bg_alpha_override;
4689
4690     bool ret = Begin(name, p_open, flags);
4691
4692     if (bg_alpha_override >= 0.0f)
4693         g.Style.Colors[bg_color_idx] = bg_color_backup;
4694     return ret;
4695 }
4696 #endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS
4697
4698 void ImGui::End()
4699 {
4700     ImGuiContext& g = *GImGui;
4701     ImGuiWindow* window = g.CurrentWindow;
4702
4703     if (window->DC.ColumnsCount != 1) // close columns set if any is open
4704         EndColumns();
4705     PopClipRect();   // inner window clip rectangle
4706
4707     // Stop logging
4708     if (!(window->Flags & ImGuiWindowFlags_ChildWindow))    // FIXME: add more options for scope of logging
4709         LogFinish();
4710
4711     // Pop
4712     // NB: we don't clear 'window->RootWindow'. The pointer is allowed to live until the next call to Begin().
4713     g.CurrentWindowStack.pop_back();
4714     if (window->Flags & ImGuiWindowFlags_Popup)
4715         g.CurrentPopupStack.pop_back();
4716     CheckStacksSize(window, false);
4717     SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
4718 }
4719
4720 // Vertical scrollbar
4721 // The entire piece of code below is rather confusing because:
4722 // - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab)
4723 // - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar
4724 // - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal.
4725 void ImGui::Scrollbar(ImGuiLayoutType direction)
4726 {
4727     ImGuiContext& g = *GImGui;
4728     ImGuiWindow* window = g.CurrentWindow;
4729
4730     const bool horizontal = (direction == ImGuiLayoutType_Horizontal);
4731     const ImGuiStyle& style = g.Style;
4732     const ImGuiID id = window->GetID(horizontal ? "#SCROLLX" : "#SCROLLY");
4733
4734     // Render background
4735     bool other_scrollbar = (horizontal ? window->ScrollbarY : window->ScrollbarX);
4736     float other_scrollbar_size_w = other_scrollbar ? style.ScrollbarSize : 0.0f;
4737     const ImRect window_rect = window->Rect();
4738     const float border_size = window->WindowBorderSize;
4739     ImRect bb = horizontal
4740         ? 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)
4741         : 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);
4742     if (!horizontal)
4743         bb.Min.y += window->TitleBarHeight() + ((window->Flags & ImGuiWindowFlags_MenuBar) ? window->MenuBarHeight() : 0.0f);
4744     if (bb.GetWidth() <= 0.0f || bb.GetHeight() <= 0.0f)
4745         return;
4746
4747     int window_rounding_corners;
4748     if (horizontal)
4749         window_rounding_corners = ImDrawCornerFlags_BotLeft | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight);
4750     else
4751         window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImDrawCornerFlags_TopRight : 0) | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight);
4752     window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_ScrollbarBg), window->WindowRounding, window_rounding_corners);
4753     bb.Expand(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)));
4754
4755     // V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar)
4756     float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight();
4757     float scroll_v = horizontal ? window->Scroll.x : window->Scroll.y;
4758     float win_size_avail_v = (horizontal ? window->SizeFull.x : window->SizeFull.y) - other_scrollbar_size_w;
4759     float win_size_contents_v = horizontal ? window->SizeContents.x : window->SizeContents.y;
4760
4761     // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount)
4762     // But we maintain a minimum size in pixel to allow for the user to still aim inside.
4763     IM_ASSERT(ImMax(win_size_contents_v, win_size_avail_v) > 0.0f); // Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers.
4764     const float win_size_v = ImMax(ImMax(win_size_contents_v, win_size_avail_v), 1.0f);
4765     const float grab_h_pixels = ImClamp(scrollbar_size_v * (win_size_avail_v / win_size_v), style.GrabMinSize, scrollbar_size_v);
4766     const float grab_h_norm = grab_h_pixels / scrollbar_size_v;
4767
4768     // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar().
4769     bool held = false;
4770     bool hovered = false;
4771     const bool previously_held = (g.ActiveId == id);
4772     ButtonBehavior(bb, id, &hovered, &held);
4773
4774     float scroll_max = ImMax(1.0f, win_size_contents_v - win_size_avail_v);
4775     float scroll_ratio = ImSaturate(scroll_v / scroll_max);
4776     float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
4777     if (held && grab_h_norm < 1.0f)
4778     {
4779         float scrollbar_pos_v = horizontal ? bb.Min.x : bb.Min.y;
4780         float mouse_pos_v = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y;
4781         float* click_delta_to_grab_center_v = horizontal ? &g.ScrollbarClickDeltaToGrabCenter.x : &g.ScrollbarClickDeltaToGrabCenter.y;
4782
4783         // Click position in scrollbar normalized space (0.0f->1.0f)
4784         const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v);
4785         SetHoveredID(id);
4786
4787         bool seek_absolute = false;
4788         if (!previously_held)
4789         {
4790             // On initial click calculate the distance between mouse and the center of the grab
4791             if (clicked_v_norm >= grab_v_norm && clicked_v_norm <= grab_v_norm + grab_h_norm)
4792             {
4793                 *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f;
4794             }
4795             else
4796             {
4797                 seek_absolute = true;
4798                 *click_delta_to_grab_center_v = 0.0f;
4799             }
4800         }
4801
4802         // Apply scroll
4803         // 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
4804         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));
4805         scroll_v = (float)(int)(0.5f + scroll_v_norm * scroll_max);//(win_size_contents_v - win_size_v));
4806         if (horizontal)
4807             window->Scroll.x = scroll_v;
4808         else
4809             window->Scroll.y = scroll_v;
4810
4811         // Update values for rendering
4812         scroll_ratio = ImSaturate(scroll_v / scroll_max);
4813         grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
4814
4815         // Update distance to grab now that we have seeked and saturated
4816         if (seek_absolute)
4817             *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f;
4818     }
4819
4820     // Render
4821     const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab);
4822     ImRect grab_rect;
4823     if (horizontal)
4824         grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImMin(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, window_rect.Max.x), bb.Max.y);
4825     else
4826         grab_rect = ImRect(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm), bb.Max.x, ImMin(ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels, window_rect.Max.y));
4827     window->DrawList->AddRectFilled(grab_rect.Min, grab_rect.Max, grab_col, style.ScrollbarRounding);
4828 }
4829
4830 void ImGui::BringWindowToFront(ImGuiWindow* window)
4831 {
4832     ImGuiContext& g = *GImGui;
4833     if (g.Windows.back() == window)
4834         return;
4835     for (int i = 0; i < g.Windows.Size; i++)
4836         if (g.Windows[i] == window)
4837         {
4838             g.Windows.erase(g.Windows.begin() + i);
4839             g.Windows.push_back(window);
4840             break;
4841         }
4842 }
4843
4844 void ImGui::BringWindowToBack(ImGuiWindow* window)
4845 {
4846     ImGuiContext& g = *GImGui;
4847     if (g.Windows[0] == window)
4848         return;
4849     for (int i = 0; i < g.Windows.Size; i++)
4850         if (g.Windows[i] == window)
4851         {
4852             memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
4853             g.Windows[0] = window;
4854             break;
4855         }
4856 }
4857
4858 // Moving window to front of display and set focus (which happens to be back of our sorted list)
4859 void ImGui::FocusWindow(ImGuiWindow* window)
4860 {
4861     ImGuiContext& g = *GImGui;
4862
4863     // Always mark the window we passed as focused. This is used for keyboard interactions such as tabbing.
4864     g.NavWindow = window;
4865
4866     // Passing NULL allow to disable keyboard focus
4867     if (!window)
4868         return;
4869
4870     // Move the root window to the top of the pile
4871     if (window->RootWindow)
4872         window = window->RootWindow;
4873
4874     // Steal focus on active widgets
4875     if (window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement should be unnecessary. Need further testing before removing it..
4876         if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != window)
4877             ClearActiveID();
4878
4879     // Bring to front
4880     if (!(window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus))
4881         BringWindowToFront(window);
4882 }
4883
4884 void ImGui::FocusPreviousWindow()
4885 {
4886     ImGuiContext& g = *GImGui;
4887     for (int i = g.Windows.Size - 1; i >= 0; i--)
4888         if (g.Windows[i]->WasActive && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow))
4889         {
4890             FocusWindow(g.Windows[i]);
4891             return;
4892         }
4893 }
4894
4895 void ImGui::PushItemWidth(float item_width)
4896 {
4897     ImGuiWindow* window = GetCurrentWindow();
4898     window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
4899     window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
4900 }
4901
4902 void ImGui::PushMultiItemsWidths(int components, float w_full)
4903 {
4904     ImGuiWindow* window = GetCurrentWindow();
4905     const ImGuiStyle& style = GImGui->Style;
4906     if (w_full <= 0.0f)
4907         w_full = CalcItemWidth();
4908     const float w_item_one  = ImMax(1.0f, (float)(int)((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
4909     const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
4910     window->DC.ItemWidthStack.push_back(w_item_last);
4911     for (int i = 0; i < components-1; i++)
4912         window->DC.ItemWidthStack.push_back(w_item_one);
4913     window->DC.ItemWidth = window->DC.ItemWidthStack.back();
4914 }
4915
4916 void ImGui::PopItemWidth()
4917 {
4918     ImGuiWindow* window = GetCurrentWindow();
4919     window->DC.ItemWidthStack.pop_back();
4920     window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
4921 }
4922
4923 float ImGui::CalcItemWidth()
4924 {
4925     ImGuiWindow* window = GetCurrentWindowRead();
4926     float w = window->DC.ItemWidth;
4927     if (w < 0.0f)
4928     {
4929         // 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.
4930         float width_to_right_edge = GetContentRegionAvail().x;
4931         w = ImMax(1.0f, width_to_right_edge + w);
4932     }
4933     w = (float)(int)w;
4934     return w;
4935 }
4936
4937 static ImFont* GetDefaultFont()
4938 {
4939     ImGuiContext& g = *GImGui;
4940     return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0];
4941 }
4942
4943 static void SetCurrentFont(ImFont* font)
4944 {
4945     ImGuiContext& g = *GImGui;
4946     IM_ASSERT(font && font->IsLoaded());    // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
4947     IM_ASSERT(font->Scale > 0.0f);
4948     g.Font = font;
4949     g.FontBaseSize = g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale;
4950     g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
4951     g.FontTexUvWhitePixel = g.Font->ContainerAtlas->TexUvWhitePixel;
4952 }
4953
4954 void ImGui::PushFont(ImFont* font)
4955 {
4956     ImGuiContext& g = *GImGui;
4957     if (!font)
4958         font = GetDefaultFont();
4959     SetCurrentFont(font);
4960     g.FontStack.push_back(font);
4961     g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
4962 }
4963
4964 void  ImGui::PopFont()
4965 {
4966     ImGuiContext& g = *GImGui;
4967     g.CurrentWindow->DrawList->PopTextureID();
4968     g.FontStack.pop_back();
4969     SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
4970 }
4971
4972 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
4973 {
4974     ImGuiWindow* window = GetCurrentWindow();
4975     if (enabled)
4976         window->DC.ItemFlags |= option;
4977     else
4978         window->DC.ItemFlags &= ~option;
4979     window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
4980 }
4981
4982 void ImGui::PopItemFlag()
4983 {
4984     ImGuiWindow* window = GetCurrentWindow();
4985     window->DC.ItemFlagsStack.pop_back();
4986     window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back();
4987 }
4988
4989 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
4990 {
4991     PushItemFlag(ImGuiItemFlags_AllowKeyboardFocus, allow_keyboard_focus);
4992 }
4993
4994 void ImGui::PopAllowKeyboardFocus()
4995 {
4996     PopItemFlag();
4997 }
4998
4999 void ImGui::PushButtonRepeat(bool repeat)
5000 {
5001     PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
5002 }
5003
5004 void ImGui::PopButtonRepeat()
5005 {
5006     PopItemFlag();
5007 }
5008
5009 void ImGui::PushTextWrapPos(float wrap_pos_x)
5010 {
5011     ImGuiWindow* window = GetCurrentWindow();
5012     window->DC.TextWrapPos = wrap_pos_x;
5013     window->DC.TextWrapPosStack.push_back(wrap_pos_x);
5014 }
5015
5016 void ImGui::PopTextWrapPos()
5017 {
5018     ImGuiWindow* window = GetCurrentWindow();
5019     window->DC.TextWrapPosStack.pop_back();
5020     window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
5021 }
5022
5023 // FIXME: This may incur a round-trip (if the end user got their data from a float4) but eventually we aim to store the in-flight colors as ImU32
5024 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
5025 {
5026     ImGuiContext& g = *GImGui;
5027     ImGuiColMod backup;
5028     backup.Col = idx;
5029     backup.BackupValue = g.Style.Colors[idx];
5030     g.ColorModifiers.push_back(backup);
5031     g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
5032 }
5033
5034 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
5035 {
5036     ImGuiContext& g = *GImGui;
5037     ImGuiColMod backup;
5038     backup.Col = idx;
5039     backup.BackupValue = g.Style.Colors[idx];
5040     g.ColorModifiers.push_back(backup);
5041     g.Style.Colors[idx] = col;
5042 }
5043
5044 void ImGui::PopStyleColor(int count)
5045 {
5046     ImGuiContext& g = *GImGui;
5047     while (count > 0)
5048     {
5049         ImGuiColMod& backup = g.ColorModifiers.back();
5050         g.Style.Colors[backup.Col] = backup.BackupValue;
5051         g.ColorModifiers.pop_back();
5052         count--;
5053     }
5054 }
5055
5056 struct ImGuiStyleVarInfo
5057 {
5058     ImGuiDataType   Type;
5059     ImU32           Offset;
5060     void*           GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
5061 };
5062
5063 static const ImGuiStyleVarInfo GStyleVarInfo[ImGuiStyleVar_Count_] =
5064 {
5065     { ImGuiDataType_Float,  (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) },                // ImGuiStyleVar_Alpha
5066     { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) },        // ImGuiStyleVar_WindowPadding
5067     { ImGuiDataType_Float,  (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) },       // ImGuiStyleVar_WindowRounding
5068     { ImGuiDataType_Float,  (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) },     // ImGuiStyleVar_WindowBorderSize
5069     { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) },        // ImGuiStyleVar_WindowMinSize
5070     { ImGuiDataType_Float,  (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) },        // ImGuiStyleVar_ChildRounding
5071     { ImGuiDataType_Float,  (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) },      // ImGuiStyleVar_ChildBorderSize
5072     { ImGuiDataType_Float,  (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) },        // ImGuiStyleVar_PopupRounding
5073     { ImGuiDataType_Float,  (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) },      // ImGuiStyleVar_PopupBorderSize
5074     { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) },         // ImGuiStyleVar_FramePadding
5075     { ImGuiDataType_Float,  (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) },        // ImGuiStyleVar_FrameRounding
5076     { ImGuiDataType_Float,  (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) },      // ImGuiStyleVar_FrameBorderSize
5077     { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) },          // ImGuiStyleVar_ItemSpacing
5078     { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) },     // ImGuiStyleVar_ItemInnerSpacing
5079     { ImGuiDataType_Float,  (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) },        // ImGuiStyleVar_IndentSpacing
5080     { ImGuiDataType_Float,  (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) },          // ImGuiStyleVar_GrabMinSize
5081     { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) },      // ImGuiStyleVar_ButtonTextAlign
5082 };
5083
5084 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
5085 {
5086     IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_Count_);
5087     return &GStyleVarInfo[idx];
5088 }
5089
5090 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
5091 {
5092     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
5093     if (var_info->Type == ImGuiDataType_Float)
5094     {
5095         ImGuiContext& g = *GImGui;
5096         float* pvar = (float*)var_info->GetVarPtr(&g.Style);
5097         g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
5098         *pvar = val;
5099         return;
5100     }
5101     IM_ASSERT(0); // Called function with wrong-type? Variable is not a float.
5102 }
5103
5104 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
5105 {
5106     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
5107     if (var_info->Type == ImGuiDataType_Float2)
5108     {
5109         ImGuiContext& g = *GImGui;
5110         ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
5111         g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
5112         *pvar = val;
5113         return;
5114     }
5115     IM_ASSERT(0); // Called function with wrong-type? Variable is not a ImVec2.
5116 }
5117
5118 void ImGui::PopStyleVar(int count)
5119 {
5120     ImGuiContext& g = *GImGui;
5121     while (count > 0)
5122     {
5123         ImGuiStyleMod& backup = g.StyleModifiers.back();
5124         const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
5125         if (info->Type == ImGuiDataType_Float)          (*(float*)info->GetVarPtr(&g.Style)) = backup.BackupFloat[0];
5126         else if (info->Type == ImGuiDataType_Float2)    (*(ImVec2*)info->GetVarPtr(&g.Style)) = ImVec2(backup.BackupFloat[0], backup.BackupFloat[1]);
5127         else if (info->Type == ImGuiDataType_Int)       (*(int*)info->GetVarPtr(&g.Style)) = backup.BackupInt[0];
5128         g.StyleModifiers.pop_back();
5129         count--;
5130     }
5131 }
5132
5133 const char* ImGui::GetStyleColorName(ImGuiCol idx)
5134 {
5135     // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
5136     switch (idx)
5137     {
5138     case ImGuiCol_Text: return "Text";
5139     case ImGuiCol_TextDisabled: return "TextDisabled";
5140     case ImGuiCol_WindowBg: return "WindowBg";
5141     case ImGuiCol_ChildBg: return "ChildBg";
5142     case ImGuiCol_PopupBg: return "PopupBg";
5143     case ImGuiCol_Border: return "Border";
5144     case ImGuiCol_BorderShadow: return "BorderShadow";
5145     case ImGuiCol_FrameBg: return "FrameBg";
5146     case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
5147     case ImGuiCol_FrameBgActive: return "FrameBgActive";
5148     case ImGuiCol_TitleBg: return "TitleBg";
5149     case ImGuiCol_TitleBgActive: return "TitleBgActive";
5150     case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
5151     case ImGuiCol_MenuBarBg: return "MenuBarBg";
5152     case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
5153     case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
5154     case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
5155     case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
5156     case ImGuiCol_CheckMark: return "CheckMark";
5157     case ImGuiCol_SliderGrab: return "SliderGrab";
5158     case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
5159     case ImGuiCol_Button: return "Button";
5160     case ImGuiCol_ButtonHovered: return "ButtonHovered";
5161     case ImGuiCol_ButtonActive: return "ButtonActive";
5162     case ImGuiCol_Header: return "Header";
5163     case ImGuiCol_HeaderHovered: return "HeaderHovered";
5164     case ImGuiCol_HeaderActive: return "HeaderActive";
5165     case ImGuiCol_Separator: return "Separator";
5166     case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
5167     case ImGuiCol_SeparatorActive: return "SeparatorActive";
5168     case ImGuiCol_ResizeGrip: return "ResizeGrip";
5169     case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
5170     case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
5171     case ImGuiCol_CloseButton: return "CloseButton";
5172     case ImGuiCol_CloseButtonHovered: return "CloseButtonHovered";
5173     case ImGuiCol_CloseButtonActive: return "CloseButtonActive";
5174     case ImGuiCol_PlotLines: return "PlotLines";
5175     case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
5176     case ImGuiCol_PlotHistogram: return "PlotHistogram";
5177     case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
5178     case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
5179     case ImGuiCol_ModalWindowDarkening: return "ModalWindowDarkening";
5180     }
5181     IM_ASSERT(0);
5182     return "Unknown";
5183 }
5184
5185 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
5186 {
5187     IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0);   // Flags not supported by this function
5188     ImGuiContext& g = *GImGui;
5189     if (flags & ImGuiHoveredFlags_FlattenChilds)
5190     {
5191         if (g.HoveredRootWindow != g.CurrentWindow->RootWindow)
5192             return false;
5193     }
5194     else
5195     {
5196         if (g.HoveredWindow != g.CurrentWindow)
5197             return false;
5198     }
5199     if (!IsWindowContentHoverable(g.HoveredRootWindow, flags))
5200         return false;
5201     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
5202         if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
5203             return false;
5204     return true;
5205 }
5206
5207 bool ImGui::IsWindowFocused()
5208 {
5209     ImGuiContext& g = *GImGui;
5210     IM_ASSERT(g.CurrentWindow);     // Not inside a Begin()/End()
5211     return g.NavWindow == g.CurrentWindow;
5212 }
5213
5214 bool ImGui::IsRootWindowFocused()
5215 {
5216     ImGuiContext& g = *GImGui;
5217     IM_ASSERT(g.CurrentWindow);     // Not inside a Begin()/End()
5218     return g.NavWindow == g.CurrentWindow->RootWindow;
5219 }
5220
5221 bool ImGui::IsRootWindowOrAnyChildFocused()
5222 {
5223     ImGuiContext& g = *GImGui;
5224     IM_ASSERT(g.CurrentWindow);     // Not inside a Begin()/End()
5225     return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
5226 }
5227
5228 float ImGui::GetWindowWidth()
5229 {
5230     ImGuiWindow* window = GImGui->CurrentWindow;
5231     return window->Size.x;
5232 }
5233
5234 float ImGui::GetWindowHeight()
5235 {
5236     ImGuiWindow* window = GImGui->CurrentWindow;
5237     return window->Size.y;
5238 }
5239
5240 ImVec2 ImGui::GetWindowPos()
5241 {
5242     ImGuiContext& g = *GImGui;
5243     ImGuiWindow* window = g.CurrentWindow;
5244     return window->Pos;
5245 }
5246
5247 static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y)
5248 {
5249     window->DC.CursorMaxPos.y += window->Scroll.y; // SizeContents is generally computed based on CursorMaxPos which is affected by scroll position, so we need to apply our change to it.
5250     window->Scroll.y = new_scroll_y;
5251     window->DC.CursorMaxPos.y -= window->Scroll.y;
5252 }
5253
5254 static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
5255 {
5256     // Test condition (NB: bit 0 is always true) and clear flags for next time
5257     if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
5258         return;
5259     window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5260     window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
5261
5262     // Set
5263     const ImVec2 old_pos = window->Pos;
5264     window->PosFloat = pos;
5265     window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
5266     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
5267     window->DC.CursorMaxPos += (window->Pos - old_pos); // And more importantly we need to adjust this so size calculation doesn't get affected.
5268 }
5269
5270 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
5271 {
5272     ImGuiWindow* window = GetCurrentWindowRead();
5273     SetWindowPos(window, pos, cond);
5274 }
5275
5276 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
5277 {
5278     if (ImGuiWindow* window = FindWindowByName(name))
5279         SetWindowPos(window, pos, cond);
5280 }
5281
5282 ImVec2 ImGui::GetWindowSize()
5283 {
5284     ImGuiWindow* window = GetCurrentWindowRead();
5285     return window->Size;
5286 }
5287
5288 static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
5289 {
5290     // Test condition (NB: bit 0 is always true) and clear flags for next time
5291     if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
5292         return;
5293     window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5294
5295     // Set
5296     if (size.x > 0.0f)
5297     {
5298         window->AutoFitFramesX = 0;
5299         window->SizeFull.x = size.x;
5300     }
5301     else
5302     {
5303         window->AutoFitFramesX = 2;
5304         window->AutoFitOnlyGrows = false;
5305     }
5306     if (size.y > 0.0f)
5307     {
5308         window->AutoFitFramesY = 0;
5309         window->SizeFull.y = size.y;
5310     }
5311     else
5312     {
5313         window->AutoFitFramesY = 2;
5314         window->AutoFitOnlyGrows = false;
5315     }
5316 }
5317
5318 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
5319 {
5320     SetWindowSize(GImGui->CurrentWindow, size, cond);
5321 }
5322
5323 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
5324 {
5325     if (ImGuiWindow* window = FindWindowByName(name))
5326         SetWindowSize(window, size, cond);
5327 }
5328
5329 static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
5330 {
5331     // Test condition (NB: bit 0 is always true) and clear flags for next time
5332     if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
5333         return;
5334     window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5335
5336     // Set
5337     window->Collapsed = collapsed;
5338 }
5339
5340 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
5341 {
5342     SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
5343 }
5344
5345 bool ImGui::IsWindowCollapsed()
5346 {
5347     ImGuiWindow* window = GetCurrentWindowRead();
5348     return window->Collapsed;
5349 }
5350
5351 bool ImGui::IsWindowAppearing()
5352 {
5353     ImGuiWindow* window = GetCurrentWindowRead();
5354     return window->Appearing;
5355 }
5356
5357 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
5358 {
5359     if (ImGuiWindow* window = FindWindowByName(name))
5360         SetWindowCollapsed(window, collapsed, cond);
5361 }
5362
5363 void ImGui::SetWindowFocus()
5364 {
5365     FocusWindow(GImGui->CurrentWindow);
5366 }
5367
5368 void ImGui::SetWindowFocus(const char* name)
5369 {
5370     if (name)
5371     {
5372         if (ImGuiWindow* window = FindWindowByName(name))
5373             FocusWindow(window);
5374     }
5375     else
5376     {
5377         FocusWindow(NULL);
5378     }
5379 }
5380
5381 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
5382 {
5383     ImGuiContext& g = *GImGui;
5384     g.SetNextWindowPosVal = pos;
5385     g.SetNextWindowPosPivot = pivot;
5386     g.SetNextWindowPosCond = cond ? cond : ImGuiCond_Always;
5387 }
5388
5389 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
5390 {
5391     ImGuiContext& g = *GImGui;
5392     g.SetNextWindowSizeVal = size;
5393     g.SetNextWindowSizeCond = cond ? cond : ImGuiCond_Always;
5394 }
5395
5396 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeConstraintCallback custom_callback, void* custom_callback_user_data)
5397 {
5398     ImGuiContext& g = *GImGui;
5399     g.SetNextWindowSizeConstraint = true;
5400     g.SetNextWindowSizeConstraintRect = ImRect(size_min, size_max);
5401     g.SetNextWindowSizeConstraintCallback = custom_callback;
5402     g.SetNextWindowSizeConstraintCallbackUserData = custom_callback_user_data;
5403 }
5404
5405 void ImGui::SetNextWindowContentSize(const ImVec2& size)
5406 {
5407     ImGuiContext& g = *GImGui;
5408     g.SetNextWindowContentSizeVal = size;
5409     g.SetNextWindowContentSizeCond = ImGuiCond_Always;
5410 }
5411
5412 void ImGui::SetNextWindowContentWidth(float width)
5413 {
5414     ImGuiContext& g = *GImGui;
5415     g.SetNextWindowContentSizeVal = ImVec2(width, g.SetNextWindowContentSizeCond ? g.SetNextWindowContentSizeVal.y : 0.0f);
5416     g.SetNextWindowContentSizeCond = ImGuiCond_Always;
5417 }
5418
5419 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
5420 {
5421     ImGuiContext& g = *GImGui;
5422     g.SetNextWindowCollapsedVal = collapsed;
5423     g.SetNextWindowCollapsedCond = cond ? cond : ImGuiCond_Always;
5424 }
5425
5426 void ImGui::SetNextWindowFocus()
5427 {
5428     ImGuiContext& g = *GImGui;
5429     g.SetNextWindowFocus = true;
5430 }
5431
5432 // In window space (not screen space!)
5433 ImVec2 ImGui::GetContentRegionMax()
5434 {
5435     ImGuiWindow* window = GetCurrentWindowRead();
5436     ImVec2 mx = window->ContentsRegionRect.Max;
5437     if (window->DC.ColumnsCount != 1)
5438         mx.x = GetColumnOffset(window->DC.ColumnsCurrent + 1) - window->WindowPadding.x;
5439     return mx;
5440 }
5441
5442 ImVec2 ImGui::GetContentRegionAvail()
5443 {
5444     ImGuiWindow* window = GetCurrentWindowRead();
5445     return GetContentRegionMax() - (window->DC.CursorPos - window->Pos);
5446 }
5447
5448 float ImGui::GetContentRegionAvailWidth()
5449 {
5450     return GetContentRegionAvail().x;
5451 }
5452
5453 // In window space (not screen space!)
5454 ImVec2 ImGui::GetWindowContentRegionMin()
5455 {
5456     ImGuiWindow* window = GetCurrentWindowRead();
5457     return window->ContentsRegionRect.Min;
5458 }
5459
5460 ImVec2 ImGui::GetWindowContentRegionMax()
5461 {
5462     ImGuiWindow* window = GetCurrentWindowRead();
5463     return window->ContentsRegionRect.Max;
5464 }
5465
5466 float ImGui::GetWindowContentRegionWidth()
5467 {
5468     ImGuiWindow* window = GetCurrentWindowRead();
5469     return window->ContentsRegionRect.Max.x - window->ContentsRegionRect.Min.x;
5470 }
5471
5472 float ImGui::GetTextLineHeight()
5473 {
5474     ImGuiContext& g = *GImGui;
5475     return g.FontSize;
5476 }
5477
5478 float ImGui::GetTextLineHeightWithSpacing()
5479 {
5480     ImGuiContext& g = *GImGui;
5481     return g.FontSize + g.Style.ItemSpacing.y;
5482 }
5483
5484 float ImGui::GetItemsLineHeightWithSpacing()
5485 {
5486     ImGuiContext& g = *GImGui;
5487     return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
5488 }
5489
5490 ImDrawList* ImGui::GetWindowDrawList()
5491 {
5492     ImGuiWindow* window = GetCurrentWindow();
5493     return window->DrawList;
5494 }
5495
5496 ImFont* ImGui::GetFont()
5497 {
5498     return GImGui->Font;
5499 }
5500
5501 float ImGui::GetFontSize()
5502 {
5503     return GImGui->FontSize;
5504 }
5505
5506 ImVec2 ImGui::GetFontTexUvWhitePixel()
5507 {
5508     return GImGui->FontTexUvWhitePixel;
5509 }
5510
5511 void ImGui::SetWindowFontScale(float scale)
5512 {
5513     ImGuiContext& g = *GImGui;
5514     ImGuiWindow* window = GetCurrentWindow();
5515     window->FontWindowScale = scale;
5516     g.FontSize = window->CalcFontSize();
5517 }
5518
5519 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
5520 // 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'.
5521 ImVec2 ImGui::GetCursorPos()
5522 {
5523     ImGuiWindow* window = GetCurrentWindowRead();
5524     return window->DC.CursorPos - window->Pos + window->Scroll;
5525 }
5526
5527 float ImGui::GetCursorPosX()
5528 {
5529     ImGuiWindow* window = GetCurrentWindowRead();
5530     return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
5531 }
5532
5533 float ImGui::GetCursorPosY()
5534 {
5535     ImGuiWindow* window = GetCurrentWindowRead();
5536     return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
5537 }
5538
5539 void ImGui::SetCursorPos(const ImVec2& local_pos)
5540 {
5541     ImGuiWindow* window = GetCurrentWindow();
5542     window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
5543     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
5544 }
5545
5546 void ImGui::SetCursorPosX(float x)
5547 {
5548     ImGuiWindow* window = GetCurrentWindow();
5549     window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
5550     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
5551 }
5552
5553 void ImGui::SetCursorPosY(float y)
5554 {
5555     ImGuiWindow* window = GetCurrentWindow();
5556     window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
5557     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
5558 }
5559
5560 ImVec2 ImGui::GetCursorStartPos()
5561 {
5562     ImGuiWindow* window = GetCurrentWindowRead();
5563     return window->DC.CursorStartPos - window->Pos;
5564 }
5565
5566 ImVec2 ImGui::GetCursorScreenPos()
5567 {
5568     ImGuiWindow* window = GetCurrentWindowRead();
5569     return window->DC.CursorPos;
5570 }
5571
5572 void ImGui::SetCursorScreenPos(const ImVec2& screen_pos)
5573 {
5574     ImGuiWindow* window = GetCurrentWindow();
5575     window->DC.CursorPos = screen_pos;
5576     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
5577 }
5578
5579 float ImGui::GetScrollX()
5580 {
5581     return GImGui->CurrentWindow->Scroll.x;
5582 }
5583
5584 float ImGui::GetScrollY()
5585 {
5586     return GImGui->CurrentWindow->Scroll.y;
5587 }
5588
5589 float ImGui::GetScrollMaxX()
5590 {
5591     ImGuiWindow* window = GetCurrentWindowRead();
5592     return ImMax(0.0f, window->SizeContents.x - (window->SizeFull.x - window->ScrollbarSizes.x));
5593 }
5594
5595 float ImGui::GetScrollMaxY()
5596 {
5597     ImGuiWindow* window = GetCurrentWindowRead();
5598     return ImMax(0.0f, window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y));
5599 }
5600
5601 void ImGui::SetScrollX(float scroll_x)
5602 {
5603     ImGuiWindow* window = GetCurrentWindow();
5604     window->ScrollTarget.x = scroll_x;
5605     window->ScrollTargetCenterRatio.x = 0.0f;
5606 }
5607
5608 void ImGui::SetScrollY(float scroll_y)
5609 {
5610     ImGuiWindow* window = GetCurrentWindow();
5611     window->ScrollTarget.y = scroll_y + window->TitleBarHeight() + window->MenuBarHeight(); // title bar height canceled out when using ScrollTargetRelY
5612     window->ScrollTargetCenterRatio.y = 0.0f;
5613 }
5614
5615 void ImGui::SetScrollFromPosY(float pos_y, float center_y_ratio)
5616 {
5617     // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
5618     ImGuiWindow* window = GetCurrentWindow();
5619     IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
5620     window->ScrollTarget.y = (float)(int)(pos_y + window->Scroll.y);
5621     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)
5622         window->ScrollTarget.y = 0.0f;
5623     window->ScrollTargetCenterRatio.y = center_y_ratio;
5624 }
5625
5626 // center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
5627 void ImGui::SetScrollHere(float center_y_ratio)
5628 {
5629     ImGuiWindow* window = GetCurrentWindow();
5630     float target_y = window->DC.CursorPosPrevLine.y - window->Pos.y; // Top of last item, in window space
5631     target_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.
5632     SetScrollFromPosY(target_y, center_y_ratio);
5633 }
5634
5635 void ImGui::SetKeyboardFocusHere(int offset)
5636 {
5637     IM_ASSERT(offset >= -1);    // -1 is allowed but not below
5638     ImGuiWindow* window = GetCurrentWindow();
5639     window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset;
5640     window->FocusIdxTabRequestNext = INT_MAX;
5641 }
5642
5643 void ImGui::SetStateStorage(ImGuiStorage* tree)
5644 {
5645     ImGuiWindow* window = GetCurrentWindow();
5646     window->DC.StateStorage = tree ? tree : &window->StateStorage;
5647 }
5648
5649 ImGuiStorage* ImGui::GetStateStorage()
5650 {
5651     ImGuiWindow* window = GetCurrentWindowRead();
5652     return window->DC.StateStorage;
5653 }
5654
5655 void ImGui::TextV(const char* fmt, va_list args)
5656 {
5657     ImGuiWindow* window = GetCurrentWindow();
5658     if (window->SkipItems)
5659         return;
5660
5661     ImGuiContext& g = *GImGui;
5662     const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
5663     TextUnformatted(g.TempBuffer, text_end);
5664 }
5665
5666 void ImGui::Text(const char* fmt, ...)
5667 {
5668     va_list args;
5669     va_start(args, fmt);
5670     TextV(fmt, args);
5671     va_end(args);
5672 }
5673
5674 void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args)
5675 {
5676     PushStyleColor(ImGuiCol_Text, col);
5677     TextV(fmt, args);
5678     PopStyleColor();
5679 }
5680
5681 void ImGui::TextColored(const ImVec4& col, const char* fmt, ...)
5682 {
5683     va_list args;
5684     va_start(args, fmt);
5685     TextColoredV(col, fmt, args);
5686     va_end(args);
5687 }
5688
5689 void ImGui::TextDisabledV(const char* fmt, va_list args)
5690 {
5691     PushStyleColor(ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled]);
5692     TextV(fmt, args);
5693     PopStyleColor();
5694 }
5695
5696 void ImGui::TextDisabled(const char* fmt, ...)
5697 {
5698     va_list args;
5699     va_start(args, fmt);
5700     TextDisabledV(fmt, args);
5701     va_end(args);
5702 }
5703
5704 void ImGui::TextWrappedV(const char* fmt, va_list args)
5705 {
5706     bool need_wrap = (GImGui->CurrentWindow->DC.TextWrapPos < 0.0f);    // Keep existing wrap position is one ia already set
5707     if (need_wrap) PushTextWrapPos(0.0f);
5708     TextV(fmt, args);
5709     if (need_wrap) PopTextWrapPos();
5710 }
5711
5712 void ImGui::TextWrapped(const char* fmt, ...)
5713 {
5714     va_list args;
5715     va_start(args, fmt);
5716     TextWrappedV(fmt, args);
5717     va_end(args);
5718 }
5719
5720 void ImGui::TextUnformatted(const char* text, const char* text_end)
5721 {
5722     ImGuiWindow* window = GetCurrentWindow();
5723     if (window->SkipItems)
5724         return;
5725
5726     ImGuiContext& g = *GImGui;
5727     IM_ASSERT(text != NULL);
5728     const char* text_begin = text;
5729     if (text_end == NULL)
5730         text_end = text + strlen(text); // FIXME-OPT
5731
5732     const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrentLineTextBaseOffset);
5733     const float wrap_pos_x = window->DC.TextWrapPos;
5734     const bool wrap_enabled = wrap_pos_x >= 0.0f;
5735     if (text_end - text > 2000 && !wrap_enabled)
5736     {
5737         // Long text!
5738         // Perform manual coarse clipping to optimize for long multi-line text
5739         // From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled.
5740         // 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.
5741         const char* line = text;
5742         const float line_height = GetTextLineHeight();
5743         const ImRect clip_rect = window->ClipRect;
5744         ImVec2 text_size(0,0);
5745
5746         if (text_pos.y <= clip_rect.Max.y)
5747         {
5748             ImVec2 pos = text_pos;
5749
5750             // Lines to skip (can't skip when logging text)
5751             if (!g.LogEnabled)
5752             {
5753                 int lines_skippable = (int)((clip_rect.Min.y - text_pos.y) / line_height);
5754                 if (lines_skippable > 0)
5755                 {
5756                     int lines_skipped = 0;
5757                     while (line < text_end && lines_skipped < lines_skippable)
5758                     {
5759                         const char* line_end = strchr(line, '\n');
5760                         if (!line_end)
5761                             line_end = text_end;
5762                         line = line_end + 1;
5763                         lines_skipped++;
5764                     }
5765                     pos.y += lines_skipped * line_height;
5766                 }
5767             }
5768
5769             // Lines to render
5770             if (line < text_end)
5771             {
5772                 ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height));
5773                 while (line < text_end)
5774                 {
5775                     const char* line_end = strchr(line, '\n');
5776                     if (IsClippedEx(line_rect, 0, false))
5777                         break;
5778
5779                     const ImVec2 line_size = CalcTextSize(line, line_end, false);
5780                     text_size.x = ImMax(text_size.x, line_size.x);
5781                     RenderText(pos, line, line_end, false);
5782                     if (!line_end)
5783                         line_end = text_end;
5784                     line = line_end + 1;
5785                     line_rect.Min.y += line_height;
5786                     line_rect.Max.y += line_height;
5787                     pos.y += line_height;
5788                 }
5789
5790                 // Count remaining lines
5791                 int lines_skipped = 0;
5792                 while (line < text_end)
5793                 {
5794                     const char* line_end = strchr(line, '\n');
5795                     if (!line_end)
5796                         line_end = text_end;
5797                     line = line_end + 1;
5798                     lines_skipped++;
5799                 }
5800                 pos.y += lines_skipped * line_height;
5801             }
5802
5803             text_size.y += (pos - text_pos).y;
5804         }
5805
5806         ImRect bb(text_pos, text_pos + text_size);
5807         ItemSize(bb);
5808         ItemAdd(bb, 0);
5809     }
5810     else
5811     {
5812         const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f;
5813         const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width);
5814
5815         // Account of baseline offset
5816         ImRect bb(text_pos, text_pos + text_size);
5817         ItemSize(text_size);
5818         if (!ItemAdd(bb, 0))
5819             return;
5820
5821         // Render (we don't hide text after ## in this end-user function)
5822         RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width);
5823     }
5824 }
5825
5826 void ImGui::AlignTextToFramePadding()
5827 {
5828     ImGuiWindow* window = GetCurrentWindow();
5829     if (window->SkipItems)
5830         return;
5831
5832     ImGuiContext& g = *GImGui;
5833     window->DC.CurrentLineHeight = ImMax(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y * 2);
5834     window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y);
5835 }
5836
5837 // Add a label+text combo aligned to other label+value widgets
5838 void ImGui::LabelTextV(const char* label, const char* fmt, va_list args)
5839 {
5840     ImGuiWindow* window = GetCurrentWindow();
5841     if (window->SkipItems)
5842         return;
5843
5844     ImGuiContext& g = *GImGui;
5845     const ImGuiStyle& style = g.Style;
5846     const float w = CalcItemWidth();
5847
5848     const ImVec2 label_size = CalcTextSize(label, NULL, true);
5849     const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2));
5850     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);
5851     ItemSize(total_bb, style.FramePadding.y);
5852     if (!ItemAdd(total_bb, 0))
5853         return;
5854
5855     // Render
5856     const char* value_text_begin = &g.TempBuffer[0];
5857     const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
5858     RenderTextClipped(value_bb.Min, value_bb.Max, value_text_begin, value_text_end, NULL, ImVec2(0.0f,0.5f));
5859     if (label_size.x > 0.0f)
5860         RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label);
5861 }
5862
5863 void ImGui::LabelText(const char* label, const char* fmt, ...)
5864 {
5865     va_list args;
5866     va_start(args, fmt);
5867     LabelTextV(label, fmt, args);
5868     va_end(args);
5869 }
5870
5871 bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags)
5872 {
5873     ImGuiContext& g = *GImGui;
5874     ImGuiWindow* window = GetCurrentWindow();
5875
5876     if (flags & ImGuiButtonFlags_Disabled)
5877     {
5878         if (out_hovered) *out_hovered = false;
5879         if (out_held) *out_held = false;
5880         if (g.ActiveId == id) ClearActiveID();
5881         return false;
5882     }
5883
5884     // Default behavior requires click+release on same spot
5885     if ((flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick)) == 0)
5886         flags |= ImGuiButtonFlags_PressedOnClickRelease;
5887
5888     ImGuiWindow* backup_hovered_window = g.HoveredWindow;
5889     if ((flags & ImGuiButtonFlags_FlattenChilds) && g.HoveredRootWindow == window)
5890         g.HoveredWindow = window;
5891
5892     bool pressed = false;
5893     bool hovered = ItemHoverable(bb, id);
5894
5895     if ((flags & ImGuiButtonFlags_FlattenChilds) && g.HoveredRootWindow == window)
5896         g.HoveredWindow = backup_hovered_window;
5897
5898     // 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.
5899     if (hovered && (flags & ImGuiButtonFlags_AllowOverlapMode) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0))
5900         hovered = false;
5901
5902     if (hovered)
5903     {
5904         if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt))
5905         {
5906             //                        | CLICKING        | HOLDING with ImGuiButtonFlags_Repeat
5907             // PressedOnClickRelease  |  <on release>*  |  <on repeat> <on repeat> .. (NOT on release)  <-- MOST COMMON! (*) only if both click/release were over bounds
5908             // PressedOnClick         |  <on click>     |  <on click> <on repeat> <on repeat> ..
5909             // PressedOnRelease       |  <on release>   |  <on repeat> <on repeat> .. (NOT on release)
5910             // PressedOnDoubleClick   |  <on dclick>    |  <on dclick> <on repeat> <on repeat> ..
5911             if ((flags & ImGuiButtonFlags_PressedOnClickRelease) && g.IO.MouseClicked[0])
5912             {
5913                 SetActiveID(id, window); // Hold on ID
5914                 FocusWindow(window);
5915                 g.ActiveIdClickOffset = g.IO.MousePos - bb.Min;
5916             }
5917             if (((flags & ImGuiButtonFlags_PressedOnClick) && g.IO.MouseClicked[0]) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDoubleClicked[0]))
5918             {
5919                 pressed = true;
5920                 if (flags & ImGuiButtonFlags_NoHoldingActiveID)
5921                 {
5922                     ClearActiveID();
5923                 }
5924                 else
5925                 {
5926                     SetActiveID(id, window); // Hold on ID
5927                     g.ActiveIdClickOffset = g.IO.MousePos - bb.Min;
5928                 }
5929                 FocusWindow(window);
5930             }
5931             if ((flags & ImGuiButtonFlags_PressedOnRelease) && g.IO.MouseReleased[0])
5932             {
5933                 if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay))  // Repeat mode trumps <on release>
5934                     pressed = true;
5935                 ClearActiveID();
5936             }
5937
5938             // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above). 
5939             // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings.
5940             if ((flags & ImGuiButtonFlags_Repeat) && g.ActiveId == id && g.IO.MouseDownDuration[0] > 0.0f && IsMouseClicked(0, true))
5941                 pressed = true;
5942         }
5943     }
5944
5945     bool held = false;
5946     if (g.ActiveId == id)
5947     {
5948         if (g.IO.MouseDown[0])
5949         {
5950             held = true;
5951         }
5952         else
5953         {
5954             if (hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease))
5955                 if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay))  // Repeat mode trumps <on release>
5956                     pressed = true;
5957             ClearActiveID();
5958         }
5959     }
5960
5961     if (out_hovered) *out_hovered = hovered;
5962     if (out_held) *out_held = held;
5963
5964     return pressed;
5965 }
5966
5967 bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags)
5968 {
5969     ImGuiWindow* window = GetCurrentWindow();
5970     if (window->SkipItems)
5971         return false;
5972
5973     ImGuiContext& g = *GImGui;
5974     const ImGuiStyle& style = g.Style;
5975     const ImGuiID id = window->GetID(label);
5976     const ImVec2 label_size = CalcTextSize(label, NULL, true);
5977
5978     ImVec2 pos = window->DC.CursorPos;
5979     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)
5980         pos.y += window->DC.CurrentLineTextBaseOffset - style.FramePadding.y;
5981     ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f);
5982
5983     const ImRect bb(pos, pos + size);
5984     ItemSize(bb, style.FramePadding.y);
5985     if (!ItemAdd(bb, id))
5986         return false;
5987
5988     if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) flags |= ImGuiButtonFlags_Repeat;
5989     bool hovered, held;
5990     bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
5991
5992     // Render
5993     const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
5994     RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
5995     RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb);
5996
5997     // Automatically close popups
5998     //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
5999     //    CloseCurrentPopup();
6000
6001     return pressed;
6002 }
6003
6004 bool ImGui::Button(const char* label, const ImVec2& size_arg)
6005 {
6006     return ButtonEx(label, size_arg, 0);
6007 }
6008
6009 // Small buttons fits within text without additional vertical spacing.
6010 bool ImGui::SmallButton(const char* label)
6011 {
6012     ImGuiContext& g = *GImGui;
6013     float backup_padding_y = g.Style.FramePadding.y;
6014     g.Style.FramePadding.y = 0.0f;
6015     bool pressed = ButtonEx(label, ImVec2(0,0), ImGuiButtonFlags_AlignTextBaseLine);
6016     g.Style.FramePadding.y = backup_padding_y;
6017     return pressed;
6018 }
6019
6020 // Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack.
6021 // 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)
6022 bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg)
6023 {
6024     ImGuiWindow* window = GetCurrentWindow();
6025     if (window->SkipItems)
6026         return false;
6027
6028     const ImGuiID id = window->GetID(str_id);
6029     ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f);
6030     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
6031     ItemSize(bb);
6032     if (!ItemAdd(bb, id))
6033         return false;
6034
6035     bool hovered, held;
6036     bool pressed = ButtonBehavior(bb, id, &hovered, &held);
6037
6038     return pressed;
6039 }
6040
6041 // Upper-right button to close a window.
6042 bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius)
6043 {
6044     ImGuiWindow* window = GetCurrentWindow();
6045
6046     const ImRect bb(pos - ImVec2(radius,radius), pos + ImVec2(radius,radius));
6047
6048     bool hovered, held;
6049     bool pressed = ButtonBehavior(bb, id, &hovered, &held);
6050
6051     // Render
6052     const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_CloseButtonActive : hovered ? ImGuiCol_CloseButtonHovered : ImGuiCol_CloseButton);
6053     const ImVec2 center = bb.GetCenter();
6054     window->DrawList->AddCircleFilled(center, ImMax(2.0f, radius), col, 12);
6055
6056     const float cross_extent = (radius * 0.7071f) - 1.0f;
6057     if (hovered)
6058     {
6059         window->DrawList->AddLine(center + ImVec2(+cross_extent,+cross_extent), center + ImVec2(-cross_extent,-cross_extent), GetColorU32(ImGuiCol_Text));
6060         window->DrawList->AddLine(center + ImVec2(+cross_extent,-cross_extent), center + ImVec2(-cross_extent,+cross_extent), GetColorU32(ImGuiCol_Text));
6061     }
6062
6063     return pressed;
6064 }
6065
6066 // [Internal]
6067 bool ImGui::ArrowButton(ImGuiID id, ImGuiDir dir, ImVec2 padding, ImGuiButtonFlags flags)
6068 {
6069     ImGuiContext& g = *GImGui;
6070     ImGuiWindow* window = g.CurrentWindow;
6071     if (window->SkipItems)
6072         return false;
6073
6074     const ImGuiStyle& style = g.Style;
6075
6076     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize + padding.x * 2.0f, g.FontSize + padding.y * 2.0f));
6077     ItemSize(bb, style.FramePadding.y);
6078     if (!ItemAdd(bb, id))
6079         return false;
6080
6081     bool hovered, held;
6082     bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
6083
6084     const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
6085 #ifdef IMGUI_HAS_NAV
6086     RenderNavHighlight(bb, id);
6087 #endif
6088     RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
6089     RenderTriangle(bb.Min + padding, dir, 1.0f);
6090
6091     return pressed;
6092 }
6093
6094 void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
6095 {
6096     ImGuiWindow* window = GetCurrentWindow();
6097     if (window->SkipItems)
6098         return;
6099
6100     ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
6101     if (border_col.w > 0.0f)
6102         bb.Max += ImVec2(2,2);
6103     ItemSize(bb);
6104     if (!ItemAdd(bb, 0))
6105         return;
6106
6107     if (border_col.w > 0.0f)
6108     {
6109         window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f);
6110         window->DrawList->AddImage(user_texture_id, bb.Min+ImVec2(1,1), bb.Max-ImVec2(1,1), uv0, uv1, GetColorU32(tint_col));
6111     }
6112     else
6113     {
6114         window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, GetColorU32(tint_col));
6115     }
6116 }
6117
6118 // frame_padding < 0: uses FramePadding from style (default)
6119 // frame_padding = 0: no framing
6120 // frame_padding > 0: set framing size
6121 // The color used are the button colors.
6122 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)
6123 {
6124     ImGuiWindow* window = GetCurrentWindow();
6125     if (window->SkipItems)
6126         return false;
6127
6128     ImGuiContext& g = *GImGui;
6129     const ImGuiStyle& style = g.Style;
6130
6131     // Default to using texture ID as ID. User can still push string/integer prefixes.
6132     // We could hash the size/uv to create a unique ID but that would prevent the user from animating UV.
6133     PushID((void *)user_texture_id);
6134     const ImGuiID id = window->GetID("#image");
6135     PopID();
6136
6137     const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : style.FramePadding;
6138     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding*2);
6139     const ImRect image_bb(window->DC.CursorPos + padding, window->DC.CursorPos + padding + size);
6140     ItemSize(bb);
6141     if (!ItemAdd(bb, id))
6142         return false;
6143
6144     bool hovered, held;
6145     bool pressed = ButtonBehavior(bb, id, &hovered, &held);
6146
6147     // Render
6148     const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
6149     RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding));
6150     if (bg_col.w > 0.0f)
6151         window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, GetColorU32(bg_col));
6152     window->DrawList->AddImage(user_texture_id, image_bb.Min, image_bb.Max, uv0, uv1, GetColorU32(tint_col));
6153
6154     return pressed;
6155 }
6156
6157 // Start logging ImGui output to TTY
6158 void ImGui::LogToTTY(int max_depth)
6159 {
6160     ImGuiContext& g = *GImGui;
6161     if (g.LogEnabled)
6162         return;
6163     ImGuiWindow* window = g.CurrentWindow;
6164
6165     g.LogEnabled = true;
6166     g.LogFile = stdout;
6167     g.LogStartDepth = window->DC.TreeDepth;
6168     if (max_depth >= 0)
6169         g.LogAutoExpandMaxDepth = max_depth;
6170 }
6171
6172 // Start logging ImGui output to given file
6173 void ImGui::LogToFile(int max_depth, const char* filename)
6174 {
6175     ImGuiContext& g = *GImGui;
6176     if (g.LogEnabled)
6177         return;
6178     ImGuiWindow* window = g.CurrentWindow;
6179
6180     if (!filename)
6181     {
6182         filename = g.IO.LogFilename;
6183         if (!filename)
6184             return;
6185     }
6186
6187     g.LogFile = ImFileOpen(filename, "ab");
6188     if (!g.LogFile)
6189     {
6190         IM_ASSERT(g.LogFile != NULL); // Consider this an error
6191         return;
6192     }
6193     g.LogEnabled = true;
6194     g.LogStartDepth = window->DC.TreeDepth;
6195     if (max_depth >= 0)
6196         g.LogAutoExpandMaxDepth = max_depth;
6197 }
6198
6199 // Start logging ImGui output to clipboard
6200 void ImGui::LogToClipboard(int max_depth)
6201 {
6202     ImGuiContext& g = *GImGui;
6203     if (g.LogEnabled)
6204         return;
6205     ImGuiWindow* window = g.CurrentWindow;
6206
6207     g.LogEnabled = true;
6208     g.LogFile = NULL;
6209     g.LogStartDepth = window->DC.TreeDepth;
6210     if (max_depth >= 0)
6211         g.LogAutoExpandMaxDepth = max_depth;
6212 }
6213
6214 void ImGui::LogFinish()
6215 {
6216     ImGuiContext& g = *GImGui;
6217     if (!g.LogEnabled)
6218         return;
6219
6220     LogText(IM_NEWLINE);
6221     g.LogEnabled = false;
6222     if (g.LogFile != NULL)
6223     {
6224         if (g.LogFile == stdout)
6225             fflush(g.LogFile);
6226         else
6227             fclose(g.LogFile);
6228         g.LogFile = NULL;
6229     }
6230     if (g.LogClipboard->size() > 1)
6231     {
6232         SetClipboardText(g.LogClipboard->begin());
6233         g.LogClipboard->clear();
6234     }
6235 }
6236
6237 // Helper to display logging buttons
6238 void ImGui::LogButtons()
6239 {
6240     ImGuiContext& g = *GImGui;
6241
6242     PushID("LogButtons");
6243     const bool log_to_tty = Button("Log To TTY"); SameLine();
6244     const bool log_to_file = Button("Log To File"); SameLine();
6245     const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
6246     PushItemWidth(80.0f);
6247     PushAllowKeyboardFocus(false);
6248     SliderInt("Depth", &g.LogAutoExpandMaxDepth, 0, 9, NULL);
6249     PopAllowKeyboardFocus();
6250     PopItemWidth();
6251     PopID();
6252
6253     // Start logging at the end of the function so that the buttons don't appear in the log
6254     if (log_to_tty)
6255         LogToTTY(g.LogAutoExpandMaxDepth);
6256     if (log_to_file)
6257         LogToFile(g.LogAutoExpandMaxDepth, g.IO.LogFilename);
6258     if (log_to_clipboard)
6259         LogToClipboard(g.LogAutoExpandMaxDepth);
6260 }
6261
6262 bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags)
6263 {
6264     if (flags & ImGuiTreeNodeFlags_Leaf)
6265         return true;
6266
6267     // We only write to the tree storage if the user clicks (or explicitely use SetNextTreeNode*** functions)
6268     ImGuiContext& g = *GImGui;
6269     ImGuiWindow* window = g.CurrentWindow;
6270     ImGuiStorage* storage = window->DC.StateStorage;
6271
6272     bool is_open;
6273     if (g.SetNextTreeNodeOpenCond != 0)
6274     {
6275         if (g.SetNextTreeNodeOpenCond & ImGuiCond_Always)
6276         {
6277             is_open = g.SetNextTreeNodeOpenVal;
6278             storage->SetInt(id, is_open);
6279         }
6280         else
6281         {
6282             // We treat ImGuiCond_Once and ImGuiCond_FirstUseEver the same because tree node state are not saved persistently.
6283             const int stored_value = storage->GetInt(id, -1);
6284             if (stored_value == -1)
6285             {
6286                 is_open = g.SetNextTreeNodeOpenVal;
6287                 storage->SetInt(id, is_open);
6288             }
6289             else
6290             {
6291                 is_open = stored_value != 0;
6292             }
6293         }
6294         g.SetNextTreeNodeOpenCond = 0;
6295     }
6296     else
6297     {
6298         is_open = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0;
6299     }
6300
6301     // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior).
6302     // NB- If we are above max depth we still allow manually opened nodes to be logged.
6303     if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth)
6304         is_open = true;
6305
6306     return is_open;
6307 }
6308
6309 bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end)
6310 {
6311     ImGuiWindow* window = GetCurrentWindow();
6312     if (window->SkipItems)
6313         return false;
6314
6315     ImGuiContext& g = *GImGui;
6316     const ImGuiStyle& style = g.Style;
6317     const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0;
6318     const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f);
6319
6320     if (!label_end)
6321         label_end = FindRenderedTextEnd(label);
6322     const ImVec2 label_size = CalcTextSize(label, label_end, false);
6323
6324     // We vertically grow up to current line height up the typical widget height.
6325     const float text_base_offset_y = ImMax(padding.y, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it
6326     const float frame_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2);
6327     ImRect bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height));
6328     if (display_frame)
6329     {
6330         // Framed header expand a little outside the default padding
6331         bb.Min.x -= (float)(int)(window->WindowPadding.x*0.5f) - 1;
6332         bb.Max.x += (float)(int)(window->WindowPadding.x*0.5f) - 1;
6333     }
6334
6335     const float text_offset_x = (g.FontSize + (display_frame ? padding.x*3 : padding.x*2));   // Collapser arrow width + Spacing
6336     const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x*2 : 0.0f);   // Include collapser
6337     ItemSize(ImVec2(text_width, frame_height), text_base_offset_y);
6338
6339     // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing
6340     // (Ideally we'd want to add a flag for the user to specify if we want the hit test to be done up to the right side of the content or not)
6341     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);
6342     bool is_open = TreeNodeBehaviorIsOpen(id, flags);
6343     if (!ItemAdd(interact_bb, id))
6344     {
6345         if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
6346             TreePushRawID(id);
6347         return is_open;
6348     }
6349
6350     // Flags that affects opening behavior:
6351     // - 0(default) ..................... single-click anywhere to open
6352     // - OpenOnDoubleClick .............. double-click anywhere to open
6353     // - OpenOnArrow .................... single-click on arrow to open
6354     // - OpenOnDoubleClick|OpenOnArrow .. single-click on arrow or double-click anywhere to open
6355     ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers | ((flags & ImGuiTreeNodeFlags_AllowOverlapMode) ? ImGuiButtonFlags_AllowOverlapMode : 0);
6356     if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
6357         button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0);
6358     bool hovered, held, pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags);
6359     if (pressed && !(flags & ImGuiTreeNodeFlags_Leaf))
6360     {
6361         bool toggled = !(flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick));
6362         if (flags & ImGuiTreeNodeFlags_OpenOnArrow)
6363             toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y));
6364         if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
6365             toggled |= g.IO.MouseDoubleClicked[0];
6366         if (toggled)
6367         {
6368             is_open = !is_open;
6369             window->DC.StateStorage->SetInt(id, is_open);
6370         }
6371     }
6372     if (flags & ImGuiTreeNodeFlags_AllowOverlapMode)
6373         SetItemAllowOverlap();
6374
6375     // Render
6376     const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
6377     const ImVec2 text_pos = bb.Min + ImVec2(text_offset_x, text_base_offset_y);
6378     if (display_frame)
6379     {
6380         // Framed type
6381         RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
6382         RenderTriangle(bb.Min + ImVec2(padding.x, text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f);
6383         if (g.LogEnabled)
6384         {
6385             // 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.
6386             const char log_prefix[] = "\n##";
6387             const char log_suffix[] = "##";
6388             LogRenderedText(&text_pos, log_prefix, log_prefix+3);
6389             RenderTextClipped(text_pos, bb.Max, label, label_end, &label_size);
6390             LogRenderedText(&text_pos, log_suffix+1, log_suffix+3);
6391         }
6392         else
6393         {
6394             RenderTextClipped(text_pos, bb.Max, label, label_end, &label_size);
6395         }
6396     }
6397     else
6398     {
6399         // Unframed typed for tree nodes
6400         if (hovered || (flags & ImGuiTreeNodeFlags_Selected))
6401             RenderFrame(bb.Min, bb.Max, col, false);
6402
6403         if (flags & ImGuiTreeNodeFlags_Bullet)
6404             RenderBullet(bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y));
6405         else if (!(flags & ImGuiTreeNodeFlags_Leaf))
6406             RenderTriangle(bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f);
6407         if (g.LogEnabled)
6408             LogRenderedText(&text_pos, ">");
6409         RenderText(text_pos, label, label_end, false);
6410     }
6411
6412     if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
6413         TreePushRawID(id);
6414     return is_open;
6415 }
6416
6417 // CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag).
6418 // 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().
6419 bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags)
6420 {
6421     ImGuiWindow* window = GetCurrentWindow();
6422     if (window->SkipItems)
6423         return false;
6424
6425     return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen, label);
6426 }
6427
6428 bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags)
6429 {
6430     ImGuiWindow* window = GetCurrentWindow();
6431     if (window->SkipItems)
6432         return false;
6433
6434     if (p_open && !*p_open)
6435         return false;
6436
6437     ImGuiID id = window->GetID(label);
6438     bool is_open = TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen | (p_open ? ImGuiTreeNodeFlags_AllowOverlapMode : 0), label);
6439     if (p_open)
6440     {
6441         // Create a small overlapping close button // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc.
6442         ImGuiContext& g = *GImGui;
6443         float button_sz = g.FontSize * 0.5f;
6444         ImGuiItemHoveredDataBackup last_item_backup;
6445         last_item_backup.Backup();
6446         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))
6447             *p_open = false;
6448         last_item_backup.Restore();
6449     }
6450
6451     return is_open;
6452 }
6453
6454 bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags)
6455 {
6456     ImGuiWindow* window = GetCurrentWindow();
6457     if (window->SkipItems)
6458         return false;
6459
6460     return TreeNodeBehavior(window->GetID(label), flags, label, NULL);
6461 }
6462
6463 bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
6464 {
6465     ImGuiWindow* window = GetCurrentWindow();
6466     if (window->SkipItems)
6467         return false;
6468
6469     ImGuiContext& g = *GImGui;
6470     const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
6471     return TreeNodeBehavior(window->GetID(str_id), flags, g.TempBuffer, label_end);
6472 }
6473
6474 bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
6475 {
6476     ImGuiWindow* window = GetCurrentWindow();
6477     if (window->SkipItems)
6478         return false;
6479
6480     ImGuiContext& g = *GImGui;
6481     const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
6482     return TreeNodeBehavior(window->GetID(ptr_id), flags, g.TempBuffer, label_end);
6483 }
6484
6485 bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args)
6486 {
6487     return TreeNodeExV(str_id, 0, fmt, args);
6488 }
6489
6490 bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args)
6491 {
6492     return TreeNodeExV(ptr_id, 0, fmt, args);
6493 }
6494
6495 bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
6496 {
6497     va_list args;
6498     va_start(args, fmt);
6499     bool is_open = TreeNodeExV(str_id, flags, fmt, args);
6500     va_end(args);
6501     return is_open;
6502 }
6503
6504 bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
6505 {
6506     va_list args;
6507     va_start(args, fmt);
6508     bool is_open = TreeNodeExV(ptr_id, flags, fmt, args);
6509     va_end(args);
6510     return is_open;
6511 }
6512
6513 bool ImGui::TreeNode(const char* str_id, const char* fmt, ...)
6514 {
6515     va_list args;
6516     va_start(args, fmt);
6517     bool is_open = TreeNodeExV(str_id, 0, fmt, args);
6518     va_end(args);
6519     return is_open;
6520 }
6521
6522 bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...)
6523 {
6524     va_list args;
6525     va_start(args, fmt);
6526     bool is_open = TreeNodeExV(ptr_id, 0, fmt, args);
6527     va_end(args);
6528     return is_open;
6529 }
6530
6531 bool ImGui::TreeNode(const char* label)
6532 {
6533     ImGuiWindow* window = GetCurrentWindow();
6534     if (window->SkipItems)
6535         return false;
6536     return TreeNodeBehavior(window->GetID(label), 0, label, NULL);
6537 }
6538
6539 void ImGui::TreeAdvanceToLabelPos()
6540 {
6541     ImGuiContext& g = *GImGui;
6542     g.CurrentWindow->DC.CursorPos.x += GetTreeNodeToLabelSpacing();
6543 }
6544
6545 // Horizontal distance preceding label when using TreeNode() or Bullet()
6546 float ImGui::GetTreeNodeToLabelSpacing()
6547 {
6548     ImGuiContext& g = *GImGui;
6549     return g.FontSize + (g.Style.FramePadding.x * 2.0f);
6550 }
6551
6552 void ImGui::SetNextTreeNodeOpen(bool is_open, ImGuiCond cond)
6553 {
6554     ImGuiContext& g = *GImGui;
6555     if (g.CurrentWindow->SkipItems)
6556         return;
6557     g.SetNextTreeNodeOpenVal = is_open;
6558     g.SetNextTreeNodeOpenCond = cond ? cond : ImGuiCond_Always;
6559 }
6560
6561 void ImGui::PushID(const char* str_id)
6562 {
6563     ImGuiWindow* window = GetCurrentWindowRead();
6564     window->IDStack.push_back(window->GetID(str_id));
6565 }
6566
6567 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
6568 {
6569     ImGuiWindow* window = GetCurrentWindowRead();
6570     window->IDStack.push_back(window->GetID(str_id_begin, str_id_end));
6571 }
6572
6573 void ImGui::PushID(const void* ptr_id)
6574 {
6575     ImGuiWindow* window = GetCurrentWindowRead();
6576     window->IDStack.push_back(window->GetID(ptr_id));
6577 }
6578
6579 void ImGui::PushID(int int_id)
6580 {
6581     const void* ptr_id = (void*)(intptr_t)int_id;
6582     ImGuiWindow* window = GetCurrentWindowRead();
6583     window->IDStack.push_back(window->GetID(ptr_id));
6584 }
6585
6586 void ImGui::PopID()
6587 {
6588     ImGuiWindow* window = GetCurrentWindowRead();
6589     window->IDStack.pop_back();
6590 }
6591
6592 ImGuiID ImGui::GetID(const char* str_id)
6593 {
6594     return GImGui->CurrentWindow->GetID(str_id);
6595 }
6596
6597 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
6598 {
6599     return GImGui->CurrentWindow->GetID(str_id_begin, str_id_end);
6600 }
6601
6602 ImGuiID ImGui::GetID(const void* ptr_id)
6603 {
6604     return GImGui->CurrentWindow->GetID(ptr_id);
6605 }
6606
6607 void ImGui::Bullet()
6608 {
6609     ImGuiWindow* window = GetCurrentWindow();
6610     if (window->SkipItems)
6611         return;
6612
6613     ImGuiContext& g = *GImGui;
6614     const ImGuiStyle& style = g.Style;
6615     const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize);
6616     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height));
6617     ItemSize(bb);
6618     if (!ItemAdd(bb, 0))
6619     {
6620         SameLine(0, style.FramePadding.x*2);
6621         return;
6622     }
6623
6624     // Render and stay on same line
6625     RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f));
6626     SameLine(0, style.FramePadding.x*2);
6627 }
6628
6629 // Text with a little bullet aligned to the typical tree node.
6630 void ImGui::BulletTextV(const char* fmt, va_list args)
6631 {
6632     ImGuiWindow* window = GetCurrentWindow();
6633     if (window->SkipItems)
6634         return;
6635
6636     ImGuiContext& g = *GImGui;
6637     const ImGuiStyle& style = g.Style;
6638
6639     const char* text_begin = g.TempBuffer;
6640     const char* text_end = text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
6641     const ImVec2 label_size = CalcTextSize(text_begin, text_end, false);
6642     const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it
6643     const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize);
6644     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
6645     ItemSize(bb);
6646     if (!ItemAdd(bb, 0))
6647         return;
6648
6649     // Render
6650     RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f));
6651     RenderText(bb.Min+ImVec2(g.FontSize + style.FramePadding.x*2, text_base_offset_y), text_begin, text_end, false);
6652 }
6653
6654 void ImGui::BulletText(const char* fmt, ...)
6655 {
6656     va_list args;
6657     va_start(args, fmt);
6658     BulletTextV(fmt, args);
6659     va_end(args);
6660 }
6661
6662 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size)
6663 {
6664     if (data_type == ImGuiDataType_Int)
6665         ImFormatString(buf, buf_size, display_format, *(int*)data_ptr);
6666     else if (data_type == ImGuiDataType_Float)
6667         ImFormatString(buf, buf_size, display_format, *(float*)data_ptr);
6668 }
6669
6670 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size)
6671 {
6672     if (data_type == ImGuiDataType_Int)
6673     {
6674         if (decimal_precision < 0)
6675             ImFormatString(buf, buf_size, "%d", *(int*)data_ptr);
6676         else
6677             ImFormatString(buf, buf_size, "%.*d", decimal_precision, *(int*)data_ptr);
6678     }
6679     else if (data_type == ImGuiDataType_Float)
6680     {
6681         if (decimal_precision < 0)
6682             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?
6683         else
6684             ImFormatString(buf, buf_size, "%.*f", decimal_precision, *(float*)data_ptr);
6685     }
6686 }
6687
6688 static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2)// Store into value1
6689 {
6690     if (data_type == ImGuiDataType_Int)
6691     {
6692         if (op == '+')
6693             *(int*)value1 = *(int*)value1 + *(const int*)value2;
6694         else if (op == '-')
6695             *(int*)value1 = *(int*)value1 - *(const int*)value2;
6696     }
6697     else if (data_type == ImGuiDataType_Float)
6698     {
6699         if (op == '+')
6700             *(float*)value1 = *(float*)value1 + *(const float*)value2;
6701         else if (op == '-')
6702             *(float*)value1 = *(float*)value1 - *(const float*)value2;
6703     }
6704 }
6705
6706 // User can input math operators (e.g. +100) to edit a numerical values.
6707 static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format)
6708 {
6709     while (ImCharIsSpace(*buf))
6710         buf++;
6711
6712     // We don't support '-' op because it would conflict with inputing negative value.
6713     // Instead you can use +-100 to subtract from an existing value
6714     char op = buf[0];
6715     if (op == '+' || op == '*' || op == '/')
6716     {
6717         buf++;
6718         while (ImCharIsSpace(*buf))
6719             buf++;
6720     }
6721     else
6722     {
6723         op = 0;
6724     }
6725     if (!buf[0])
6726         return false;
6727
6728     if (data_type == ImGuiDataType_Int)
6729     {
6730         if (!scalar_format)
6731             scalar_format = "%d";
6732         int* v = (int*)data_ptr;
6733         const int old_v = *v;
6734         int arg0i = *v;
6735         if (op && sscanf(initial_value_buf, scalar_format, &arg0i) < 1)
6736             return false;
6737
6738         // 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
6739         float arg1f = 0.0f;
6740         if (op == '+')      { if (sscanf(buf, "%f", &arg1f) == 1) *v = (int)(arg0i + arg1f); }                 // Add (use "+-" to subtract)
6741         else if (op == '*') { if (sscanf(buf, "%f", &arg1f) == 1) *v = (int)(arg0i * arg1f); }                 // Multiply
6742         else if (op == '/') { if (sscanf(buf, "%f", &arg1f) == 1 && arg1f != 0.0f) *v = (int)(arg0i / arg1f); }// Divide
6743         else                { if (sscanf(buf, scalar_format, &arg0i) == 1) *v = arg0i; }                       // Assign constant (read as integer so big values are not lossy)
6744         return (old_v != *v);
6745     }
6746     else if (data_type == ImGuiDataType_Float)
6747     {
6748         // For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in
6749         scalar_format = "%f";
6750         float* v = (float*)data_ptr;
6751         const float old_v = *v;
6752         float arg0f = *v;
6753         if (op && sscanf(initial_value_buf, scalar_format, &arg0f) < 1)
6754             return false;
6755
6756         float arg1f = 0.0f;
6757         if (sscanf(buf, scalar_format, &arg1f) < 1)
6758             return false;
6759         if (op == '+')      { *v = arg0f + arg1f; }                    // Add (use "+-" to subtract)
6760         else if (op == '*') { *v = arg0f * arg1f; }                    // Multiply
6761         else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide
6762         else                { *v = arg1f; }                            // Assign constant
6763         return (old_v != *v);
6764     }
6765
6766     return false;
6767 }
6768
6769 // Create text input in place of a slider (when CTRL+Clicking on slider)
6770 // FIXME: Logic is messy and confusing.
6771 bool ImGui::InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label, ImGuiDataType data_type, void* data_ptr, ImGuiID id, int decimal_precision)
6772 {
6773     ImGuiContext& g = *GImGui;
6774     ImGuiWindow* window = GetCurrentWindow();
6775
6776     // Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen)
6777     // On the first frame, g.ScalarAsInputTextId == 0, then on subsequent frames it becomes == id
6778     SetActiveID(g.ScalarAsInputTextId, window);
6779     SetHoveredID(0);
6780     FocusableItemUnregister(window);
6781
6782     char buf[32];
6783     DataTypeFormatString(data_type, data_ptr, decimal_precision, buf, IM_ARRAYSIZE(buf));
6784     bool text_value_changed = InputTextEx(label, buf, IM_ARRAYSIZE(buf), aabb.GetSize(), ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_AutoSelectAll);
6785     if (g.ScalarAsInputTextId == 0)     // First frame we started displaying the InputText widget
6786     {
6787         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)
6788         g.ScalarAsInputTextId = g.ActiveId;
6789         SetHoveredID(id);
6790     }
6791     if (text_value_changed)
6792         return DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, NULL);
6793     return false;
6794 }
6795
6796 // Parse display precision back from the display format string
6797 int ImGui::ParseFormatPrecision(const char* fmt, int default_precision)
6798 {
6799     int precision = default_precision;
6800     while ((fmt = strchr(fmt, '%')) != NULL)
6801     {
6802         fmt++;
6803         if (fmt[0] == '%') { fmt++; continue; } // Ignore "%%"
6804         while (*fmt >= '0' && *fmt <= '9')
6805             fmt++;
6806         if (*fmt == '.')
6807         {
6808             fmt = ImAtoi(fmt + 1, &precision);
6809             if (precision < 0 || precision > 10)
6810                 precision = default_precision;
6811         }
6812         if (*fmt == 'e' || *fmt == 'E') // Maximum precision with scientific notation
6813             precision = -1;
6814         break;
6815     }
6816     return precision;
6817 }
6818
6819 static float GetMinimumStepAtDecimalPrecision(int decimal_precision)
6820 {
6821     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 };
6822     return (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : powf(10.0f, (float)-decimal_precision);
6823 }
6824
6825 float ImGui::RoundScalar(float value, int decimal_precision)
6826 {
6827     // Round past decimal precision
6828     // So when our value is 1.99999 with a precision of 0.001 we'll end up rounding to 2.0
6829     // FIXME: Investigate better rounding methods
6830     if (decimal_precision < 0)
6831         return value;
6832     const float min_step = GetMinimumStepAtDecimalPrecision(decimal_precision);
6833     bool negative = value < 0.0f;
6834     value = fabsf(value);
6835     float remainder = fmodf(value, min_step);
6836     if (remainder <= min_step*0.5f)
6837         value -= remainder;
6838     else
6839         value += (min_step - remainder);
6840     return negative ? -value : value;
6841 }
6842
6843 static inline float SliderBehaviorCalcRatioFromValue(float v, float v_min, float v_max, float power, float linear_zero_pos)
6844 {
6845     if (v_min == v_max)
6846         return 0.0f;
6847
6848     const bool is_non_linear = (power < 1.0f-0.00001f) || (power > 1.0f+0.00001f);
6849     const float v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min);
6850     if (is_non_linear)
6851     {
6852         if (v_clamped < 0.0f)
6853         {
6854             const float f = 1.0f - (v_clamped - v_min) / (ImMin(0.0f,v_max) - v_min);
6855             return (1.0f - powf(f, 1.0f/power)) * linear_zero_pos;
6856         }
6857         else
6858         {
6859             const float f = (v_clamped - ImMax(0.0f,v_min)) / (v_max - ImMax(0.0f,v_min));
6860             return linear_zero_pos + powf(f, 1.0f/power) * (1.0f - linear_zero_pos);
6861         }
6862     }
6863
6864     // Linear slider
6865     return (v_clamped - v_min) / (v_max - v_min);
6866 }
6867
6868 bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags)
6869 {
6870     ImGuiContext& g = *GImGui;
6871     ImGuiWindow* window = GetCurrentWindow();
6872     const ImGuiStyle& style = g.Style;
6873
6874     // Draw frame
6875     RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
6876
6877     const bool is_non_linear = (power < 1.0f-0.00001f) || (power > 1.0f+0.00001f);
6878     const bool is_horizontal = (flags & ImGuiSliderFlags_Vertical) == 0;
6879
6880     const float grab_padding = 2.0f;
6881     const float slider_sz = is_horizontal ? (frame_bb.GetWidth() - grab_padding * 2.0f) : (frame_bb.GetHeight() - grab_padding * 2.0f);
6882     float grab_sz;
6883     if (decimal_precision != 0)
6884         grab_sz = ImMin(style.GrabMinSize, slider_sz);
6885     else
6886         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
6887     const float slider_usable_sz = slider_sz - grab_sz;
6888     const float slider_usable_pos_min = (is_horizontal ? frame_bb.Min.x : frame_bb.Min.y) + grab_padding + grab_sz*0.5f;
6889     const float slider_usable_pos_max = (is_horizontal ? frame_bb.Max.x : frame_bb.Max.y) - grab_padding - grab_sz*0.5f;
6890
6891     // For logarithmic sliders that cross over sign boundary we want the exponential increase to be symmetric around 0.0f
6892     float linear_zero_pos = 0.0f;   // 0.0->1.0f
6893     if (v_min * v_max < 0.0f)
6894     {
6895         // Different sign
6896         const float linear_dist_min_to_0 = powf(fabsf(0.0f - v_min), 1.0f/power);
6897         const float linear_dist_max_to_0 = powf(fabsf(v_max - 0.0f), 1.0f/power);
6898         linear_zero_pos = linear_dist_min_to_0 / (linear_dist_min_to_0+linear_dist_max_to_0);
6899     }
6900     else
6901     {
6902         // Same sign
6903         linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f;
6904     }
6905
6906     // Process clicking on the slider
6907     bool value_changed = false;
6908     if (g.ActiveId == id)
6909     {
6910         bool set_new_value = false;
6911         float clicked_t = 0.0f;
6912         if (g.IO.MouseDown[0])
6913         {
6914             const float mouse_abs_pos = is_horizontal ? g.IO.MousePos.x : g.IO.MousePos.y;
6915             clicked_t = (slider_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f) : 0.0f;
6916             if (!is_horizontal)
6917                 clicked_t = 1.0f - clicked_t;
6918             set_new_value = true;
6919         }
6920         else
6921         {
6922             ClearActiveID();
6923         }
6924
6925         if (set_new_value)
6926         {
6927             float new_value;
6928             if (is_non_linear)
6929             {
6930                 // Account for logarithmic scale on both sides of the zero
6931                 if (clicked_t < linear_zero_pos)
6932                 {
6933                     // Negative: rescale to the negative range before powering
6934                     float a = 1.0f - (clicked_t / linear_zero_pos);
6935                     a = powf(a, power);
6936                     new_value = ImLerp(ImMin(v_max,0.0f), v_min, a);
6937                 }
6938                 else
6939                 {
6940                     // Positive: rescale to the positive range before powering
6941                     float a;
6942                     if (fabsf(linear_zero_pos - 1.0f) > 1.e-6f)
6943                         a = (clicked_t - linear_zero_pos) / (1.0f - linear_zero_pos);
6944                     else
6945                         a = clicked_t;
6946                     a = powf(a, power);
6947                     new_value = ImLerp(ImMax(v_min,0.0f), v_max, a);
6948                 }
6949             }
6950             else
6951             {
6952                 // Linear slider
6953                 new_value = ImLerp(v_min, v_max, clicked_t);
6954             }
6955
6956             // Round past decimal precision
6957             new_value = RoundScalar(new_value, decimal_precision);
6958             if (*v != new_value)
6959             {
6960                 *v = new_value;
6961                 value_changed = true;
6962             }
6963         }
6964     }
6965
6966     // Draw
6967     float grab_t = SliderBehaviorCalcRatioFromValue(*v, v_min, v_max, power, linear_zero_pos);
6968     if (!is_horizontal)
6969         grab_t = 1.0f - grab_t;
6970     const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t);
6971     ImRect grab_bb;
6972     if (is_horizontal)
6973         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));
6974     else
6975         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));
6976     window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);
6977
6978     return value_changed;
6979 }
6980
6981 // Use power!=1.0 for logarithmic sliders.
6982 // Adjust display_format to decorate the value with a prefix or a suffix.
6983 //   "%.3f"         1.234
6984 //   "%5.2f secs"   01.23 secs
6985 //   "Gold: %.0f"   Gold: 1
6986 bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format, float power)
6987 {
6988     ImGuiWindow* window = GetCurrentWindow();
6989     if (window->SkipItems)
6990         return false;
6991
6992     ImGuiContext& g = *GImGui;
6993     const ImGuiStyle& style = g.Style;
6994     const ImGuiID id = window->GetID(label);
6995     const float w = CalcItemWidth();
6996
6997     const ImVec2 label_size = CalcTextSize(label, NULL, true);
6998     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
6999     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));
7000
7001     // NB- we don't call ItemSize() yet because we may turn into a text edit box below
7002     if (!ItemAdd(total_bb, id))
7003     {
7004         ItemSize(total_bb, style.FramePadding.y);
7005         return false;
7006     }
7007     const bool hovered = ItemHoverable(frame_bb, id);
7008
7009     if (!display_format)
7010         display_format = "%.3f";
7011     int decimal_precision = ParseFormatPrecision(display_format, 3);
7012
7013     // Tabbing or CTRL-clicking on Slider turns it into an input box
7014     bool start_text_input = false;
7015     const bool tab_focus_requested = FocusableItemRegister(window, id);
7016     if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]))
7017     {
7018         SetActiveID(id, window);
7019         FocusWindow(window);
7020         if (tab_focus_requested || g.IO.KeyCtrl)
7021         {
7022             start_text_input = true;
7023             g.ScalarAsInputTextId = 0;
7024         }
7025     }
7026     if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
7027         return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision);
7028
7029     // Actual slider behavior + render grab
7030     ItemSize(total_bb, style.FramePadding.y);
7031     const bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision);
7032
7033     // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
7034     char value_buf[64];
7035     const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
7036     RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f));
7037
7038     if (label_size.x > 0.0f)
7039         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
7040
7041     return value_changed;
7042 }
7043
7044 bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* display_format, float power)
7045 {
7046     ImGuiWindow* window = GetCurrentWindow();
7047     if (window->SkipItems)
7048         return false;
7049
7050     ImGuiContext& g = *GImGui;
7051     const ImGuiStyle& style = g.Style;
7052     const ImGuiID id = window->GetID(label);
7053
7054     const ImVec2 label_size = CalcTextSize(label, NULL, true);
7055     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
7056     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));
7057
7058     ItemSize(bb, style.FramePadding.y);
7059     if (!ItemAdd(frame_bb, id))
7060         return false;
7061     const bool hovered = ItemHoverable(frame_bb, id);
7062
7063     if (!display_format)
7064         display_format = "%.3f";
7065     int decimal_precision = ParseFormatPrecision(display_format, 3);
7066
7067     if (hovered && g.IO.MouseClicked[0])
7068     {
7069         SetActiveID(id, window);
7070         FocusWindow(window);
7071     }
7072
7073     // Actual slider behavior + render grab
7074     bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision, ImGuiSliderFlags_Vertical);
7075
7076     // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
7077     // For the vertical slider we allow centered text to overlap the frame padding
7078     char value_buf[64];
7079     char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
7080     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));
7081     if (label_size.x > 0.0f)
7082         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
7083
7084     return value_changed;
7085 }
7086
7087 bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max)
7088 {
7089     float v_deg = (*v_rad) * 360.0f / (2*IM_PI);
7090     bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, "%.0f deg", 1.0f);
7091     *v_rad = v_deg * (2*IM_PI) / 360.0f;
7092     return value_changed;
7093 }
7094
7095 bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* display_format)
7096 {
7097     if (!display_format)
7098         display_format = "%.0f";
7099     float v_f = (float)*v;
7100     bool value_changed = SliderFloat(label, &v_f, (float)v_min, (float)v_max, display_format, 1.0f);
7101     *v = (int)v_f;
7102     return value_changed;
7103 }
7104
7105 bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* display_format)
7106 {
7107     if (!display_format)
7108         display_format = "%.0f";
7109     float v_f = (float)*v;
7110     bool value_changed = VSliderFloat(label, size, &v_f, (float)v_min, (float)v_max, display_format, 1.0f);
7111     *v = (int)v_f;
7112     return value_changed;
7113 }
7114
7115 // Add multiple sliders on 1 line for compact edition of multiple components
7116 bool ImGui::SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* display_format, float power)
7117 {
7118     ImGuiWindow* window = GetCurrentWindow();
7119     if (window->SkipItems)
7120         return false;
7121
7122     ImGuiContext& g = *GImGui;
7123     bool value_changed = false;
7124     BeginGroup();
7125     PushID(label);
7126     PushMultiItemsWidths(components);
7127     for (int i = 0; i < components; i++)
7128     {
7129         PushID(i);
7130         value_changed |= SliderFloat("##v", &v[i], v_min, v_max, display_format, power);
7131         SameLine(0, g.Style.ItemInnerSpacing.x);
7132         PopID();
7133         PopItemWidth();
7134     }
7135     PopID();
7136
7137     TextUnformatted(label, FindRenderedTextEnd(label));
7138     EndGroup();
7139
7140     return value_changed;
7141 }
7142
7143 bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format, float power)
7144 {
7145     return SliderFloatN(label, v, 2, v_min, v_max, display_format, power);
7146 }
7147
7148 bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format, float power)
7149 {
7150     return SliderFloatN(label, v, 3, v_min, v_max, display_format, power);
7151 }
7152
7153 bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* display_format, float power)
7154 {
7155     return SliderFloatN(label, v, 4, v_min, v_max, display_format, power);
7156 }
7157
7158 bool ImGui::SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* display_format)
7159 {
7160     ImGuiWindow* window = GetCurrentWindow();
7161     if (window->SkipItems)
7162         return false;
7163
7164     ImGuiContext& g = *GImGui;
7165     bool value_changed = false;
7166     BeginGroup();
7167     PushID(label);
7168     PushMultiItemsWidths(components);
7169     for (int i = 0; i < components; i++)
7170     {
7171         PushID(i);
7172         value_changed |= SliderInt("##v", &v[i], v_min, v_max, display_format);
7173         SameLine(0, g.Style.ItemInnerSpacing.x);
7174         PopID();
7175         PopItemWidth();
7176     }
7177     PopID();
7178
7179     TextUnformatted(label, FindRenderedTextEnd(label));
7180     EndGroup();
7181
7182     return value_changed;
7183 }
7184
7185 bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* display_format)
7186 {
7187     return SliderIntN(label, v, 2, v_min, v_max, display_format);
7188 }
7189
7190 bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* display_format)
7191 {
7192     return SliderIntN(label, v, 3, v_min, v_max, display_format);
7193 }
7194
7195 bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* display_format)
7196 {
7197     return SliderIntN(label, v, 4, v_min, v_max, display_format);
7198 }
7199
7200 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)
7201 {
7202     ImGuiContext& g = *GImGui;
7203     const ImGuiStyle& style = g.Style;
7204
7205     // Draw frame
7206     const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
7207     RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding);
7208
7209     bool value_changed = false;
7210
7211     // Process clicking on the drag
7212     if (g.ActiveId == id)
7213     {
7214         if (g.IO.MouseDown[0])
7215         {
7216             if (g.ActiveIdIsJustActivated)
7217             {
7218                 // Lock current value on click
7219                 g.DragCurrentValue = *v;
7220                 g.DragLastMouseDelta = ImVec2(0.f, 0.f);
7221             }
7222
7223             if (v_speed == 0.0f && (v_max - v_min) != 0.0f && (v_max - v_min) < FLT_MAX)
7224                 v_speed = (v_max - v_min) * g.DragSpeedDefaultRatio;
7225
7226             float v_cur = g.DragCurrentValue;
7227             const ImVec2 mouse_drag_delta = GetMouseDragDelta(0, 1.0f);
7228             float adjust_delta = 0.0f;
7229             //if (g.ActiveIdSource == ImGuiInputSource_Mouse)
7230             {
7231                 adjust_delta = mouse_drag_delta.x - g.DragLastMouseDelta.x;
7232                 if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f)
7233                     adjust_delta *= g.DragSpeedScaleFast;
7234                 if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f)
7235                     adjust_delta *= g.DragSpeedScaleSlow;
7236             }
7237             adjust_delta *= v_speed;
7238             g.DragLastMouseDelta.x = mouse_drag_delta.x;
7239
7240             if (fabsf(adjust_delta) > 0.0f)
7241             {
7242                 if (fabsf(power - 1.0f) > 0.001f)
7243                 {
7244                     // Logarithmic curve on both side of 0.0
7245                     float v0_abs = v_cur >= 0.0f ? v_cur : -v_cur;
7246                     float v0_sign = v_cur >= 0.0f ? 1.0f : -1.0f;
7247                     float v1 = powf(v0_abs, 1.0f / power) + (adjust_delta * v0_sign);
7248                     float v1_abs = v1 >= 0.0f ? v1 : -v1;
7249                     float v1_sign = v1 >= 0.0f ? 1.0f : -1.0f;          // Crossed sign line
7250                     v_cur = powf(v1_abs, power) * v0_sign * v1_sign;    // Reapply sign
7251                 }
7252                 else
7253                 {
7254                     v_cur += adjust_delta;
7255                 }
7256
7257                 // Clamp
7258                 if (v_min < v_max)
7259                     v_cur = ImClamp(v_cur, v_min, v_max);
7260                 g.DragCurrentValue = v_cur;
7261             }
7262
7263             // Round to user desired precision, then apply
7264             v_cur = RoundScalar(v_cur, decimal_precision);
7265             if (*v != v_cur)
7266             {
7267                 *v = v_cur;
7268                 value_changed = true;
7269             }
7270         }
7271         else
7272         {
7273             ClearActiveID();
7274         }
7275     }
7276
7277     return value_changed;
7278 }
7279
7280 bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* display_format, float power)
7281 {
7282     ImGuiWindow* window = GetCurrentWindow();
7283     if (window->SkipItems)
7284         return false;
7285
7286     ImGuiContext& g = *GImGui;
7287     const ImGuiStyle& style = g.Style;
7288     const ImGuiID id = window->GetID(label);
7289     const float w = CalcItemWidth();
7290
7291     const ImVec2 label_size = CalcTextSize(label, NULL, true);
7292     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
7293     const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
7294     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));
7295
7296     // NB- we don't call ItemSize() yet because we may turn into a text edit box below
7297     if (!ItemAdd(total_bb, id))
7298     {
7299         ItemSize(total_bb, style.FramePadding.y);
7300         return false;
7301     }
7302     const bool hovered = ItemHoverable(frame_bb, id);
7303
7304     if (!display_format)
7305         display_format = "%.3f";
7306     int decimal_precision = ParseFormatPrecision(display_format, 3);
7307
7308     // Tabbing or CTRL-clicking on Drag turns it into an input box
7309     bool start_text_input = false;
7310     const bool tab_focus_requested = FocusableItemRegister(window, id);
7311     if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] || g.IO.MouseDoubleClicked[0])))
7312     {
7313         SetActiveID(id, window);
7314         FocusWindow(window);
7315         if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0])
7316         {
7317             start_text_input = true;
7318             g.ScalarAsInputTextId = 0;
7319         }
7320     }
7321     if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
7322         return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision);
7323
7324     // Actual drag behavior
7325     ItemSize(total_bb, style.FramePadding.y);
7326     const bool value_changed = DragBehavior(frame_bb, id, v, v_speed, v_min, v_max, decimal_precision, power);
7327
7328     // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
7329     char value_buf[64];
7330     const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
7331     RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f));
7332
7333     if (label_size.x > 0.0f)
7334         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
7335
7336     return value_changed;
7337 }
7338
7339 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)
7340 {
7341     ImGuiWindow* window = GetCurrentWindow();
7342     if (window->SkipItems)
7343         return false;
7344
7345     ImGuiContext& g = *GImGui;
7346     bool value_changed = false;
7347     BeginGroup();
7348     PushID(label);
7349     PushMultiItemsWidths(components);
7350     for (int i = 0; i < components; i++)
7351     {
7352         PushID(i);
7353         value_changed |= DragFloat("##v", &v[i], v_speed, v_min, v_max, display_format, power);
7354         SameLine(0, g.Style.ItemInnerSpacing.x);
7355         PopID();
7356         PopItemWidth();
7357     }
7358     PopID();
7359
7360     TextUnformatted(label, FindRenderedTextEnd(label));
7361     EndGroup();
7362
7363     return value_changed;
7364 }
7365
7366 bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* display_format, float power)
7367 {
7368     return DragFloatN(label, v, 2, v_speed, v_min, v_max, display_format, power);
7369 }
7370
7371 bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* display_format, float power)
7372 {
7373     return DragFloatN(label, v, 3, v_speed, v_min, v_max, display_format, power);
7374 }
7375
7376 bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* display_format, float power)
7377 {
7378     return DragFloatN(label, v, 4, v_speed, v_min, v_max, display_format, power);
7379 }
7380
7381 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)
7382 {
7383     ImGuiWindow* window = GetCurrentWindow();
7384     if (window->SkipItems)
7385         return false;
7386
7387     ImGuiContext& g = *GImGui;
7388     PushID(label);
7389     BeginGroup();
7390     PushMultiItemsWidths(2);
7391
7392     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);
7393     PopItemWidth();
7394     SameLine(0, g.Style.ItemInnerSpacing.x);
7395     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);
7396     PopItemWidth();
7397     SameLine(0, g.Style.ItemInnerSpacing.x);
7398
7399     TextUnformatted(label, FindRenderedTextEnd(label));
7400     EndGroup();
7401     PopID();
7402
7403     return value_changed;
7404 }
7405
7406 // NB: v_speed is float to allow adjusting the drag speed with more precision
7407 bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* display_format)
7408 {
7409     if (!display_format)
7410         display_format = "%.0f";
7411     float v_f = (float)*v;
7412     bool value_changed = DragFloat(label, &v_f, v_speed, (float)v_min, (float)v_max, display_format);
7413     *v = (int)v_f;
7414     return value_changed;
7415 }
7416
7417 bool ImGui::DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* display_format)
7418 {
7419     ImGuiWindow* window = GetCurrentWindow();
7420     if (window->SkipItems)
7421         return false;
7422
7423     ImGuiContext& g = *GImGui;
7424     bool value_changed = false;
7425     BeginGroup();
7426     PushID(label);
7427     PushMultiItemsWidths(components);
7428     for (int i = 0; i < components; i++)
7429     {
7430         PushID(i);
7431         value_changed |= DragInt("##v", &v[i], v_speed, v_min, v_max, display_format);
7432         SameLine(0, g.Style.ItemInnerSpacing.x);
7433         PopID();
7434         PopItemWidth();
7435     }
7436     PopID();
7437
7438     TextUnformatted(label, FindRenderedTextEnd(label));
7439     EndGroup();
7440
7441     return value_changed;
7442 }
7443
7444 bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* display_format)
7445 {
7446     return DragIntN(label, v, 2, v_speed, v_min, v_max, display_format);
7447 }
7448
7449 bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* display_format)
7450 {
7451     return DragIntN(label, v, 3, v_speed, v_min, v_max, display_format);
7452 }
7453
7454 bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* display_format)
7455 {
7456     return DragIntN(label, v, 4, v_speed, v_min, v_max, display_format);
7457 }
7458
7459 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)
7460 {
7461     ImGuiWindow* window = GetCurrentWindow();
7462     if (window->SkipItems)
7463         return false;
7464
7465     ImGuiContext& g = *GImGui;
7466     PushID(label);
7467     BeginGroup();
7468     PushMultiItemsWidths(2);
7469
7470     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);
7471     PopItemWidth();
7472     SameLine(0, g.Style.ItemInnerSpacing.x);
7473     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);
7474     PopItemWidth();
7475     SameLine(0, g.Style.ItemInnerSpacing.x);
7476
7477     TextUnformatted(label, FindRenderedTextEnd(label));
7478     EndGroup();
7479     PopID();
7480
7481     return value_changed;
7482 }
7483
7484 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)
7485 {
7486     ImGuiWindow* window = GetCurrentWindow();
7487     if (window->SkipItems)
7488         return;
7489
7490     ImGuiContext& g = *GImGui;
7491     const ImGuiStyle& style = g.Style;
7492
7493     const ImVec2 label_size = CalcTextSize(label, NULL, true);
7494     if (graph_size.x == 0.0f)
7495         graph_size.x = CalcItemWidth();
7496     if (graph_size.y == 0.0f)
7497         graph_size.y = label_size.y + (style.FramePadding.y * 2);
7498
7499     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(graph_size.x, graph_size.y));
7500     const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
7501     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));
7502     ItemSize(total_bb, style.FramePadding.y);
7503     if (!ItemAdd(total_bb, 0))
7504         return;
7505     const bool hovered = ItemHoverable(inner_bb, 0);
7506
7507     // Determine scale from values if not specified
7508     if (scale_min == FLT_MAX || scale_max == FLT_MAX)
7509     {
7510         float v_min = FLT_MAX;
7511         float v_max = -FLT_MAX;
7512         for (int i = 0; i < values_count; i++)
7513         {
7514             const float v = values_getter(data, i);
7515             v_min = ImMin(v_min, v);
7516             v_max = ImMax(v_max, v);
7517         }
7518         if (scale_min == FLT_MAX)
7519             scale_min = v_min;
7520         if (scale_max == FLT_MAX)
7521             scale_max = v_max;
7522     }
7523
7524     RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
7525
7526     if (values_count > 0)
7527     {
7528         int res_w = ImMin((int)graph_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
7529         int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
7530
7531         // Tooltip on hover
7532         int v_hovered = -1;
7533         if (hovered)
7534         {
7535             const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f);
7536             const int v_idx = (int)(t * item_count);
7537             IM_ASSERT(v_idx >= 0 && v_idx < values_count);
7538
7539             const float v0 = values_getter(data, (v_idx + values_offset) % values_count);
7540             const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count);
7541             if (plot_type == ImGuiPlotType_Lines)
7542                 SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx+1, v1);
7543             else if (plot_type == ImGuiPlotType_Histogram)
7544                 SetTooltip("%d: %8.4g", v_idx, v0);
7545             v_hovered = v_idx;
7546         }
7547
7548         const float t_step = 1.0f / (float)res_w;
7549
7550         float v0 = values_getter(data, (0 + values_offset) % values_count);
7551         float t0 = 0.0f;
7552         ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) / (scale_max - scale_min)) );                       // Point in the normalized space of our target rectangle
7553         float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (-scale_min / (scale_max - scale_min)) : (scale_min < 0.0f ? 0.0f : 1.0f);   // Where does the zero line stands
7554
7555         const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram);
7556         const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered);
7557
7558         for (int n = 0; n < res_w; n++)
7559         {
7560             const float t1 = t0 + t_step;
7561             const int v1_idx = (int)(t0 * item_count + 0.5f);
7562             IM_ASSERT(v1_idx >= 0 && v1_idx < values_count);
7563             const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count);
7564             const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) / (scale_max - scale_min)) );
7565
7566             // 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.
7567             ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0);
7568             ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, histogram_zero_line_t));
7569             if (plot_type == ImGuiPlotType_Lines)
7570             {
7571                 window->DrawList->AddLine(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
7572             }
7573             else if (plot_type == ImGuiPlotType_Histogram)
7574             {
7575                 if (pos1.x >= pos0.x + 2.0f)
7576                     pos1.x -= 1.0f;
7577                 window->DrawList->AddRectFilled(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
7578             }
7579
7580             t0 = t1;
7581             tp0 = tp1;
7582         }
7583     }
7584
7585     // Text overlay
7586     if (overlay_text)
7587         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));
7588
7589     if (label_size.x > 0.0f)
7590         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
7591 }
7592
7593 struct ImGuiPlotArrayGetterData
7594 {
7595     const float* Values;
7596     int Stride;
7597
7598     ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; }
7599 };
7600
7601 static float Plot_ArrayGetter(void* data, int idx)
7602 {
7603     ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data;
7604     const float v = *(float*)(void*)((unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride);
7605     return v;
7606 }
7607
7608 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)
7609 {
7610     ImGuiPlotArrayGetterData data(values, stride);
7611     PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
7612 }
7613
7614 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)
7615 {
7616     PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
7617 }
7618
7619 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)
7620 {
7621     ImGuiPlotArrayGetterData data(values, stride);
7622     PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
7623 }
7624
7625 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)
7626 {
7627     PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
7628 }
7629
7630 // size_arg (for each axis) < 0.0f: align to end, 0.0f: auto, > 0.0f: specified size
7631 void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay)
7632 {
7633     ImGuiWindow* window = GetCurrentWindow();
7634     if (window->SkipItems)
7635         return;
7636
7637     ImGuiContext& g = *GImGui;
7638     const ImGuiStyle& style = g.Style;
7639
7640     ImVec2 pos = window->DC.CursorPos;
7641     ImRect bb(pos, pos + CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y*2.0f));
7642     ItemSize(bb, style.FramePadding.y);
7643     if (!ItemAdd(bb, 0))
7644         return;
7645
7646     // Render
7647     fraction = ImSaturate(fraction);
7648     RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
7649     bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize));
7650     const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y);
7651     RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), 0.0f, fraction, style.FrameRounding);
7652
7653     // Default displaying the fraction as percentage string, but user can override it
7654     char overlay_buf[32];
7655     if (!overlay)
7656     {
7657         ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction*100+0.01f);
7658         overlay = overlay_buf;
7659     }
7660
7661     ImVec2 overlay_size = CalcTextSize(overlay, NULL);
7662     if (overlay_size.x > 0.0f)
7663         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);
7664 }
7665
7666 bool ImGui::Checkbox(const char* label, bool* v)
7667 {
7668     ImGuiWindow* window = GetCurrentWindow();
7669     if (window->SkipItems)
7670         return false;
7671
7672     ImGuiContext& g = *GImGui;
7673     const ImGuiStyle& style = g.Style;
7674     const ImGuiID id = window->GetID(label);
7675     const ImVec2 label_size = CalcTextSize(label, NULL, true);
7676
7677     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)); // We want a square shape to we use Y twice
7678     ItemSize(check_bb, style.FramePadding.y);
7679
7680     ImRect total_bb = check_bb;
7681     if (label_size.x > 0)
7682         SameLine(0, style.ItemInnerSpacing.x);
7683     const ImRect text_bb(window->DC.CursorPos + ImVec2(0,style.FramePadding.y), window->DC.CursorPos + ImVec2(0,style.FramePadding.y) + label_size);
7684     if (label_size.x > 0)
7685     {
7686         ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y);
7687         total_bb = ImRect(ImMin(check_bb.Min, text_bb.Min), ImMax(check_bb.Max, text_bb.Max));
7688     }
7689
7690     if (!ItemAdd(total_bb, id))
7691         return false;
7692
7693     bool hovered, held;
7694     bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
7695     if (pressed)
7696         *v = !(*v);
7697
7698     RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding);
7699     if (*v)
7700     {
7701         const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight());
7702         const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f));
7703         RenderCheckMark(check_bb.Min + ImVec2(pad,pad), GetColorU32(ImGuiCol_CheckMark), check_bb.GetWidth() - pad*2.0f);
7704     }
7705
7706     if (g.LogEnabled)
7707         LogRenderedText(&text_bb.Min, *v ? "[x]" : "[ ]");
7708     if (label_size.x > 0.0f)
7709         RenderText(text_bb.Min, label);
7710
7711     return pressed;
7712 }
7713
7714 bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value)
7715 {
7716     bool v = ((*flags & flags_value) == flags_value);
7717     bool pressed = Checkbox(label, &v);
7718     if (pressed)
7719     {
7720         if (v)
7721             *flags |= flags_value;
7722         else
7723             *flags &= ~flags_value;
7724     }
7725
7726     return pressed;
7727 }
7728
7729 bool ImGui::RadioButton(const char* label, bool active)
7730 {
7731     ImGuiWindow* window = GetCurrentWindow();
7732     if (window->SkipItems)
7733         return false;
7734
7735     ImGuiContext& g = *GImGui;
7736     const ImGuiStyle& style = g.Style;
7737     const ImGuiID id = window->GetID(label);
7738     const ImVec2 label_size = CalcTextSize(label, NULL, true);
7739
7740     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));
7741     ItemSize(check_bb, style.FramePadding.y);
7742
7743     ImRect total_bb = check_bb;
7744     if (label_size.x > 0)
7745         SameLine(0, style.ItemInnerSpacing.x);
7746     const ImRect text_bb(window->DC.CursorPos + ImVec2(0, style.FramePadding.y), window->DC.CursorPos + ImVec2(0, style.FramePadding.y) + label_size);
7747     if (label_size.x > 0)
7748     {
7749         ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y);
7750         total_bb.Add(text_bb);
7751     }
7752
7753     if (!ItemAdd(total_bb, id))
7754         return false;
7755
7756     ImVec2 center = check_bb.GetCenter();
7757     center.x = (float)(int)center.x + 0.5f;
7758     center.y = (float)(int)center.y + 0.5f;
7759     const float radius = check_bb.GetHeight() * 0.5f;
7760
7761     bool hovered, held;
7762     bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
7763
7764     window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16);
7765     if (active)
7766     {
7767         const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight());
7768         const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f));
7769         window->DrawList->AddCircleFilled(center, radius-pad, GetColorU32(ImGuiCol_CheckMark), 16);
7770     }
7771
7772     if (style.FrameBorderSize > 0.0f)
7773     {
7774         window->DrawList->AddCircle(center+ImVec2(1,1), radius, GetColorU32(ImGuiCol_BorderShadow), 16, style.FrameBorderSize);
7775         window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16, style.FrameBorderSize);
7776     }
7777
7778     if (g.LogEnabled)
7779         LogRenderedText(&text_bb.Min, active ? "(x)" : "( )");
7780     if (label_size.x > 0.0f)
7781         RenderText(text_bb.Min, label);
7782
7783     return pressed;
7784 }
7785
7786 bool ImGui::RadioButton(const char* label, int* v, int v_button)
7787 {
7788     const bool pressed = RadioButton(label, *v == v_button);
7789     if (pressed)
7790     {
7791         *v = v_button;
7792     }
7793     return pressed;
7794 }
7795
7796 static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end)
7797 {
7798     int line_count = 0;
7799     const char* s = text_begin;
7800     while (char c = *s++) // We are only matching for \n so we can ignore UTF-8 decoding
7801         if (c == '\n')
7802             line_count++;
7803     s--;
7804     if (s[0] != '\n' && s[0] != '\r')
7805         line_count++;
7806     *out_text_end = s;
7807     return line_count;
7808 }
7809
7810 static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line)
7811 {
7812     ImFont* font = GImGui->Font;
7813     const float line_height = GImGui->FontSize;
7814     const float scale = line_height / font->FontSize;
7815
7816     ImVec2 text_size = ImVec2(0,0);
7817     float line_width = 0.0f;
7818
7819     const ImWchar* s = text_begin;
7820     while (s < text_end)
7821     {
7822         unsigned int c = (unsigned int)(*s++);
7823         if (c == '\n')
7824         {
7825             text_size.x = ImMax(text_size.x, line_width);
7826             text_size.y += line_height;
7827             line_width = 0.0f;
7828             if (stop_on_new_line)
7829                 break;
7830             continue;
7831         }
7832         if (c == '\r')
7833             continue;
7834
7835         const float char_width = font->GetCharAdvance((unsigned short)c) * scale;
7836         line_width += char_width;
7837     }
7838
7839     if (text_size.x < line_width)
7840         text_size.x = line_width;
7841
7842     if (out_offset)
7843         *out_offset = ImVec2(line_width, text_size.y + line_height);  // offset allow for the possibility of sitting after a trailing \n
7844
7845     if (line_width > 0 || text_size.y == 0.0f)                        // whereas size.y will ignore the trailing \n
7846         text_size.y += line_height;
7847
7848     if (remaining)
7849         *remaining = s;
7850
7851     return text_size;
7852 }
7853
7854 // 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)
7855 namespace ImGuiStb
7856 {
7857
7858 static int     STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj)                             { return obj->CurLenW; }
7859 static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx)                      { return obj->Text[idx]; }
7860 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); }
7861 static int     STB_TEXTEDIT_KEYTOTEXT(int key)                                                    { return key >= 0x10000 ? 0 : key; }
7862 static ImWchar STB_TEXTEDIT_NEWLINE = '\n';
7863 static void    STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx)
7864 {
7865     const ImWchar* text = obj->Text.Data;
7866     const ImWchar* text_remaining = NULL;
7867     const ImVec2 size = InputTextCalcTextSizeW(text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true);
7868     r->x0 = 0.0f;
7869     r->x1 = size.x;
7870     r->baseline_y_delta = size.y;
7871     r->ymin = 0.0f;
7872     r->ymax = size.y;
7873     r->num_chars = (int)(text_remaining - (text + line_start_idx));
7874 }
7875
7876 static bool is_separator(unsigned int c)                                        { return ImCharIsSpace(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; }
7877 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; }
7878 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; }
7879 #ifdef __APPLE__    // FIXME: Move setting to IO structure
7880 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; }
7881 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; }
7882 #else
7883 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; }
7884 #endif
7885 #define STB_TEXTEDIT_MOVEWORDLEFT   STB_TEXTEDIT_MOVEWORDLEFT_IMPL    // They need to be #define for stb_textedit.h
7886 #define STB_TEXTEDIT_MOVEWORDRIGHT  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL
7887
7888 static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n)
7889 {
7890     ImWchar* dst = obj->Text.Data + pos;
7891
7892     // We maintain our buffer length in both UTF-8 and wchar formats
7893     obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n);
7894     obj->CurLenW -= n;
7895
7896     // Offset remaining text
7897     const ImWchar* src = obj->Text.Data + pos + n;
7898     while (ImWchar c = *src++)
7899         *dst++ = c;
7900     *dst = '\0';
7901 }
7902
7903 static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len)
7904 {
7905     const int text_len = obj->CurLenW;
7906     IM_ASSERT(pos <= text_len);
7907     if (new_text_len + text_len + 1 > obj->Text.Size)
7908         return false;
7909
7910     const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len);
7911     if (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufSizeA)
7912         return false;
7913
7914     ImWchar* text = obj->Text.Data;
7915     if (pos != text_len)
7916         memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar));
7917     memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar));
7918
7919     obj->CurLenW += new_text_len;
7920     obj->CurLenA += new_text_len_utf8;
7921     obj->Text[obj->CurLenW] = '\0';
7922
7923     return true;
7924 }
7925
7926 // 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)
7927 #define STB_TEXTEDIT_K_LEFT         0x10000 // keyboard input to move cursor left
7928 #define STB_TEXTEDIT_K_RIGHT        0x10001 // keyboard input to move cursor right
7929 #define STB_TEXTEDIT_K_UP           0x10002 // keyboard input to move cursor up
7930 #define STB_TEXTEDIT_K_DOWN         0x10003 // keyboard input to move cursor down
7931 #define STB_TEXTEDIT_K_LINESTART    0x10004 // keyboard input to move cursor to start of line
7932 #define STB_TEXTEDIT_K_LINEEND      0x10005 // keyboard input to move cursor to end of line
7933 #define STB_TEXTEDIT_K_TEXTSTART    0x10006 // keyboard input to move cursor to start of text
7934 #define STB_TEXTEDIT_K_TEXTEND      0x10007 // keyboard input to move cursor to end of text
7935 #define STB_TEXTEDIT_K_DELETE       0x10008 // keyboard input to delete selection or character under cursor
7936 #define STB_TEXTEDIT_K_BACKSPACE    0x10009 // keyboard input to delete selection or character left of cursor
7937 #define STB_TEXTEDIT_K_UNDO         0x1000A // keyboard input to perform undo
7938 #define STB_TEXTEDIT_K_REDO         0x1000B // keyboard input to perform redo
7939 #define STB_TEXTEDIT_K_WORDLEFT     0x1000C // keyboard input to move cursor left one word
7940 #define STB_TEXTEDIT_K_WORDRIGHT    0x1000D // keyboard input to move cursor right one word
7941 #define STB_TEXTEDIT_K_SHIFT        0x20000
7942
7943 #define STB_TEXTEDIT_IMPLEMENTATION
7944 #include "stb_textedit.h"
7945
7946 }
7947
7948 void ImGuiTextEditState::OnKeyPressed(int key)
7949 {
7950     stb_textedit_key(this, &StbState, key);
7951     CursorFollow = true;
7952     CursorAnimReset();
7953 }
7954
7955 // Public API to manipulate UTF-8 text
7956 // We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar)
7957 // FIXME: The existence of this rarely exercised code path is a bit of a nuisance.
7958 void ImGuiTextEditCallbackData::DeleteChars(int pos, int bytes_count)
7959 {
7960     IM_ASSERT(pos + bytes_count <= BufTextLen);
7961     char* dst = Buf + pos;
7962     const char* src = Buf + pos + bytes_count;
7963     while (char c = *src++)
7964         *dst++ = c;
7965     *dst = '\0';
7966
7967     if (CursorPos + bytes_count >= pos)
7968         CursorPos -= bytes_count;
7969     else if (CursorPos >= pos)
7970         CursorPos = pos;
7971     SelectionStart = SelectionEnd = CursorPos;
7972     BufDirty = true;
7973     BufTextLen -= bytes_count;
7974 }
7975
7976 void ImGuiTextEditCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end)
7977 {
7978     const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text);
7979     if (new_text_len + BufTextLen + 1 >= BufSize)
7980         return;
7981
7982     if (BufTextLen != pos)
7983         memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos));
7984     memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char));
7985     Buf[BufTextLen + new_text_len] = '\0';
7986
7987     if (CursorPos >= pos)
7988         CursorPos += new_text_len;
7989     SelectionStart = SelectionEnd = CursorPos;
7990     BufDirty = true;
7991     BufTextLen += new_text_len;
7992 }
7993
7994 // Return false to discard a character.
7995 static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
7996 {
7997     unsigned int c = *p_char;
7998
7999     if (c < 128 && c != ' ' && !isprint((int)(c & 0xFF)))
8000     {
8001         bool pass = false;
8002         pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline));
8003         pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput));
8004         if (!pass)
8005             return false;
8006     }
8007
8008     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.
8009         return false;
8010
8011     if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank))
8012     {
8013         if (flags & ImGuiInputTextFlags_CharsDecimal)
8014             if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/'))
8015                 return false;
8016
8017         if (flags & ImGuiInputTextFlags_CharsHexadecimal)
8018             if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F'))
8019                 return false;
8020
8021         if (flags & ImGuiInputTextFlags_CharsUppercase)
8022             if (c >= 'a' && c <= 'z')
8023                 *p_char = (c += (unsigned int)('A'-'a'));
8024
8025         if (flags & ImGuiInputTextFlags_CharsNoBlank)
8026             if (ImCharIsSpace(c))
8027                 return false;
8028     }
8029
8030     if (flags & ImGuiInputTextFlags_CallbackCharFilter)
8031     {
8032         ImGuiTextEditCallbackData callback_data;
8033         memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData));
8034         callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter;
8035         callback_data.EventChar = (ImWchar)c;
8036         callback_data.Flags = flags;
8037         callback_data.UserData = user_data;
8038         if (callback(&callback_data) != 0)
8039             return false;
8040         *p_char = callback_data.EventChar;
8041         if (!callback_data.EventChar)
8042             return false;
8043     }
8044
8045     return true;
8046 }
8047
8048 // Edit a string of text
8049 // 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.
8050 // 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
8051 bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
8052 {
8053     ImGuiWindow* window = GetCurrentWindow();
8054     if (window->SkipItems)
8055         return false;
8056
8057     IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys)
8058     IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key)
8059
8060     ImGuiContext& g = *GImGui;
8061     const ImGuiIO& io = g.IO;
8062     const ImGuiStyle& style = g.Style;
8063
8064     const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0;
8065     const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0;
8066     const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0;
8067
8068     if (is_multiline) // Open group before calling GetID() because groups tracks id created during their spawn
8069         BeginGroup();
8070     const ImGuiID id = window->GetID(label);
8071     const ImVec2 label_size = CalcTextSize(label, NULL, true);
8072     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
8073     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
8074     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));
8075
8076     ImGuiWindow* draw_window = window;
8077     if (is_multiline)
8078     {
8079         if (!BeginChildFrame(id, frame_bb.GetSize()))
8080         {
8081             EndChildFrame();
8082             EndGroup();
8083             return false;
8084         }
8085         draw_window = GetCurrentWindow();
8086         size.x -= draw_window->ScrollbarSizes.x;
8087     }
8088     else
8089     {
8090         ItemSize(total_bb, style.FramePadding.y);
8091         if (!ItemAdd(total_bb, id))
8092             return false;
8093     }
8094     const bool hovered = ItemHoverable(frame_bb, id);
8095     if (hovered)
8096         g.MouseCursor = ImGuiMouseCursor_TextInput;
8097
8098     // Password pushes a temporary font with only a fallback glyph
8099     if (is_password)
8100     {
8101         const ImFontGlyph* glyph = g.Font->FindGlyph('*');
8102         ImFont* password_font = &g.InputTextPasswordFont;
8103         password_font->FontSize = g.Font->FontSize;
8104         password_font->Scale = g.Font->Scale;
8105         password_font->DisplayOffset = g.Font->DisplayOffset;
8106         password_font->Ascent = g.Font->Ascent;
8107         password_font->Descent = g.Font->Descent;
8108         password_font->ContainerAtlas = g.Font->ContainerAtlas;
8109         password_font->FallbackGlyph = glyph;
8110         password_font->FallbackAdvanceX = glyph->AdvanceX;
8111         IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexAdvanceX.empty() && password_font->IndexLookup.empty());
8112         PushFont(password_font);
8113     }
8114
8115     // NB: we are only allowed to access 'edit_state' if we are the active widget.
8116     ImGuiTextEditState& edit_state = g.InputTextState;
8117
8118     const bool focus_requested = FocusableItemRegister(window, id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 0);    // Using completion callback disable keyboard tabbing
8119     const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent);
8120     const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code;
8121
8122     const bool user_clicked = hovered && io.MouseClicked[0];
8123     const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.Id == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY");
8124
8125     bool clear_active_id = false;
8126
8127     bool select_all = (g.ActiveId != id) && (flags & ImGuiInputTextFlags_AutoSelectAll) != 0;
8128     if (focus_requested || user_clicked || user_scrolled)
8129     {
8130         if (g.ActiveId != id)
8131         {
8132             // Start edition
8133             // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar)
8134             // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode)
8135             const int prev_len_w = edit_state.CurLenW;
8136             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.
8137             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.
8138             ImStrncpy(edit_state.InitialText.Data, buf, edit_state.InitialText.Size);
8139             const char* buf_end = NULL;
8140             edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end);
8141             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.
8142             edit_state.CursorAnimReset();
8143
8144             // Preserve cursor position and undo/redo stack if we come back to same widget
8145             // FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar).
8146             const bool recycle_state = (edit_state.Id == id) && (prev_len_w == edit_state.CurLenW);
8147             if (recycle_state)
8148             {
8149                 // Recycle existing cursor/selection/undo stack but clamp position
8150                 // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler.
8151                 edit_state.CursorClamp();
8152             }
8153             else
8154             {
8155                 edit_state.Id = id;
8156                 edit_state.ScrollX = 0.0f;
8157                 stb_textedit_initialize_state(&edit_state.StbState, !is_multiline);
8158                 if (!is_multiline && focus_requested_by_code)
8159                     select_all = true;
8160             }
8161             if (flags & ImGuiInputTextFlags_AlwaysInsertMode)
8162                 edit_state.StbState.insert_mode = true;
8163             if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl)))
8164                 select_all = true;
8165         }
8166         SetActiveID(id, window);
8167         FocusWindow(window);
8168     }
8169     else if (io.MouseClicked[0])
8170     {
8171         // Release focus when we click outside
8172         clear_active_id = true;
8173     }
8174
8175     bool value_changed = false;
8176     bool enter_pressed = false;
8177
8178     if (g.ActiveId == id)
8179     {
8180         if (!is_editable && !g.ActiveIdIsJustActivated)
8181         {
8182             // When read-only we always use the live data passed to the function
8183             edit_state.Text.resize(buf_size+1);
8184             const char* buf_end = NULL;
8185             edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end);
8186             edit_state.CurLenA = (int)(buf_end - buf);
8187             edit_state.CursorClamp();
8188         }
8189
8190         edit_state.BufSizeA = buf_size;
8191
8192         // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget.
8193         // Down the line we should have a cleaner library-wide concept of Selected vs Active.
8194         g.ActiveIdAllowOverlap = !io.MouseDown[0];
8195         g.WantTextInputNextFrame = 1;
8196
8197         // Edit in progress
8198         const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX;
8199         const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize*0.5f));
8200
8201         const bool osx_double_click_selects_words = io.OptMacOSXBehaviors;      // OS X style: Double click selects by word instead of selecting whole text
8202         if (select_all || (hovered && !osx_double_click_selects_words && io.MouseDoubleClicked[0]))
8203         {
8204             edit_state.SelectAll();
8205             edit_state.SelectedAllMouseLock = true;
8206         }
8207         else if (hovered && osx_double_click_selects_words && io.MouseDoubleClicked[0])
8208         {
8209             // Select a word only, OS X style (by simulating keystrokes)
8210             edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT);
8211             edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT);
8212         }
8213         else if (io.MouseClicked[0] && !edit_state.SelectedAllMouseLock)
8214         {
8215             stb_textedit_click(&edit_state, &edit_state.StbState, mouse_x, mouse_y);
8216             edit_state.CursorAnimReset();
8217         }
8218         else if (io.MouseDown[0] && !edit_state.SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f))
8219         {
8220             stb_textedit_drag(&edit_state, &edit_state.StbState, mouse_x, mouse_y);
8221             edit_state.CursorAnimReset();
8222             edit_state.CursorFollow = true;
8223         }
8224         if (edit_state.SelectedAllMouseLock && !io.MouseDown[0])
8225             edit_state.SelectedAllMouseLock = false;
8226
8227         if (io.InputCharacters[0])
8228         {
8229             // Process text input (before we check for Return because using some IME will effectively send a Return?)
8230             // 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.
8231             if (!(io.KeyCtrl && !io.KeyAlt) && is_editable)
8232             {
8233                 for (int n = 0; n < IM_ARRAYSIZE(io.InputCharacters) && io.InputCharacters[n]; n++)
8234                     if (unsigned int c = (unsigned int)io.InputCharacters[n])
8235                     {
8236                         // Insert character if they pass filtering
8237                         if (!InputTextFilterCharacter(&c, flags, callback, user_data))
8238                             continue;
8239                         edit_state.OnKeyPressed((int)c);
8240                     }
8241             }
8242
8243             // Consume characters
8244             memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters));
8245         }
8246     }
8247
8248     bool cancel_edit = false;
8249     if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id)
8250     {
8251         // Handle key-presses
8252         const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0);
8253         const bool is_shortcut_key_only = (io.OptMacOSXBehaviors ? (io.KeySuper && !io.KeyCtrl) : (io.KeyCtrl && !io.KeySuper)) && !io.KeyAlt && !io.KeyShift; // OS X style: Shortcuts using Cmd/Super instead of Ctrl
8254         const bool is_wordmove_key_down = io.OptMacOSXBehaviors ? io.KeyAlt : io.KeyCtrl;                     // OS X style: Text editing cursor movement using Alt instead of Ctrl
8255         const bool is_startend_key_down = io.OptMacOSXBehaviors && io.KeySuper && !io.KeyCtrl && !io.KeyAlt;  // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End
8256
8257         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); }
8258         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); }
8259         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); }
8260         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); }
8261         else if (IsKeyPressedMap(ImGuiKey_Home))                        { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); }
8262         else if (IsKeyPressedMap(ImGuiKey_End))                         { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); }
8263         else if (IsKeyPressedMap(ImGuiKey_Delete) && is_editable)       { edit_state.OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); }
8264         else if (IsKeyPressedMap(ImGuiKey_Backspace) && is_editable)
8265         {
8266             if (!edit_state.HasSelection())
8267             {
8268                 if (is_wordmove_key_down) edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT|STB_TEXTEDIT_K_SHIFT);
8269                 else if (io.OptMacOSXBehaviors && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) edit_state.OnKeyPressed(STB_TEXTEDIT_K_LINESTART|STB_TEXTEDIT_K_SHIFT);
8270             }
8271             edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask);
8272         }
8273         else if (IsKeyPressedMap(ImGuiKey_Enter))
8274         {
8275             bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0;
8276             if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl))
8277             {
8278                 enter_pressed = clear_active_id = true;
8279             }
8280             else if (is_editable)
8281             {
8282                 unsigned int c = '\n'; // Insert new line
8283                 if (InputTextFilterCharacter(&c, flags, callback, user_data))
8284                     edit_state.OnKeyPressed((int)c);
8285             }
8286         }
8287         else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !io.KeyCtrl && !io.KeyShift && !io.KeyAlt && is_editable)
8288         {
8289             unsigned int c = '\t'; // Insert TAB
8290             if (InputTextFilterCharacter(&c, flags, callback, user_data))
8291                 edit_state.OnKeyPressed((int)c);
8292         }
8293         else if (IsKeyPressedMap(ImGuiKey_Escape))                                      { clear_active_id = cancel_edit = true; }
8294         else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Z) && is_editable)    { edit_state.OnKeyPressed(STB_TEXTEDIT_K_UNDO); edit_state.ClearSelection(); }
8295         else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Y) && is_editable)    { edit_state.OnKeyPressed(STB_TEXTEDIT_K_REDO); edit_state.ClearSelection(); }
8296         else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_A))                   { edit_state.SelectAll(); edit_state.CursorFollow = true; }
8297         else if (is_shortcut_key_only && !is_password && ((IsKeyPressedMap(ImGuiKey_X) && is_editable) || IsKeyPressedMap(ImGuiKey_C)) && (!is_multiline || edit_state.HasSelection()))
8298         {
8299             // Cut, Copy
8300             const bool cut = IsKeyPressedMap(ImGuiKey_X);
8301             if (cut && !edit_state.HasSelection())
8302                 edit_state.SelectAll();
8303
8304             if (io.SetClipboardTextFn)
8305             {
8306                 const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0;
8307                 const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : edit_state.CurLenW;
8308                 edit_state.TempTextBuffer.resize((ie-ib) * 4 + 1);
8309                 ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data+ib, edit_state.Text.Data+ie);
8310                 SetClipboardText(edit_state.TempTextBuffer.Data);
8311             }
8312
8313             if (cut)
8314             {
8315                 edit_state.CursorFollow = true;
8316                 stb_textedit_cut(&edit_state, &edit_state.StbState);
8317             }
8318         }
8319         else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_V) && is_editable)
8320         {
8321             // Paste
8322             if (const char* clipboard = GetClipboardText())
8323             {
8324                 // Filter pasted buffer
8325                 const int clipboard_len = (int)strlen(clipboard);
8326                 ImWchar* clipboard_filtered = (ImWchar*)ImGui::MemAlloc((clipboard_len+1) * sizeof(ImWchar));
8327                 int clipboard_filtered_len = 0;
8328                 for (const char* s = clipboard; *s; )
8329                 {
8330                     unsigned int c;
8331                     s += ImTextCharFromUtf8(&c, s, NULL);
8332                     if (c == 0)
8333                         break;
8334                     if (c >= 0x10000 || !InputTextFilterCharacter(&c, flags, callback, user_data))
8335                         continue;
8336                     clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c;
8337                 }
8338                 clipboard_filtered[clipboard_filtered_len] = 0;
8339                 if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation
8340                 {
8341                     stb_textedit_paste(&edit_state, &edit_state.StbState, clipboard_filtered, clipboard_filtered_len);
8342                     edit_state.CursorFollow = true;
8343                 }
8344                 ImGui::MemFree(clipboard_filtered);
8345             }
8346         }
8347     }
8348
8349     if (g.ActiveId == id)
8350     {
8351         if (cancel_edit)
8352         {
8353             // Restore initial value
8354             if (is_editable)
8355             {
8356                 ImStrncpy(buf, edit_state.InitialText.Data, buf_size);
8357                 value_changed = true;
8358             }
8359         }
8360
8361         // When using 'ImGuiInputTextFlags_EnterReturnsTrue' as a special case we reapply the live buffer back to the input buffer before clearing ActiveId, even though strictly speaking it wasn't modified on this frame.
8362         // If we didn't do that, code like InputInt() with ImGuiInputTextFlags_EnterReturnsTrue would fail. Also this allows the user to use InputText() with ImGuiInputTextFlags_EnterReturnsTrue without maintaining any user-side storage.
8363         bool apply_edit_back_to_user_buffer = !cancel_edit || (enter_pressed && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0);
8364         if (apply_edit_back_to_user_buffer)
8365         {
8366             // Apply new value immediately - copy modified buffer back
8367             // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer
8368             // FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect.
8369             // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks.
8370             if (is_editable)
8371             {
8372                 edit_state.TempTextBuffer.resize(edit_state.Text.Size * 4);
8373                 ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data, NULL);
8374             }
8375
8376             // User callback
8377             if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0)
8378             {
8379                 IM_ASSERT(callback != NULL);
8380
8381                 // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment.
8382                 ImGuiInputTextFlags event_flag = 0;
8383                 ImGuiKey event_key = ImGuiKey_COUNT;
8384                 if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab))
8385                 {
8386                     event_flag = ImGuiInputTextFlags_CallbackCompletion;
8387                     event_key = ImGuiKey_Tab;
8388                 }
8389                 else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow))
8390                 {
8391                     event_flag = ImGuiInputTextFlags_CallbackHistory;
8392                     event_key = ImGuiKey_UpArrow;
8393                 }
8394                 else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow))
8395                 {
8396                     event_flag = ImGuiInputTextFlags_CallbackHistory;
8397                     event_key = ImGuiKey_DownArrow;
8398                 }
8399                 else if (flags & ImGuiInputTextFlags_CallbackAlways)
8400                     event_flag = ImGuiInputTextFlags_CallbackAlways;
8401
8402                 if (event_flag)
8403                 {
8404                     ImGuiTextEditCallbackData callback_data;
8405                     memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData));
8406                     callback_data.EventFlag = event_flag;
8407                     callback_data.Flags = flags;
8408                     callback_data.UserData = user_data;
8409                     callback_data.ReadOnly = !is_editable;
8410
8411                     callback_data.EventKey = event_key;
8412                     callback_data.Buf = edit_state.TempTextBuffer.Data;
8413                     callback_data.BufTextLen = edit_state.CurLenA;
8414                     callback_data.BufSize = edit_state.BufSizeA;
8415                     callback_data.BufDirty = false;
8416
8417                     // 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)
8418                     ImWchar* text = edit_state.Text.Data;
8419                     const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor);
8420                     const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start);
8421                     const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_end);
8422
8423                     // Call user code
8424                     callback(&callback_data);
8425
8426                     // Read back what user may have modified
8427                     IM_ASSERT(callback_data.Buf == edit_state.TempTextBuffer.Data);  // Invalid to modify those fields
8428                     IM_ASSERT(callback_data.BufSize == edit_state.BufSizeA);
8429                     IM_ASSERT(callback_data.Flags == flags);
8430                     if (callback_data.CursorPos != utf8_cursor_pos)            edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos);
8431                     if (callback_data.SelectionStart != utf8_selection_start)  edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart);
8432                     if (callback_data.SelectionEnd != utf8_selection_end)      edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd);
8433                     if (callback_data.BufDirty)
8434                     {
8435                         IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!
8436                         edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, callback_data.Buf, NULL);
8437                         edit_state.CurLenA = callback_data.BufTextLen;  // Assume correct length and valid UTF-8 from user, saves us an extra strlen()
8438                         edit_state.CursorAnimReset();
8439                     }
8440                 }
8441             }
8442
8443             // Copy back to user buffer
8444             if (is_editable && strcmp(edit_state.TempTextBuffer.Data, buf) != 0)
8445             {
8446                 ImStrncpy(buf, edit_state.TempTextBuffer.Data, buf_size);
8447                 value_changed = true;
8448             }
8449         }
8450     }
8451
8452     // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value)
8453     if (clear_active_id && g.ActiveId == id)
8454         ClearActiveID();
8455
8456     // Render
8457     // 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.
8458     const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempTextBuffer.Data : buf; buf = NULL; 
8459
8460     if (!is_multiline)
8461         RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
8462
8463     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
8464     ImVec2 render_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding;
8465     ImVec2 text_size(0.f, 0.f);
8466     const bool is_currently_scrolling = (edit_state.Id == id && is_multiline && g.ActiveId == draw_window->GetIDNoKeepAlive("#SCROLLY"));
8467     if (g.ActiveId == id || is_currently_scrolling)
8468     {
8469         edit_state.CursorAnim += io.DeltaTime;
8470
8471         // This is going to be messy. We need to:
8472         // - Display the text (this alone can be more easily clipped)
8473         // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation)
8474         // - Measure text height (for scrollbar)
8475         // 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)
8476         // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8.
8477         const ImWchar* text_begin = edit_state.Text.Data;
8478         ImVec2 cursor_offset, select_start_offset;
8479
8480         {
8481             // Count lines + find lines numbers straddling 'cursor' and 'select_start' position.
8482             const ImWchar* searches_input_ptr[2];
8483             searches_input_ptr[0] = text_begin + edit_state.StbState.cursor;
8484             searches_input_ptr[1] = NULL;
8485             int searches_remaining = 1;
8486             int searches_result_line_number[2] = { -1, -999 };
8487             if (edit_state.StbState.select_start != edit_state.StbState.select_end)
8488             {
8489                 searches_input_ptr[1] = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
8490                 searches_result_line_number[1] = -1;
8491                 searches_remaining++;
8492             }
8493
8494             // Iterate all lines to find our line numbers
8495             // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter.
8496             searches_remaining += is_multiline ? 1 : 0;
8497             int line_count = 0;
8498             for (const ImWchar* s = text_begin; *s != 0; s++)
8499                 if (*s == '\n')
8500                 {
8501                     line_count++;
8502                     if (searches_result_line_number[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_number[0] = line_count; if (--searches_remaining <= 0) break; }
8503                     if (searches_result_line_number[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_number[1] = line_count; if (--searches_remaining <= 0) break; }
8504                 }
8505             line_count++;
8506             if (searches_result_line_number[0] == -1) searches_result_line_number[0] = line_count;
8507             if (searches_result_line_number[1] == -1) searches_result_line_number[1] = line_count;
8508
8509             // Calculate 2d position by finding the beginning of the line and measuring distance
8510             cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x;
8511             cursor_offset.y = searches_result_line_number[0] * g.FontSize;
8512             if (searches_result_line_number[1] >= 0)
8513             {
8514                 select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x;
8515                 select_start_offset.y = searches_result_line_number[1] * g.FontSize;
8516             }
8517
8518             // Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224)
8519             if (is_multiline)
8520                 text_size = ImVec2(size.x, line_count * g.FontSize);
8521         }
8522
8523         // Scroll
8524         if (edit_state.CursorFollow)
8525         {
8526             // Horizontal scroll in chunks of quarter width
8527             if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll))
8528             {
8529                 const float scroll_increment_x = size.x * 0.25f;
8530                 if (cursor_offset.x < edit_state.ScrollX)
8531                     edit_state.ScrollX = (float)(int)ImMax(0.0f, cursor_offset.x - scroll_increment_x);
8532                 else if (cursor_offset.x - size.x >= edit_state.ScrollX)
8533                     edit_state.ScrollX = (float)(int)(cursor_offset.x - size.x + scroll_increment_x);
8534             }
8535             else
8536             {
8537                 edit_state.ScrollX = 0.0f;
8538             }
8539
8540             // Vertical scroll
8541             if (is_multiline)
8542             {
8543                 float scroll_y = draw_window->Scroll.y;
8544                 if (cursor_offset.y - g.FontSize < scroll_y)
8545                     scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize);
8546                 else if (cursor_offset.y - size.y >= scroll_y)
8547                     scroll_y = cursor_offset.y - size.y;
8548                 draw_window->DC.CursorPos.y += (draw_window->Scroll.y - scroll_y);   // To avoid a frame of lag
8549                 draw_window->Scroll.y = scroll_y;
8550                 render_pos.y = draw_window->DC.CursorPos.y;
8551             }
8552         }
8553         edit_state.CursorFollow = false;
8554         const ImVec2 render_scroll = ImVec2(edit_state.ScrollX, 0.0f);
8555
8556         // Draw selection
8557         if (edit_state.StbState.select_start != edit_state.StbState.select_end)
8558         {
8559             const ImWchar* text_selected_begin = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
8560             const ImWchar* text_selected_end = text_begin + ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end);
8561
8562             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.
8563             float bg_offy_dn = is_multiline ? 0.0f : 2.0f;
8564             ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg);
8565             ImVec2 rect_pos = render_pos + select_start_offset - render_scroll;
8566             for (const ImWchar* p = text_selected_begin; p < text_selected_end; )
8567             {
8568                 if (rect_pos.y > clip_rect.w + g.FontSize)
8569                     break;
8570                 if (rect_pos.y < clip_rect.y)
8571                 {
8572                     while (p < text_selected_end)
8573                         if (*p++ == '\n')
8574                             break;
8575                 }
8576                 else
8577                 {
8578                     ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true);
8579                     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
8580                     ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos +ImVec2(rect_size.x, bg_offy_dn));
8581                     rect.ClipWith(clip_rect);
8582                     if (rect.Overlaps(clip_rect))
8583                         draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
8584                 }
8585                 rect_pos.x = render_pos.x - render_scroll.x;
8586                 rect_pos.y += g.FontSize;
8587             }
8588         }
8589
8590         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);
8591
8592         // Draw blinking cursor
8593         bool cursor_is_visible = (!g.IO.OptCursorBlink) || (g.InputTextState.CursorAnim <= 0.0f) || fmodf(g.InputTextState.CursorAnim, 1.20f) <= 0.80f;
8594         ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll;
8595         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);
8596         if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
8597             draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text));
8598
8599         // 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.)
8600         if (is_editable)
8601             g.OsImePosRequest = ImVec2(cursor_screen_pos.x - 1, cursor_screen_pos.y - g.FontSize);
8602     }
8603     else
8604     {
8605         // Render text only
8606         const char* buf_end = NULL;
8607         if (is_multiline)
8608             text_size = ImVec2(size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_end) * g.FontSize); // We don't need width
8609         draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos, GetColorU32(ImGuiCol_Text), buf_display, buf_end, 0.0f, is_multiline ? NULL : &clip_rect);
8610     }
8611
8612     if (is_multiline)
8613     {
8614         Dummy(text_size + ImVec2(0.0f, g.FontSize)); // Always add room to scroll an extra line
8615         EndChildFrame();
8616         EndGroup();
8617     }
8618
8619     if (is_password)
8620         PopFont();
8621
8622     // Log as text
8623     if (g.LogEnabled && !is_password)
8624         LogRenderedText(&render_pos, buf_display, NULL);
8625
8626     if (label_size.x > 0)
8627         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
8628
8629     if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0)
8630         return enter_pressed;
8631     else
8632         return value_changed;
8633 }
8634
8635 bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
8636 {
8637     IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline()
8638     return InputTextEx(label, buf, (int)buf_size, ImVec2(0,0), flags, callback, user_data);
8639 }
8640
8641 bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
8642 {
8643     return InputTextEx(label, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data);
8644 }
8645
8646 static inline float SmallSquareSize()
8647 {
8648     ImGuiContext& g = *GImGui;
8649     return g.FontSize + g.Style.FramePadding.y * 2.0f;
8650 }
8651
8652 // NB: scalar_format here must be a simple "%xx" format string with no prefix/suffix (unlike the Drag/Slider functions "display_format" argument)
8653 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)
8654 {
8655     ImGuiWindow* window = GetCurrentWindow();
8656     if (window->SkipItems)
8657         return false;
8658
8659     ImGuiContext& g = *GImGui;
8660     const ImGuiStyle& style = g.Style;
8661     const ImVec2 label_size = CalcTextSize(label, NULL, true);
8662
8663     BeginGroup();
8664     PushID(label);
8665     const ImVec2 button_sz = ImVec2(SmallSquareSize(), SmallSquareSize());
8666     if (step_ptr)
8667         PushItemWidth(ImMax(1.0f, CalcItemWidth() - (button_sz.x + style.ItemInnerSpacing.x)*2));
8668
8669     char buf[64];
8670     DataTypeFormatString(data_type, data_ptr, scalar_format, buf, IM_ARRAYSIZE(buf));
8671
8672     bool value_changed = false;
8673     if (!(extra_flags & ImGuiInputTextFlags_CharsHexadecimal))
8674         extra_flags |= ImGuiInputTextFlags_CharsDecimal;
8675     extra_flags |= ImGuiInputTextFlags_AutoSelectAll;
8676     if (InputText("", buf, IM_ARRAYSIZE(buf), extra_flags)) // PushId(label) + "" gives us the expected ID from outside point of view
8677         value_changed = DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, scalar_format);
8678
8679     // Step buttons
8680     if (step_ptr)
8681     {
8682         PopItemWidth();
8683         SameLine(0, style.ItemInnerSpacing.x);
8684         if (ButtonEx("-", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups))
8685         {
8686             DataTypeApplyOp(data_type, '-', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr);
8687             value_changed = true;
8688         }
8689         SameLine(0, style.ItemInnerSpacing.x);
8690         if (ButtonEx("+", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups))
8691         {
8692             DataTypeApplyOp(data_type, '+', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr);
8693             value_changed = true;
8694         }
8695     }
8696     PopID();
8697
8698     if (label_size.x > 0)
8699     {
8700         SameLine(0, style.ItemInnerSpacing.x);
8701         RenderText(ImVec2(window->DC.CursorPos.x, window->DC.CursorPos.y + style.FramePadding.y), label);
8702         ItemSize(label_size, style.FramePadding.y);
8703     }
8704     EndGroup();
8705
8706     return value_changed;
8707 }
8708
8709 bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags extra_flags)
8710 {
8711     char display_format[16];
8712     if (decimal_precision < 0)
8713         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
8714     else
8715         ImFormatString(display_format, IM_ARRAYSIZE(display_format), "%%.%df", decimal_precision);
8716     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);
8717 }
8718
8719 bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags extra_flags)
8720 {
8721     // 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.
8722     const char* scalar_format = (extra_flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d";
8723     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);
8724 }
8725
8726 bool ImGui::InputFloatN(const char* label, float* v, int components, int decimal_precision, ImGuiInputTextFlags extra_flags)
8727 {
8728     ImGuiWindow* window = GetCurrentWindow();
8729     if (window->SkipItems)
8730         return false;
8731
8732     ImGuiContext& g = *GImGui;
8733     bool value_changed = false;
8734     BeginGroup();
8735     PushID(label);
8736     PushMultiItemsWidths(components);
8737     for (int i = 0; i < components; i++)
8738     {
8739         PushID(i);
8740         value_changed |= InputFloat("##v", &v[i], 0, 0, decimal_precision, extra_flags);
8741         SameLine(0, g.Style.ItemInnerSpacing.x);
8742         PopID();
8743         PopItemWidth();
8744     }
8745     PopID();
8746
8747     TextUnformatted(label, FindRenderedTextEnd(label));
8748     EndGroup();
8749
8750     return value_changed;
8751 }
8752
8753 bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags extra_flags)
8754 {
8755     return InputFloatN(label, v, 2, decimal_precision, extra_flags);
8756 }
8757
8758 bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags extra_flags)
8759 {
8760     return InputFloatN(label, v, 3, decimal_precision, extra_flags);
8761 }
8762
8763 bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags extra_flags)
8764 {
8765     return InputFloatN(label, v, 4, decimal_precision, extra_flags);
8766 }
8767
8768 bool ImGui::InputIntN(const char* label, int* v, int components, ImGuiInputTextFlags extra_flags)
8769 {
8770     ImGuiWindow* window = GetCurrentWindow();
8771     if (window->SkipItems)
8772         return false;
8773
8774     ImGuiContext& g = *GImGui;
8775     bool value_changed = false;
8776     BeginGroup();
8777     PushID(label);
8778     PushMultiItemsWidths(components);
8779     for (int i = 0; i < components; i++)
8780     {
8781         PushID(i);
8782         value_changed |= InputInt("##v", &v[i], 0, 0, extra_flags);
8783         SameLine(0, g.Style.ItemInnerSpacing.x);
8784         PopID();
8785         PopItemWidth();
8786     }
8787     PopID();
8788
8789     TextUnformatted(label, FindRenderedTextEnd(label));
8790     EndGroup();
8791
8792     return value_changed;
8793 }
8794
8795 bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags)
8796 {
8797     return InputIntN(label, v, 2, extra_flags);
8798 }
8799
8800 bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags)
8801 {
8802     return InputIntN(label, v, 3, extra_flags);
8803 }
8804
8805 bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags)
8806 {
8807     return InputIntN(label, v, 4, extra_flags);
8808 }
8809
8810 static bool Items_ArrayGetter(void* data, int idx, const char** out_text)
8811 {
8812     const char* const* items = (const char* const*)data;
8813     if (out_text)
8814         *out_text = items[idx];
8815     return true;
8816 }
8817
8818 static bool Items_SingleStringGetter(void* data, int idx, const char** out_text)
8819 {
8820     // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited.
8821     const char* items_separated_by_zeros = (const char*)data;
8822     int items_count = 0;
8823     const char* p = items_separated_by_zeros;
8824     while (*p)
8825     {
8826         if (idx == items_count)
8827             break;
8828         p += strlen(p) + 1;
8829         items_count++;
8830     }
8831     if (!*p)
8832         return false;
8833     if (out_text)
8834         *out_text = p;
8835     return true;
8836 }
8837
8838 // Combo box helper allowing to pass an array of strings.
8839 bool ImGui::Combo(const char* label, int* current_item, const char* const* items, int items_count, int height_in_items)
8840 {
8841     const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items);
8842     return value_changed;
8843 }
8844
8845 // Combo box helper allowing to pass all items in a single string.
8846 bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items)
8847 {
8848     int items_count = 0;
8849     const char* p = items_separated_by_zeros;       // FIXME-OPT: Avoid computing this, or at least only when combo is open
8850     while (*p)
8851     {
8852         p += strlen(p) + 1;
8853         items_count++;
8854     }
8855     bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items);
8856     return value_changed;
8857 }
8858
8859 bool ImGui::BeginCombo(const char* label, const char* preview_value, ImVec2 popup_size)
8860 {
8861     ImGuiWindow* window = GetCurrentWindow();
8862     if (window->SkipItems)
8863         return false;
8864
8865     ImGuiContext& g = *GImGui;
8866     const ImGuiStyle& style = g.Style;
8867     const ImGuiID id = window->GetID(label);
8868     const float w = CalcItemWidth();
8869
8870     const ImVec2 label_size = CalcTextSize(label, NULL, true);
8871     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
8872     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));
8873     ItemSize(total_bb, style.FramePadding.y);
8874     if (!ItemAdd(total_bb, id))
8875         return false;
8876
8877     const float arrow_size = SmallSquareSize();
8878
8879     bool hovered, held;
8880     bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held);
8881
8882     bool popup_open = IsPopupOpen(id);
8883
8884     const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f));
8885     RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
8886     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
8887     RenderTriangle(ImVec2(frame_bb.Max.x - arrow_size + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), ImGuiDir_Down);
8888
8889     if (preview_value != NULL)
8890         RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, preview_value, NULL, NULL, ImVec2(0.0f,0.0f));
8891
8892     if (label_size.x > 0)
8893         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
8894
8895     if (pressed && !popup_open)
8896     {
8897         OpenPopupEx(id, false);
8898         popup_open = true;
8899     }
8900
8901     if (!popup_open)
8902         return false;
8903
8904     if (popup_size.x == 0.0f)
8905         popup_size.x = w;
8906
8907     float popup_y1 = frame_bb.Max.y;
8908     float popup_y2 = ImClamp(popup_y1 + popup_size.y, popup_y1, g.IO.DisplaySize.y - style.DisplaySafeAreaPadding.y);
8909     if ((popup_y2 - popup_y1) < ImMin(popup_size.y, frame_bb.Min.y - style.DisplaySafeAreaPadding.y))
8910     {
8911         // 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)
8912         popup_y1 = ImClamp(frame_bb.Min.y - popup_size.y, style.DisplaySafeAreaPadding.y, frame_bb.Min.y);
8913         popup_y2 = frame_bb.Min.y;
8914         SetNextWindowPos(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FrameBorderSize), ImGuiCond_Always, ImVec2(0.0f, 1.0f));
8915     }
8916     else
8917     {
8918         // Position our combo below
8919         SetNextWindowPos(ImVec2(frame_bb.Min.x, frame_bb.Max.y - style.FrameBorderSize), ImGuiCond_Always, ImVec2(0.0f, 0.0f));
8920     }
8921     SetNextWindowSize(ImVec2(popup_size.x, popup_y2 - popup_y1), ImGuiCond_Appearing);
8922     PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
8923
8924     if (!BeginPopupEx(id, ImGuiWindowFlags_ComboBox))
8925     {
8926         IM_ASSERT(0);   // This should never happen as we tested for IsPopupOpen() above
8927         return false;
8928     }
8929     Spacing();
8930
8931     return true;
8932 }
8933
8934 void ImGui::EndCombo()
8935 {
8936     EndPopup();
8937     PopStyleVar();
8938 }
8939
8940 // Combo box function.
8941 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)
8942 {
8943     ImGuiContext& g = *GImGui;
8944     const ImGuiStyle& style = g.Style;
8945
8946     const char* preview_text = NULL;
8947     if (*current_item >= 0 && *current_item < items_count)
8948         items_getter(data, *current_item, &preview_text);
8949
8950     // Size default to hold ~7 items
8951     if (height_in_items < 0)
8952         height_in_items = 7;
8953     float popup_height = (g.FontSize + style.ItemSpacing.y) * ImMin(items_count, height_in_items) + (style.FramePadding.y * 3);
8954
8955     if (!BeginCombo(label, preview_text, ImVec2(0.0f, popup_height)))
8956         return false;
8957
8958     // Display items
8959     // FIXME-OPT: Use clipper
8960     bool value_changed = false;
8961     for (int i = 0; i < items_count; i++)
8962     {
8963         PushID((void*)(intptr_t)i);
8964         const bool item_selected = (i == *current_item);
8965         const char* item_text;
8966         if (!items_getter(data, i, &item_text))
8967             item_text = "*Unknown item*";
8968         if (Selectable(item_text, item_selected))
8969         {
8970             value_changed = true;
8971             *current_item = i;
8972         }
8973         if (item_selected && IsWindowAppearing())
8974             SetScrollHere();
8975         PopID();
8976     }
8977
8978     EndCombo();
8979     return value_changed;
8980 }
8981
8982 // Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image.
8983 // But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID.
8984 bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
8985 {
8986     ImGuiWindow* window = GetCurrentWindow();
8987     if (window->SkipItems)
8988         return false;
8989
8990     ImGuiContext& g = *GImGui;
8991     const ImGuiStyle& style = g.Style;
8992
8993     if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1) // FIXME-OPT: Avoid if vertically clipped.
8994         PopClipRect();
8995
8996     ImGuiID id = window->GetID(label);
8997     ImVec2 label_size = CalcTextSize(label, NULL, true);
8998     ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y);
8999     ImVec2 pos = window->DC.CursorPos;
9000     pos.y += window->DC.CurrentLineTextBaseOffset;
9001     ImRect bb(pos, pos + size);
9002     ItemSize(bb);
9003
9004     // Fill horizontal space.
9005     ImVec2 window_padding = window->WindowPadding;
9006     float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? GetWindowContentRegionMax().x : GetContentRegionMax().x;
9007     float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - window->DC.CursorPos.x);
9008     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);
9009     ImRect bb_with_spacing(pos, pos + size_draw);
9010     if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth))
9011         bb_with_spacing.Max.x += window_padding.x;
9012
9013     // Selectables are tightly packed together, we extend the box to cover spacing between selectable.
9014     float spacing_L = (float)(int)(style.ItemSpacing.x * 0.5f);
9015     float spacing_U = (float)(int)(style.ItemSpacing.y * 0.5f);
9016     float spacing_R = style.ItemSpacing.x - spacing_L;
9017     float spacing_D = style.ItemSpacing.y - spacing_U;
9018     bb_with_spacing.Min.x -= spacing_L;
9019     bb_with_spacing.Min.y -= spacing_U;
9020     bb_with_spacing.Max.x += spacing_R;
9021     bb_with_spacing.Max.y += spacing_D;
9022     if (!ItemAdd(bb_with_spacing, id))
9023     {
9024         if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1)
9025             PushColumnClipRect();
9026         return false;
9027     }
9028
9029     ImGuiButtonFlags button_flags = 0;
9030     if (flags & ImGuiSelectableFlags_Menu) button_flags |= ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_NoHoldingActiveID;
9031     if (flags & ImGuiSelectableFlags_MenuItem) button_flags |= ImGuiButtonFlags_PressedOnRelease;
9032     if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled;
9033     if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick;
9034     bool hovered, held;
9035     bool pressed = ButtonBehavior(bb_with_spacing, id, &hovered, &held, button_flags);
9036     if (flags & ImGuiSelectableFlags_Disabled)
9037         selected = false;
9038
9039     // Render
9040     if (hovered || selected)
9041     {
9042         const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
9043         RenderFrame(bb_with_spacing.Min, bb_with_spacing.Max, col, false, 0.0f);
9044     }
9045
9046     if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1)
9047     {
9048         PushColumnClipRect();
9049         bb_with_spacing.Max.x -= (GetContentRegionMax().x - max_x);
9050     }
9051
9052     if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
9053     RenderTextClipped(bb.Min, bb_with_spacing.Max, label, NULL, &label_size, ImVec2(0.0f,0.0f));
9054     if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor();
9055
9056     // Automatically close popups
9057     if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(window->DC.ItemFlags & ImGuiItemFlags_SelectableDontClosePopup))
9058         CloseCurrentPopup();
9059     return pressed;
9060 }
9061
9062 bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
9063 {
9064     if (Selectable(label, *p_selected, flags, size_arg))
9065     {
9066         *p_selected = !*p_selected;
9067         return true;
9068     }
9069     return false;
9070 }
9071
9072 // Helper to calculate the size of a listbox and display a label on the right.
9073 // Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an empty label "##empty"
9074 bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg)
9075 {
9076     ImGuiWindow* window = GetCurrentWindow();
9077     if (window->SkipItems)
9078         return false;
9079
9080     const ImGuiStyle& style = GetStyle();
9081     const ImGuiID id = GetID(label);
9082     const ImVec2 label_size = CalcTextSize(label, NULL, true);
9083
9084     // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
9085     ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y);
9086     ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y));
9087     ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
9088     ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
9089     window->DC.LastItemRect = bb;
9090
9091     BeginGroup();
9092     if (label_size.x > 0)
9093         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
9094
9095     BeginChildFrame(id, frame_bb.GetSize());
9096     return true;
9097 }
9098
9099 bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items)
9100 {
9101     // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
9102     // 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.
9103     // I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution.
9104     if (height_in_items < 0)
9105         height_in_items = ImMin(items_count, 7);
9106     float height_in_items_f = height_in_items < items_count ? (height_in_items + 0.40f) : (height_in_items + 0.00f);
9107
9108     // 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().
9109     ImVec2 size;
9110     size.x = 0.0f;
9111     size.y = GetTextLineHeightWithSpacing() * height_in_items_f + GetStyle().ItemSpacing.y;
9112     return ListBoxHeader(label, size);
9113 }
9114
9115 void ImGui::ListBoxFooter()
9116 {
9117     ImGuiWindow* parent_window = GetParentWindow();
9118     const ImRect bb = parent_window->DC.LastItemRect;
9119     const ImGuiStyle& style = GetStyle();
9120
9121     EndChildFrame();
9122
9123     // Redeclare item size so that it includes the label (we have stored the full size in LastItemRect)
9124     // We call SameLine() to restore DC.CurrentLine* data
9125     SameLine();
9126     parent_window->DC.CursorPos = bb.Min;
9127     ItemSize(bb, style.FramePadding.y);
9128     EndGroup();
9129 }
9130
9131 bool ImGui::ListBox(const char* label, int* current_item, const char* const* items, int items_count, int height_items)
9132 {
9133     const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items);
9134     return value_changed;
9135 }
9136
9137 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)
9138 {
9139     if (!ListBoxHeader(label, items_count, height_in_items))
9140         return false;
9141
9142     // 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.
9143     bool value_changed = false;
9144     ImGuiListClipper clipper(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to.
9145     while (clipper.Step())
9146         for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
9147         {
9148             const bool item_selected = (i == *current_item);
9149             const char* item_text;
9150             if (!items_getter(data, i, &item_text))
9151                 item_text = "*Unknown item*";
9152
9153             PushID(i);
9154             if (Selectable(item_text, item_selected))
9155             {
9156                 *current_item = i;
9157                 value_changed = true;
9158             }
9159             PopID();
9160         }
9161     ListBoxFooter();
9162     return value_changed;
9163 }
9164
9165 bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled)
9166 {
9167     ImGuiWindow* window = GetCurrentWindow();
9168     if (window->SkipItems)
9169         return false;
9170
9171     ImGuiContext& g = *GImGui;
9172     ImGuiStyle& style = g.Style;
9173     ImVec2 pos = window->DC.CursorPos;
9174     ImVec2 label_size = CalcTextSize(label, NULL, true);
9175
9176     ImGuiSelectableFlags flags = ImGuiSelectableFlags_MenuItem | (enabled ? 0 : ImGuiSelectableFlags_Disabled);
9177     bool pressed;
9178     if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
9179     {
9180         // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful
9181         // Note that in this situation we render neither the shortcut neither the selected tick mark
9182         float w = label_size.x;
9183         window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
9184         PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f);
9185         pressed = Selectable(label, false, flags, ImVec2(w, 0.0f));
9186         PopStyleVar();
9187         window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
9188     }
9189     else
9190     {
9191         ImVec2 shortcut_size = shortcut ? CalcTextSize(shortcut, NULL) : ImVec2(0.0f, 0.0f);
9192         float w = window->MenuColumns.DeclColumns(label_size.x, shortcut_size.x, (float)(int)(g.FontSize * 1.20f)); // Feedback for next frame
9193         float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
9194         pressed = Selectable(label, false, flags | ImGuiSelectableFlags_DrawFillAvailWidth, ImVec2(w, 0.0f));
9195         if (shortcut_size.x > 0.0f)
9196         {
9197             PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
9198             RenderText(pos + ImVec2(window->MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false);
9199             PopStyleColor();
9200         }
9201         if (selected)
9202             RenderCheckMark(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize  * 0.866f);
9203     }
9204     return pressed;
9205 }
9206
9207 bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled)
9208 {
9209     if (MenuItem(label, shortcut, p_selected ? *p_selected : false, enabled))
9210     {
9211         if (p_selected)
9212             *p_selected = !*p_selected;
9213         return true;
9214     }
9215     return false;
9216 }
9217
9218 bool ImGui::BeginMainMenuBar()
9219 {
9220     ImGuiContext& g = *GImGui;
9221     SetNextWindowPos(ImVec2(0.0f, 0.0f));
9222     SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.FontBaseSize + g.Style.FramePadding.y * 2.0f));
9223     PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
9224     PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0));
9225     if (!Begin("##MainMenuBar", NULL, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_MenuBar)
9226         || !BeginMenuBar())
9227     {
9228         End();
9229         PopStyleVar(2);
9230         return false;
9231     }
9232     g.CurrentWindow->DC.MenuBarOffsetX += g.Style.DisplaySafeAreaPadding.x;
9233     return true;
9234 }
9235
9236 void ImGui::EndMainMenuBar()
9237 {
9238     EndMenuBar();
9239     End();
9240     PopStyleVar(2);
9241 }
9242
9243 bool ImGui::BeginMenuBar()
9244 {
9245     ImGuiWindow* window = GetCurrentWindow();
9246     if (window->SkipItems)
9247         return false;
9248     if (!(window->Flags & ImGuiWindowFlags_MenuBar))
9249         return false;
9250
9251     IM_ASSERT(!window->DC.MenuBarAppending);
9252     BeginGroup(); // Save position
9253     PushID("##menubar");
9254     
9255     // We don't clip with regular window clipping rectangle as it is already set to the area below. However we clip with window full rect.
9256     // We remove 1 worth of rounding to Max.x to that text in long menus don't tend to display over the lower-right rounded area, which looks particularly glitchy.
9257     ImRect bar_rect = window->MenuBarRect();
9258     ImRect clip_rect(ImFloor(bar_rect.Min.x + 0.5f), ImFloor(bar_rect.Min.y + window->WindowBorderSize + 0.5f), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - window->WindowRounding) + 0.5f), ImFloor(bar_rect.Max.y + 0.5f));
9259     clip_rect.ClipWith(window->Rect());
9260     PushClipRect(clip_rect.Min, clip_rect.Max, false);
9261
9262     window->DC.CursorPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffsetX, bar_rect.Min.y);// + g.Style.FramePadding.y);
9263     window->DC.LayoutType = ImGuiLayoutType_Horizontal;
9264     window->DC.MenuBarAppending = true;
9265     AlignTextToFramePadding();
9266     return true;
9267 }
9268
9269 void ImGui::EndMenuBar()
9270 {
9271     ImGuiWindow* window = GetCurrentWindow();
9272     if (window->SkipItems)
9273         return;
9274
9275     IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar);
9276     IM_ASSERT(window->DC.MenuBarAppending);
9277     PopClipRect();
9278     PopID();
9279     window->DC.MenuBarOffsetX = window->DC.CursorPos.x - window->MenuBarRect().Min.x;
9280     window->DC.GroupStack.back().AdvanceCursor = false;
9281     EndGroup();
9282     window->DC.LayoutType = ImGuiLayoutType_Vertical;
9283     window->DC.MenuBarAppending = false;
9284 }
9285
9286 bool ImGui::BeginMenu(const char* label, bool enabled)
9287 {
9288     ImGuiWindow* window = GetCurrentWindow();
9289     if (window->SkipItems)
9290         return false;
9291
9292     ImGuiContext& g = *GImGui;
9293     const ImGuiStyle& style = g.Style;
9294     const ImGuiID id = window->GetID(label);
9295
9296     ImVec2 label_size = CalcTextSize(label, NULL, true);
9297
9298     bool pressed;
9299     bool menu_is_open = IsPopupOpen(id);
9300     bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentMenuSet == window->GetID("##menus"));
9301     ImGuiWindow* backed_nav_window = g.NavWindow;
9302     if (menuset_is_open)
9303         g.NavWindow = window;  // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent)
9304
9305     // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu (using FindBestPopupWindowPos).
9306     ImVec2 popup_pos, pos = window->DC.CursorPos;
9307     if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
9308     {
9309         // Menu inside an horizontal menu bar
9310         // Selectable extend their highlight by half ItemSpacing in each direction.
9311         // For ChildMenu, the popup position will be overwritten by the call to FindBestPopupWindowPos() in Begin()
9312         popup_pos = ImVec2(pos.x - window->WindowPadding.x, pos.y - style.FramePadding.y + window->MenuBarHeight());
9313         window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
9314         PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f);
9315         float w = label_size.x;
9316         pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
9317         PopStyleVar();
9318         window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
9319     }
9320     else
9321     {
9322         // Menu inside a menu
9323         popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
9324         float w = window->MenuColumns.DeclColumns(label_size.x, 0.0f, (float)(int)(g.FontSize * 1.20f)); // Feedback to next frame
9325         float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
9326         pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
9327         if (!enabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
9328         RenderTriangle(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), ImGuiDir_Right);
9329         if (!enabled) PopStyleColor();
9330     }
9331
9332     const bool hovered = enabled && ItemHoverable(window->DC.LastItemRect, id);
9333     if (menuset_is_open)
9334         g.NavWindow = backed_nav_window;
9335
9336     bool want_open = false, want_close = false;
9337     if (window->DC.LayoutType != ImGuiLayoutType_Horizontal) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
9338     {
9339         // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.
9340         bool moving_within_opened_triangle = false;
9341         if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentWindow == window)
9342         {
9343             if (ImGuiWindow* next_window = g.OpenPopupStack[g.CurrentPopupStack.Size].Window)
9344             {
9345                 ImRect next_window_rect = next_window->Rect();
9346                 ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta;
9347                 ImVec2 tb = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR();
9348                 ImVec2 tc = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR();
9349                 float extra = ImClamp(fabsf(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack.
9350                 ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f;   // to avoid numerical issues
9351                 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?
9352                 tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f);
9353                 moving_within_opened_triangle = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos);
9354                 //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
9355             }
9356         }
9357
9358         want_close = (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_within_opened_triangle);
9359         want_open = (!menu_is_open && hovered && !moving_within_opened_triangle) || (!menu_is_open && hovered && pressed);
9360     }
9361     else
9362     {
9363         // Menu bar
9364         if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it
9365         {
9366             want_close = true;
9367             want_open = menu_is_open = false;
9368         }
9369         else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others
9370         {
9371             want_open = true;
9372         }
9373     }
9374
9375     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.. }'
9376         want_close = true;
9377     if (want_close && IsPopupOpen(id))
9378         ClosePopupToLevel(GImGui->CurrentPopupStack.Size);
9379
9380     if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.CurrentPopupStack.Size)
9381     {
9382         // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame.
9383         OpenPopup(label);
9384         return false;
9385     }
9386
9387     menu_is_open |= want_open;
9388     if (want_open)
9389         OpenPopup(label);
9390
9391     if (menu_is_open)
9392     {
9393         SetNextWindowPos(popup_pos, ImGuiCond_Always);
9394         ImGuiWindowFlags flags = ImGuiWindowFlags_AlwaysAutoResize | ((window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu|ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu);
9395         menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
9396     }
9397
9398     return menu_is_open;
9399 }
9400
9401 void ImGui::EndMenu()
9402 {
9403     EndPopup();
9404 }
9405
9406 // Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
9407 void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags)
9408 {
9409     ImGuiContext& g = *GImGui;
9410
9411     int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]);
9412     BeginTooltipEx(0, true);
9413     
9414     const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text;
9415     if (text_end > text)
9416     {
9417         TextUnformatted(text, text_end);
9418         Separator();
9419     }
9420
9421     ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2);
9422     ColorButton("##preview", ImVec4(col[0], col[1], col[2], col[3]), (flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz);
9423     SameLine();
9424     if (flags & ImGuiColorEditFlags_NoAlpha)
9425         Text("#%02X%02X%02X\nR: %d, G: %d, B: %d\n(%.3f, %.3f, %.3f)", cr, cg, cb, cr, cg, cb, col[0], col[1], col[2]);
9426     else
9427         Text("#%02X%02X%02X%02X\nR:%d, G:%d, B:%d, A:%d\n(%.3f, %.3f, %.3f, %.3f)", cr, cg, cb, ca, cr, cg, cb, ca, col[0], col[1], col[2], col[3]);
9428     EndTooltip();
9429 }
9430
9431 static inline ImU32 ImAlphaBlendColor(ImU32 col_a, ImU32 col_b)
9432 {
9433     float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;
9434     int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);
9435     int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);
9436     int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);
9437     return IM_COL32(r, g, b, 0xFF);
9438 }
9439
9440 // NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that.
9441 // I spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding alltogether.
9442 void ImGui::RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU32 col, float grid_step, ImVec2 grid_off, float rounding, int rounding_corners_flags)
9443 {
9444     ImGuiWindow* window = GetCurrentWindow();
9445     if (((col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) < 0xFF)
9446     {
9447         ImU32 col_bg1 = GetColorU32(ImAlphaBlendColor(IM_COL32(204,204,204,255), col));
9448         ImU32 col_bg2 = GetColorU32(ImAlphaBlendColor(IM_COL32(128,128,128,255), col));
9449         window->DrawList->AddRectFilled(p_min, p_max, col_bg1, rounding, rounding_corners_flags);
9450
9451         int yi = 0;
9452         for (float y = p_min.y + grid_off.y; y < p_max.y; y += grid_step, yi++)
9453         {
9454             float y1 = ImClamp(y, p_min.y, p_max.y), y2 = ImMin(y + grid_step, p_max.y);
9455             if (y2 <= y1)
9456                 continue;
9457             for (float x = p_min.x + grid_off.x + (yi & 1) * grid_step; x < p_max.x; x += grid_step * 2.0f)
9458             {
9459                 float x1 = ImClamp(x, p_min.x, p_max.x), x2 = ImMin(x + grid_step, p_max.x);
9460                 if (x2 <= x1)
9461                     continue;
9462                 int rounding_corners_flags_cell = 0;
9463                 if (y1 <= p_min.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopRight; }
9464                 if (y2 >= p_max.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotRight; }
9465                 rounding_corners_flags_cell &= rounding_corners_flags;
9466                 window->DrawList->AddRectFilled(ImVec2(x1,y1), ImVec2(x2,y2), col_bg2, rounding_corners_flags_cell ? rounding : 0.0f, rounding_corners_flags_cell);
9467             }
9468         }
9469     }
9470     else
9471     {
9472         window->DrawList->AddRectFilled(p_min, p_max, col, rounding, rounding_corners_flags);
9473     }
9474 }
9475
9476 void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags)
9477 {
9478     ImGuiContext& g = *GImGui;
9479     if ((flags & ImGuiColorEditFlags__InputsMask) == 0)
9480         flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__InputsMask;
9481     if ((flags & ImGuiColorEditFlags__DataTypeMask) == 0)
9482         flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__DataTypeMask;
9483     if ((flags & ImGuiColorEditFlags__PickerMask) == 0)
9484         flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__PickerMask;
9485     IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__InputsMask)));   // Check only 1 option is selected
9486     IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__DataTypeMask))); // Check only 1 option is selected
9487     IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask)));   // Check only 1 option is selected
9488     g.ColorEditOptions = flags;
9489 }
9490
9491 // A little colored square. Return true when clicked.
9492 // FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip.
9493 // 'desc_id' is not called 'label' because we don't display it next to the button, but only in the tooltip.
9494 bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, ImVec2 size)
9495 {
9496     ImGuiWindow* window = GetCurrentWindow();
9497     if (window->SkipItems)
9498         return false;
9499
9500     ImGuiContext& g = *GImGui;
9501     const ImGuiID id = window->GetID(desc_id);
9502     float default_size = SmallSquareSize();
9503     if (size.x == 0.0f)
9504         size.x = default_size;
9505     if (size.y == 0.0f)
9506         size.y = default_size;
9507     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
9508     ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f);
9509     if (!ItemAdd(bb, id))
9510         return false;
9511
9512     bool hovered, held;
9513     bool pressed = ButtonBehavior(bb, id, &hovered, &held);
9514
9515     if (flags & ImGuiColorEditFlags_NoAlpha)
9516         flags &= ~(ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf);
9517     
9518     ImVec4 col_without_alpha(col.x, col.y, col.z, 1.0f);
9519     float grid_step = ImMin(size.x, size.y) / 2.99f;
9520     float rounding = ImMin(g.Style.FrameRounding, grid_step * 0.5f);
9521     ImRect bb_inner = bb;
9522     float off = -0.75f; // The border (using Col_FrameBg) tends to look off when color is near-opaque and rounding is enabled. This offset seemed like a good middle ground to reduce those artifacts.
9523     bb_inner.Expand(off);
9524     if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col.w < 1.0f)
9525     {
9526         float mid_x = (float)(int)((bb_inner.Min.x + bb_inner.Max.x) * 0.5f + 0.5f);
9527         RenderColorRectWithAlphaCheckerboard(ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawCornerFlags_TopRight| ImDrawCornerFlags_BotRight);
9528         window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_without_alpha), rounding, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotLeft);
9529     }
9530     else
9531     {
9532         // Because GetColorU32() multiplies by the global style Alpha and we don't want to display a checkerboard if the source code had no alpha
9533         ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaPreview) ? col : col_without_alpha;
9534         if (col_source.w < 1.0f)
9535             RenderColorRectWithAlphaCheckerboard(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding);
9536         else
9537             window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding, ImDrawCornerFlags_All);
9538     }
9539     if (g.Style.FrameBorderSize > 0.0f)
9540         RenderFrameBorder(bb.Min, bb.Max, rounding);
9541     else
9542         window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color button are often in need of some sort of border
9543
9544     if (hovered && !(flags & ImGuiColorEditFlags_NoTooltip))
9545         ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf));
9546
9547     return pressed;
9548 }
9549
9550 bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags)
9551 {
9552     return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha);
9553 }
9554
9555 void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags)
9556 {
9557     bool allow_opt_inputs = !(flags & ImGuiColorEditFlags__InputsMask);
9558     bool allow_opt_datatype = !(flags & ImGuiColorEditFlags__DataTypeMask);
9559     if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context"))
9560         return;
9561     ImGuiContext& g = *GImGui;
9562     ImGuiColorEditFlags opts = g.ColorEditOptions;
9563     if (allow_opt_inputs)
9564     {
9565         if (RadioButton("RGB", (opts & ImGuiColorEditFlags_RGB) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_RGB;
9566         if (RadioButton("HSV", (opts & ImGuiColorEditFlags_HSV) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HSV;
9567         if (RadioButton("HEX", (opts & ImGuiColorEditFlags_HEX) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HEX;
9568     }
9569     if (allow_opt_datatype)
9570     {
9571         if (allow_opt_inputs) Separator();
9572         if (RadioButton("0..255",     (opts & ImGuiColorEditFlags_Uint8) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Uint8;
9573         if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Float;
9574     }
9575
9576     if (allow_opt_inputs || allow_opt_datatype)
9577         Separator();
9578     if (Button("Copy as..", ImVec2(-1,0)))
9579         OpenPopup("Copy");
9580     if (BeginPopup("Copy"))
9581     {
9582         int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]);
9583         char buf[64];
9584         ImFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff, %.3ff)", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);
9585         if (Selectable(buf))
9586             SetClipboardText(buf);
9587         ImFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d,%d)", cr, cg, cb, ca);
9588         if (Selectable(buf))
9589             SetClipboardText(buf);
9590         if (flags & ImGuiColorEditFlags_NoAlpha)
9591             ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X", cr, cg, cb);
9592         else
9593             ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X%02X", cr, cg, cb, ca);
9594         if (Selectable(buf))
9595             SetClipboardText(buf);
9596         EndPopup();
9597     }
9598
9599     g.ColorEditOptions = opts;
9600     EndPopup();
9601 }
9602
9603 static void ColorPickerOptionsPopup(ImGuiColorEditFlags flags, float* ref_col)
9604 {
9605     bool allow_opt_picker = !(flags & ImGuiColorEditFlags__PickerMask);
9606     bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar);
9607     if ((!allow_opt_picker && !allow_opt_alpha_bar) || !ImGui::BeginPopup("context"))
9608         return;
9609     ImGuiContext& g = *GImGui;
9610     if (allow_opt_picker)
9611     {
9612         ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (SmallSquareSize() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function
9613         ImGui::PushItemWidth(picker_size.x);
9614         for (int picker_type = 0; picker_type < 2; picker_type++)
9615         {
9616             // Draw small/thumbnail version of each picker type (over an invisible button for selection)
9617             if (picker_type > 0) ImGui::Separator();
9618             ImGui::PushID(picker_type);
9619             ImGuiColorEditFlags picker_flags = ImGuiColorEditFlags_NoInputs|ImGuiColorEditFlags_NoOptions|ImGuiColorEditFlags_NoLabel|ImGuiColorEditFlags_NoSidePreview|(flags & ImGuiColorEditFlags_NoAlpha);
9620             if (picker_type == 0) picker_flags |= ImGuiColorEditFlags_PickerHueBar;
9621             if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel;
9622             ImVec2 backup_pos = ImGui::GetCursorScreenPos();
9623             if (ImGui::Selectable("##selectable", false, 0, picker_size)) // By default, Selectable() is closing popup
9624                 g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags__PickerMask) | (picker_flags & ImGuiColorEditFlags__PickerMask);
9625             ImGui::SetCursorScreenPos(backup_pos);
9626             ImVec4 dummy_ref_col;
9627             memcpy(&dummy_ref_col.x, ref_col, sizeof(float) * (picker_flags & ImGuiColorEditFlags_NoAlpha ? 3 : 4));
9628             ImGui::ColorPicker4("##dummypicker", &dummy_ref_col.x, picker_flags);
9629             ImGui::PopID();
9630         }
9631         ImGui::PopItemWidth();
9632     }
9633     if (allow_opt_alpha_bar)
9634     {
9635         if (allow_opt_picker) ImGui::Separator();
9636         ImGui::CheckboxFlags("Alpha Bar", (unsigned int*)&g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar);
9637     }
9638     ImGui::EndPopup();
9639 }
9640
9641 // Edit colors components (each component in 0.0f..1.0f range). 
9642 // See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
9643 // With typical options: Left-click on colored square to open color picker. Right-click to open option menu. CTRL-Click over input fields to edit them and TAB to go to next item.
9644 bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags)
9645 {
9646     ImGuiWindow* window = GetCurrentWindow();
9647     if (window->SkipItems)
9648         return false;
9649
9650     ImGuiContext& g = *GImGui;
9651     const ImGuiStyle& style = g.Style;
9652     const float square_sz = SmallSquareSize();
9653     const float w_extra = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x);
9654     const float w_items_all = CalcItemWidth() - w_extra;
9655     const char* label_display_end = FindRenderedTextEnd(label);
9656
9657     const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0;
9658     const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0;
9659     const int components = alpha ? 4 : 3;
9660     const ImGuiColorEditFlags flags_untouched = flags;
9661
9662     BeginGroup();
9663     PushID(label);
9664
9665     // If we're not showing any slider there's no point in doing any HSV conversions
9666     if (flags & ImGuiColorEditFlags_NoInputs)
9667         flags = (flags & (~ImGuiColorEditFlags__InputsMask)) | ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_NoOptions;
9668
9669     // Context menu: display and modify options (before defaults are applied)
9670     if (!(flags & ImGuiColorEditFlags_NoOptions))
9671         ColorEditOptionsPopup(col, flags);
9672  
9673     // Read stored options
9674     if (!(flags & ImGuiColorEditFlags__InputsMask))
9675         flags |= (g.ColorEditOptions & ImGuiColorEditFlags__InputsMask);
9676     if (!(flags & ImGuiColorEditFlags__DataTypeMask))
9677         flags |= (g.ColorEditOptions & ImGuiColorEditFlags__DataTypeMask);
9678     if (!(flags & ImGuiColorEditFlags__PickerMask))
9679         flags |= (g.ColorEditOptions & ImGuiColorEditFlags__PickerMask);
9680     flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask));
9681
9682     // Convert to the formats we need
9683     float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f };
9684     if (flags & ImGuiColorEditFlags_HSV)
9685         ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]);
9686     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]) };
9687
9688     bool value_changed = false;
9689     bool value_changed_as_float = false;
9690
9691     if ((flags & (ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_HSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)
9692     {
9693         // RGB/HSV 0..255 Sliders
9694         const float w_item_one  = ImMax(1.0f, (float)(int)((w_items_all - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
9695         const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
9696
9697         const bool hide_prefix = (w_item_one <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x);
9698         const char* ids[4] = { "##X", "##Y", "##Z", "##W" };
9699         const char* fmt_table_int[3][4] =
9700         {
9701             {   "%3.0f",   "%3.0f",   "%3.0f",   "%3.0f" }, // Short display
9702             { "R:%3.0f", "G:%3.0f", "B:%3.0f", "A:%3.0f" }, // Long display for RGBA
9703             { "H:%3.0f", "S:%3.0f", "V:%3.0f", "A:%3.0f" }  // Long display for HSVA
9704         };
9705         const char* fmt_table_float[3][4] =
9706         {
9707             {   "%0.3f",   "%0.3f",   "%0.3f",   "%0.3f" }, // Short display
9708             { "R:%0.3f", "G:%0.3f", "B:%0.3f", "A:%0.3f" }, // Long display for RGBA
9709             { "H:%0.3f", "S:%0.3f", "V:%0.3f", "A:%0.3f" }  // Long display for HSVA
9710         };
9711         const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_HSV) ? 2 : 1;
9712
9713         PushItemWidth(w_item_one);
9714         for (int n = 0; n < components; n++)
9715         {
9716             if (n > 0)
9717                 SameLine(0, style.ItemInnerSpacing.x);
9718             if (n + 1 == components)
9719                 PushItemWidth(w_item_last);
9720             if (flags & ImGuiColorEditFlags_Float)
9721                 value_changed = value_changed_as_float = value_changed | DragFloat(ids[n], &f[n], 1.0f/255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]);
9722             else
9723                 value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]);
9724             if (!(flags & ImGuiColorEditFlags_NoOptions))
9725                 OpenPopupOnItemClick("context");
9726         }
9727         PopItemWidth();
9728         PopItemWidth();
9729     }
9730     else if ((flags & ImGuiColorEditFlags_HEX) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)
9731     {
9732         // RGB Hexadecimal Input
9733         char buf[64];
9734         if (alpha)
9735             ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", ImClamp(i[0],0,255), ImClamp(i[1],0,255), ImClamp(i[2],0,255), ImClamp(i[3],0,255));
9736         else
9737             ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", ImClamp(i[0],0,255), ImClamp(i[1],0,255), ImClamp(i[2],0,255));
9738         PushItemWidth(w_items_all);
9739         if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase))
9740         {
9741             value_changed = true;
9742             char* p = buf;
9743             while (*p == '#' || ImCharIsSpace(*p))
9744                 p++;
9745             i[0] = i[1] = i[2] = i[3] = 0;
9746             if (alpha)
9747                 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)
9748             else
9749                 sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]);
9750         }
9751         if (!(flags & ImGuiColorEditFlags_NoOptions))
9752             OpenPopupOnItemClick("context");
9753         PopItemWidth();
9754     }
9755
9756     bool picker_active = false;
9757     if (!(flags & ImGuiColorEditFlags_NoSmallPreview))
9758     {
9759         if (!(flags & ImGuiColorEditFlags_NoInputs))
9760             SameLine(0, style.ItemInnerSpacing.x);
9761
9762         const ImVec4 col_v4(col[0], col[1], col[2], alpha ? col[3] : 1.0f);
9763         if (ColorButton("##ColorButton", col_v4, flags))
9764         {
9765             if (!(flags & ImGuiColorEditFlags_NoPicker))
9766             {
9767                 // Store current color and open a picker
9768                 g.ColorPickerRef = col_v4;
9769                 OpenPopup("picker");
9770                 SetNextWindowPos(window->DC.LastItemRect.GetBL() + ImVec2(-1,style.ItemSpacing.y));
9771             }
9772         }
9773         if (!(flags & ImGuiColorEditFlags_NoOptions))
9774             OpenPopupOnItemClick("context");
9775         
9776         if (BeginPopup("picker"))
9777         {
9778             picker_active = true;
9779             if (label != label_display_end)
9780             {
9781                 TextUnformatted(label, label_display_end);
9782                 Separator();
9783             }
9784             ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar;
9785             ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf;
9786             PushItemWidth(square_sz * 12.0f); // Use 256 + bar sizes?
9787             value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x);
9788             PopItemWidth();
9789             EndPopup();
9790         }
9791     }
9792
9793     if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel))
9794     {
9795         SameLine(0, style.ItemInnerSpacing.x);
9796         TextUnformatted(label, label_display_end);
9797     }
9798
9799     // Convert back
9800     if (!picker_active)
9801     {
9802         if (!value_changed_as_float) 
9803             for (int n = 0; n < 4; n++)
9804                 f[n] = i[n] / 255.0f;
9805         if (flags & ImGuiColorEditFlags_HSV)
9806             ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]);
9807         if (value_changed)
9808         {
9809             col[0] = f[0];
9810             col[1] = f[1];
9811             col[2] = f[2];
9812             if (alpha)
9813                 col[3] = f[3];
9814         }
9815     }
9816
9817     PopID();
9818     EndGroup();
9819
9820     return value_changed;
9821 }
9822
9823 bool ImGui::ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags)
9824 {
9825     float col4[4] = { col[0], col[1], col[2], 1.0f };
9826     if (!ColorPicker4(label, col4, flags | ImGuiColorEditFlags_NoAlpha))
9827         return false;
9828     col[0] = col4[0]; col[1] = col4[1]; col[2] = col4[2];
9829     return true;
9830 }
9831
9832 // 'pos' is position of the arrow tip. half_sz.x is length from base to tip. half_sz.y is length on each side.
9833 static void RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col)
9834 {
9835     switch (direction)
9836     {
9837     case ImGuiDir_Left:  draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), pos, col); return;
9838     case ImGuiDir_Right: draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), pos, col); return;
9839     case ImGuiDir_Up:    draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), pos, col); return;
9840     case ImGuiDir_Down:  draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), pos, col); return;
9841     default: return; // Fix warning for ImGuiDir_None
9842     }
9843 }
9844
9845 static void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, float bar_w)
9846 {
9847     RenderArrow(draw_list, ImVec2(pos.x + half_sz.x + 1,         pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Right, IM_COL32_BLACK);
9848     RenderArrow(draw_list, ImVec2(pos.x + half_sz.x,             pos.y), half_sz,                              ImGuiDir_Right, IM_COL32_WHITE);
9849     RenderArrow(draw_list, ImVec2(pos.x + bar_w - half_sz.x - 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Left,  IM_COL32_BLACK);
9850     RenderArrow(draw_list, ImVec2(pos.x + bar_w - half_sz.x,     pos.y), half_sz,                              ImGuiDir_Left,  IM_COL32_WHITE);
9851 }
9852
9853 // ColorPicker
9854 // Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
9855 // FIXME: we adjust the big color square height based on item width, which may cause a flickering feedback loop (if automatic height makes a vertical scrollbar appears, affecting automatic width..) 
9856 bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags, const float* ref_col)
9857 {
9858     ImGuiContext& g = *GImGui;
9859     ImGuiWindow* window = GetCurrentWindow();
9860     ImDrawList* draw_list = window->DrawList;
9861
9862     ImGuiStyle& style = g.Style;
9863     ImGuiIO& io = g.IO;
9864
9865     PushID(label);
9866     BeginGroup();
9867
9868     if (!(flags & ImGuiColorEditFlags_NoSidePreview))
9869         flags |= ImGuiColorEditFlags_NoSmallPreview;
9870
9871     // Context menu: display and store options.
9872     if (!(flags & ImGuiColorEditFlags_NoOptions))
9873         ColorPickerOptionsPopup(flags, col);
9874
9875     // Read stored options
9876     if (!(flags & ImGuiColorEditFlags__PickerMask))
9877         flags |= ((g.ColorEditOptions & ImGuiColorEditFlags__PickerMask) ? g.ColorEditOptions : ImGuiColorEditFlags__OptionsDefault) & ImGuiColorEditFlags__PickerMask; 
9878     IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask))); // Check that only 1 is selected
9879     if (!(flags & ImGuiColorEditFlags_NoOptions))
9880         flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar);
9881
9882     // Setup
9883     bool alpha_bar = (flags & ImGuiColorEditFlags_AlphaBar) && !(flags & ImGuiColorEditFlags_NoAlpha);
9884     ImVec2 picker_pos = window->DC.CursorPos;
9885     float square_sz = SmallSquareSize();
9886     float bars_width = square_sz; // Arbitrary smallish width of Hue/Alpha picking bars
9887     float sv_picker_size = ImMax(bars_width * 1, CalcItemWidth() - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x)); // Saturation/Value picking box
9888     float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x;
9889     float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x;
9890     float bars_triangles_half_sz = (float)(int)(bars_width * 0.20f);
9891
9892     float wheel_thickness = sv_picker_size * 0.08f;
9893     float wheel_r_outer = sv_picker_size * 0.50f;
9894     float wheel_r_inner = wheel_r_outer - wheel_thickness;
9895     ImVec2 wheel_center(picker_pos.x + (sv_picker_size + bars_width)*0.5f, picker_pos.y + sv_picker_size*0.5f);
9896     
9897     // Note: the triangle is displayed rotated with triangle_pa pointing to Hue, but most coordinates stays unrotated for logic.
9898     float triangle_r = wheel_r_inner - (int)(sv_picker_size * 0.027f);
9899     ImVec2 triangle_pa = ImVec2(triangle_r, 0.0f); // Hue point.
9900     ImVec2 triangle_pb = ImVec2(triangle_r * -0.5f, triangle_r * -0.866025f); // Black point.
9901     ImVec2 triangle_pc = ImVec2(triangle_r * -0.5f, triangle_r * +0.866025f); // White point.
9902
9903     float H,S,V;
9904     ColorConvertRGBtoHSV(col[0], col[1], col[2], H, S, V);
9905
9906     bool value_changed = false, value_changed_h = false, value_changed_sv = false;
9907
9908     if (flags & ImGuiColorEditFlags_PickerHueWheel)
9909     {
9910         // Hue wheel + SV triangle logic
9911         InvisibleButton("hsv", ImVec2(sv_picker_size + style.ItemInnerSpacing.x + bars_width, sv_picker_size));
9912         if (IsItemActive())
9913         {
9914             ImVec2 initial_off = g.IO.MouseClickedPos[0] - wheel_center;
9915             ImVec2 current_off = g.IO.MousePos - wheel_center;
9916             float initial_dist2 = ImLengthSqr(initial_off);
9917             if (initial_dist2 >= (wheel_r_inner-1)*(wheel_r_inner-1) && initial_dist2 <= (wheel_r_outer+1)*(wheel_r_outer+1))
9918             {
9919                 // Interactive with Hue wheel
9920                 H = atan2f(current_off.y, current_off.x) / IM_PI*0.5f;
9921                 if (H < 0.0f)
9922                     H += 1.0f;
9923                 value_changed = value_changed_h = true;
9924             }
9925             float cos_hue_angle = cosf(-H * 2.0f * IM_PI);
9926             float sin_hue_angle = sinf(-H * 2.0f * IM_PI);
9927             if (ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, ImRotate(initial_off, cos_hue_angle, sin_hue_angle)))
9928             {
9929                 // Interacting with SV triangle
9930                 ImVec2 current_off_unrotated = ImRotate(current_off, cos_hue_angle, sin_hue_angle);
9931                 if (!ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated))
9932                     current_off_unrotated = ImTriangleClosestPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated);
9933                 float uu, vv, ww;
9934                 ImTriangleBarycentricCoords(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated, uu, vv, ww);
9935                 V = ImClamp(1.0f - vv, 0.0001f, 1.0f);
9936                 S = ImClamp(uu / V, 0.0001f, 1.0f);
9937                 value_changed = value_changed_sv = true;
9938             }
9939         }
9940         if (!(flags & ImGuiColorEditFlags_NoOptions))
9941             OpenPopupOnItemClick("context");
9942     }
9943     else if (flags & ImGuiColorEditFlags_PickerHueBar)
9944     {
9945         // SV rectangle logic
9946         InvisibleButton("sv", ImVec2(sv_picker_size, sv_picker_size));
9947         if (IsItemActive())
9948         {
9949             S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size-1));
9950             V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1));
9951             value_changed = value_changed_sv = true;
9952         }
9953         if (!(flags & ImGuiColorEditFlags_NoOptions))
9954             OpenPopupOnItemClick("context");
9955
9956         // Hue bar logic
9957         SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y));
9958         InvisibleButton("hue", ImVec2(bars_width, sv_picker_size));
9959         if (IsItemActive())
9960         {
9961             H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1));
9962             value_changed = value_changed_h = true;
9963         }
9964     }
9965
9966     // Alpha bar logic
9967     if (alpha_bar)
9968     {
9969         SetCursorScreenPos(ImVec2(bar1_pos_x, picker_pos.y));
9970         InvisibleButton("alpha", ImVec2(bars_width, sv_picker_size));
9971         if (IsItemActive())
9972         {
9973             col[3] = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1));
9974             value_changed = true;
9975         }
9976     }
9977
9978     if (!(flags & ImGuiColorEditFlags_NoSidePreview))
9979     {
9980         SameLine(0, style.ItemInnerSpacing.x);
9981         BeginGroup();
9982     }
9983
9984     if (!(flags & ImGuiColorEditFlags_NoLabel))
9985     {
9986         const char* label_display_end = FindRenderedTextEnd(label);
9987         if (label != label_display_end)
9988         {
9989             if ((flags & ImGuiColorEditFlags_NoSidePreview))
9990                 SameLine(0, style.ItemInnerSpacing.x);
9991             TextUnformatted(label, label_display_end);
9992         }
9993     }
9994
9995     if (!(flags & ImGuiColorEditFlags_NoSidePreview))
9996     {
9997         ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);
9998         if ((flags & ImGuiColorEditFlags_NoLabel))
9999             Text("Current");
10000         ColorButton("##current", col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2));
10001         if (ref_col != NULL)
10002         {
10003             Text("Original");
10004             ImVec4 ref_col_v4(ref_col[0], ref_col[1], ref_col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : ref_col[3]);
10005             if (ColorButton("##original", ref_col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2)))
10006             {
10007                 memcpy(col, ref_col, ((flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4) * sizeof(float));
10008                 value_changed = true;
10009             }
10010         }
10011         EndGroup();
10012     }
10013
10014     // Convert back color to RGB
10015     if (value_changed_h || value_changed_sv)
10016         ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10*1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]);
10017
10018     // R,G,B and H,S,V slider color editor
10019     if ((flags & ImGuiColorEditFlags_NoInputs) == 0)
10020     {
10021         PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x);
10022         ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf;
10023         ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker;
10024         if (flags & ImGuiColorEditFlags_RGB || (flags & ImGuiColorEditFlags__InputsMask) == 0)
10025             value_changed |= ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_RGB);
10026         if (flags & ImGuiColorEditFlags_HSV || (flags & ImGuiColorEditFlags__InputsMask) == 0)
10027             value_changed |= ColorEdit4("##hsv", col, sub_flags | ImGuiColorEditFlags_HSV);
10028         if (flags & ImGuiColorEditFlags_HEX || (flags & ImGuiColorEditFlags__InputsMask) == 0)
10029             value_changed |= ColorEdit4("##hex", col, sub_flags | ImGuiColorEditFlags_HEX);
10030         PopItemWidth();
10031     }
10032
10033     // Try to cancel hue wrap (after ColorEdit), if any
10034     if (value_changed)
10035     {
10036         float new_H, new_S, new_V;
10037         ColorConvertRGBtoHSV(col[0], col[1], col[2], new_H, new_S, new_V);
10038         if (new_H <= 0 && H > 0) 
10039         {
10040             if (new_V <= 0 && V != new_V)
10041                 ColorConvertHSVtoRGB(H, S, new_V <= 0 ? V * 0.5f : new_V, col[0], col[1], col[2]);
10042             else if (new_S <= 0)
10043                 ColorConvertHSVtoRGB(H, new_S <= 0 ? S * 0.5f : new_S, new_V, col[0], col[1], col[2]);
10044         }
10045     }
10046
10047     ImVec4 hue_color_f(1, 1, 1, 1); ColorConvertHSVtoRGB(H, 1, 1, hue_color_f.x, hue_color_f.y, hue_color_f.z);
10048     ImU32 hue_color32 = ColorConvertFloat4ToU32(hue_color_f);
10049     ImU32 col32_no_alpha = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 1.0f));
10050
10051     const ImU32 hue_colors[6+1] = { IM_COL32(255,0,0,255), IM_COL32(255,255,0,255), IM_COL32(0,255,0,255), IM_COL32(0,255,255,255), IM_COL32(0,0,255,255), IM_COL32(255,0,255,255), IM_COL32(255,0,0,255) };
10052     ImVec2 sv_cursor_pos;
10053     
10054     if (flags & ImGuiColorEditFlags_PickerHueWheel)
10055     {
10056         // Render Hue Wheel
10057         const float aeps = 1.5f / wheel_r_outer; // Half a pixel arc length in radians (2pi cancels out).
10058         const int segment_per_arc = ImMax(4, (int)wheel_r_outer / 12);
10059         for (int n = 0; n < 6; n++)
10060         {
10061             const float a0 = (n)     /6.0f * 2.0f * IM_PI - aeps;
10062             const float a1 = (n+1.0f)/6.0f * 2.0f * IM_PI + aeps;
10063             const int vert_start_idx = draw_list->VtxBuffer.Size;
10064             draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer)*0.5f, a0, a1, segment_per_arc);
10065             draw_list->PathStroke(IM_COL32_WHITE, false, wheel_thickness);
10066             const int vert_end_idx = draw_list->VtxBuffer.Size;
10067
10068             // Paint colors over existing vertices
10069             ImVec2 gradient_p0(wheel_center.x + cosf(a0) * wheel_r_inner, wheel_center.y + sinf(a0) * wheel_r_inner);
10070             ImVec2 gradient_p1(wheel_center.x + cosf(a1) * wheel_r_inner, wheel_center.y + sinf(a1) * wheel_r_inner);
10071             ShadeVertsLinearColorGradientKeepAlpha(draw_list->VtxBuffer.Data + vert_start_idx, draw_list->VtxBuffer.Data + vert_end_idx, gradient_p0, gradient_p1, hue_colors[n], hue_colors[n+1]);
10072         }
10073
10074         // Render Cursor + preview on Hue Wheel
10075         float cos_hue_angle = cosf(H * 2.0f * IM_PI);
10076         float sin_hue_angle = sinf(H * 2.0f * IM_PI);
10077         ImVec2 hue_cursor_pos(wheel_center.x + cos_hue_angle * (wheel_r_inner+wheel_r_outer)*0.5f, wheel_center.y + sin_hue_angle * (wheel_r_inner+wheel_r_outer)*0.5f);
10078         float hue_cursor_rad = value_changed_h ? wheel_thickness * 0.65f : wheel_thickness * 0.55f;
10079         int hue_cursor_segments = ImClamp((int)(hue_cursor_rad / 1.4f), 9, 32);
10080         draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments);
10081         draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad+1, IM_COL32(128,128,128,255), hue_cursor_segments);
10082         draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, IM_COL32_WHITE, hue_cursor_segments);
10083
10084         // Render SV triangle (rotated according to hue)
10085         ImVec2 tra = wheel_center + ImRotate(triangle_pa, cos_hue_angle, sin_hue_angle);
10086         ImVec2 trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle);
10087         ImVec2 trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle);
10088         ImVec2 uv_white = g.FontTexUvWhitePixel;
10089         draw_list->PrimReserve(6, 6);
10090         draw_list->PrimVtx(tra, uv_white, hue_color32);
10091         draw_list->PrimVtx(trb, uv_white, hue_color32);
10092         draw_list->PrimVtx(trc, uv_white, IM_COL32_WHITE);
10093         draw_list->PrimVtx(tra, uv_white, IM_COL32_BLACK_TRANS);
10094         draw_list->PrimVtx(trb, uv_white, IM_COL32_BLACK);
10095         draw_list->PrimVtx(trc, uv_white, IM_COL32_BLACK_TRANS);
10096         draw_list->AddTriangle(tra, trb, trc, IM_COL32(128,128,128,255), 1.5f);
10097         sv_cursor_pos = ImLerp(ImLerp(trc, tra, ImSaturate(S)), trb, ImSaturate(1 - V));
10098     }
10099     else if (flags & ImGuiColorEditFlags_PickerHueBar)
10100     {
10101         // Render SV Square
10102         draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), IM_COL32_WHITE, hue_color32, hue_color32, IM_COL32_WHITE);
10103         draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), IM_COL32_BLACK_TRANS, IM_COL32_BLACK_TRANS, IM_COL32_BLACK, IM_COL32_BLACK);
10104         RenderFrameBorder(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), 0.0f);
10105         sv_cursor_pos.x = ImClamp((float)(int)(picker_pos.x + ImSaturate(S)     * sv_picker_size + 0.5f), picker_pos.x + 2, picker_pos.x + sv_picker_size - 2); // Sneakily prevent the circle to stick out too much
10106         sv_cursor_pos.y = ImClamp((float)(int)(picker_pos.y + ImSaturate(1 - V) * sv_picker_size + 0.5f), picker_pos.y + 2, picker_pos.y + sv_picker_size - 2);
10107
10108         // Render Hue Bar
10109         for (int i = 0; i < 6; ++i)
10110             draw_list->AddRectFilledMultiColor(ImVec2(bar0_pos_x, picker_pos.y + i * (sv_picker_size / 6)), ImVec2(bar0_pos_x + bars_width, picker_pos.y + (i + 1) * (sv_picker_size / 6)), hue_colors[i], hue_colors[i], hue_colors[i + 1], hue_colors[i + 1]);
10111         float bar0_line_y = (float)(int)(picker_pos.y + H * sv_picker_size + 0.5f);
10112         RenderFrameBorder(ImVec2(bar0_pos_x, picker_pos.y), ImVec2(bar0_pos_x + bars_width, picker_pos.y + sv_picker_size), 0.0f);
10113         RenderArrowsForVerticalBar(draw_list, ImVec2(bar0_pos_x - 1, bar0_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f);
10114     }
10115
10116     // Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range)
10117     float sv_cursor_rad = value_changed_sv ? 10.0f : 6.0f;
10118     draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, col32_no_alpha, 12);
10119     draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad+1, IM_COL32(128,128,128,255), 12);
10120     draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, IM_COL32_WHITE, 12);
10121
10122     // Render alpha bar
10123     if (alpha_bar)
10124     {
10125         float alpha = ImSaturate(col[3]);
10126         ImRect bar1_bb(bar1_pos_x, picker_pos.y, bar1_pos_x + bars_width, picker_pos.y + sv_picker_size);
10127         RenderColorRectWithAlphaCheckerboard(bar1_bb.Min, bar1_bb.Max, IM_COL32(0,0,0,0), bar1_bb.GetWidth() / 2.0f, ImVec2(0.0f, 0.0f));
10128         draw_list->AddRectFilledMultiColor(bar1_bb.Min, bar1_bb.Max, col32_no_alpha, col32_no_alpha, col32_no_alpha & ~IM_COL32_A_MASK, col32_no_alpha & ~IM_COL32_A_MASK);
10129         float bar1_line_y = (float)(int)(picker_pos.y + (1.0f - alpha) * sv_picker_size + 0.5f);
10130         RenderFrameBorder(bar1_bb.Min, bar1_bb.Max, 0.0f);
10131         RenderArrowsForVerticalBar(draw_list, ImVec2(bar1_pos_x - 1, bar1_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f);
10132     }
10133
10134     EndGroup();
10135     PopID();
10136
10137     return value_changed;
10138 }
10139
10140 // Horizontal separating line.
10141 void ImGui::Separator()
10142 {
10143     ImGuiWindow* window = GetCurrentWindow();
10144     if (window->SkipItems)
10145         return;
10146     ImGuiContext& g = *GImGui;
10147
10148     ImGuiWindowFlags flags = 0;
10149     if ((flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical)) == 0)
10150         flags |= (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal;
10151     IM_ASSERT(ImIsPowerOfTwo((int)(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical))));   // Check that only 1 option is selected
10152     if (flags & ImGuiSeparatorFlags_Vertical)
10153     {
10154         VerticalSeparator();
10155         return;
10156     }
10157
10158     // Horizontal Separator
10159     if (window->DC.ColumnsCount > 1)
10160         PopClipRect();
10161
10162     float x1 = window->Pos.x;
10163     float x2 = window->Pos.x + window->Size.x;
10164     if (!window->DC.GroupStack.empty())
10165         x1 += window->DC.IndentX;
10166
10167     const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y+1.0f));
10168     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.
10169     if (!ItemAdd(bb, 0))
10170     {
10171         if (window->DC.ColumnsCount > 1)
10172             PushColumnClipRect();
10173         return;
10174     }
10175
10176     window->DrawList->AddLine(bb.Min, ImVec2(bb.Max.x,bb.Min.y), GetColorU32(ImGuiCol_Separator));
10177
10178     if (g.LogEnabled)
10179             LogRenderedText(NULL, IM_NEWLINE "--------------------------------");
10180
10181     if (window->DC.ColumnsCount > 1)
10182     {
10183         PushColumnClipRect();
10184         window->DC.ColumnsCellMinY = window->DC.CursorPos.y;
10185     }
10186 }
10187
10188 void ImGui::VerticalSeparator()
10189 {
10190     ImGuiWindow* window = GetCurrentWindow();
10191     if (window->SkipItems)
10192         return;
10193     ImGuiContext& g = *GImGui;
10194
10195     float y1 = window->DC.CursorPos.y;
10196     float y2 = window->DC.CursorPos.y + window->DC.CurrentLineHeight; 
10197     const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + 1.0f, y2));
10198     ItemSize(ImVec2(bb.GetWidth(), 0.0f));
10199     if (!ItemAdd(bb, 0))
10200         return;
10201
10202     window->DrawList->AddLine(ImVec2(bb.Min.x, bb.Min.y), ImVec2(bb.Min.x, bb.Max.y), GetColorU32(ImGuiCol_Separator));
10203     if (g.LogEnabled)
10204         LogText(" |");
10205 }
10206
10207 bool ImGui::SplitterBehavior(ImGuiID id, const ImRect& bb, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend)
10208 {
10209     ImGuiContext& g = *GImGui;
10210     ImGuiWindow* window = g.CurrentWindow;
10211
10212     const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
10213 #ifdef IMGUI_HAS_NAV
10214     window->DC.ItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus;
10215 #endif
10216     bool add = ItemAdd(bb, id);
10217     window->DC.ItemFlags = item_flags_backup;
10218     if (!add)
10219         return false;
10220
10221     bool hovered, held;
10222     ImRect bb_interact = bb;
10223     bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f));
10224     ButtonBehavior(bb_interact, id, &hovered, &held, ImGuiButtonFlags_FlattenChilds | ImGuiButtonFlags_AllowOverlapMode);
10225     if (g.ActiveId != id)
10226         SetItemAllowOverlap();
10227
10228     if (held || (g.HoveredId == id && g.HoveredIdPreviousFrame == id))
10229         SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW);
10230
10231     ImRect bb_render = bb;
10232     if (held)
10233     {
10234         ImVec2 mouse_delta_2d = g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min;
10235         float mouse_delta = (axis == ImGuiAxis_Y) ? mouse_delta_2d.y : mouse_delta_2d.x;
10236
10237         // Minimum pane size
10238         if (mouse_delta < min_size1 - *size1)
10239             mouse_delta = min_size1 - *size1;
10240         if (mouse_delta > *size2 - min_size2)
10241             mouse_delta = *size2 - min_size2;
10242
10243         // Apply resize
10244         *size1 += mouse_delta;
10245         *size2 -= mouse_delta;
10246         bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta));
10247     }
10248
10249     // Render
10250     const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
10251     RenderFrame(bb_render.Min, bb_render.Max, col, true, g.Style.FrameRounding);
10252
10253     return held;
10254 }
10255
10256 void ImGui::Spacing()
10257 {
10258     ImGuiWindow* window = GetCurrentWindow();
10259     if (window->SkipItems)
10260         return;
10261     ItemSize(ImVec2(0,0));
10262 }
10263
10264 void ImGui::Dummy(const ImVec2& size)
10265 {
10266     ImGuiWindow* window = GetCurrentWindow();
10267     if (window->SkipItems)
10268         return;
10269
10270     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
10271     ItemSize(bb);
10272     ItemAdd(bb, 0);
10273 }
10274
10275 bool ImGui::IsRectVisible(const ImVec2& size)
10276 {
10277     ImGuiWindow* window = GetCurrentWindowRead();
10278     return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
10279 }
10280
10281 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
10282 {
10283     ImGuiWindow* window = GetCurrentWindowRead();
10284     return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
10285 }
10286
10287 // 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.)
10288 void ImGui::BeginGroup()
10289 {
10290     ImGuiWindow* window = GetCurrentWindow();
10291
10292     window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
10293     ImGuiGroupData& group_data = window->DC.GroupStack.back();
10294     group_data.BackupCursorPos = window->DC.CursorPos;
10295     group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
10296     group_data.BackupIndentX = window->DC.IndentX;
10297     group_data.BackupGroupOffsetX = window->DC.GroupOffsetX;
10298     group_data.BackupCurrentLineHeight = window->DC.CurrentLineHeight;
10299     group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset;
10300     group_data.BackupLogLinePosY = window->DC.LogLinePosY;
10301     group_data.BackupActiveIdIsAlive = GImGui->ActiveIdIsAlive;
10302     group_data.AdvanceCursor = true;
10303
10304     window->DC.GroupOffsetX = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffsetX;
10305     window->DC.IndentX = window->DC.GroupOffsetX;
10306     window->DC.CursorMaxPos = window->DC.CursorPos;
10307     window->DC.CurrentLineHeight = 0.0f;
10308     window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
10309 }
10310
10311 void ImGui::EndGroup()
10312 {
10313     ImGuiContext& g = *GImGui;
10314     ImGuiWindow* window = GetCurrentWindow();
10315
10316     IM_ASSERT(!window->DC.GroupStack.empty());    // Mismatched BeginGroup()/EndGroup() calls
10317
10318     ImGuiGroupData& group_data = window->DC.GroupStack.back();
10319
10320     ImRect group_bb(group_data.BackupCursorPos, window->DC.CursorMaxPos);
10321     group_bb.Max.y -= g.Style.ItemSpacing.y;      // Cancel out last vertical spacing because we are adding one ourselves.
10322     group_bb.Max = ImMax(group_bb.Min, group_bb.Max);
10323
10324     window->DC.CursorPos = group_data.BackupCursorPos;
10325     window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
10326     window->DC.CurrentLineHeight = group_data.BackupCurrentLineHeight;
10327     window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset;
10328     window->DC.IndentX = group_data.BackupIndentX;
10329     window->DC.GroupOffsetX = group_data.BackupGroupOffsetX;
10330     window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
10331
10332     if (group_data.AdvanceCursor)
10333     {
10334         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.
10335         ItemSize(group_bb.GetSize(), group_data.BackupCurrentLineTextBaseOffset);
10336         ItemAdd(group_bb, 0);
10337     }
10338
10339     // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive() will be functional on the entire group.
10340     // 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.
10341     const bool active_id_within_group = (!group_data.BackupActiveIdIsAlive && g.ActiveIdIsAlive && g.ActiveId && g.ActiveIdWindow->RootWindow == window->RootWindow);
10342     if (active_id_within_group)
10343         window->DC.LastItemId = g.ActiveId;
10344     window->DC.LastItemRect = group_bb;
10345
10346     window->DC.GroupStack.pop_back();
10347
10348     //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255));   // [Debug]
10349 }
10350
10351 // Gets back to previous line and continue with horizontal layout
10352 //      pos_x == 0      : follow right after previous item
10353 //      pos_x != 0      : align to specified x position (relative to window/group left)
10354 //      spacing_w < 0   : use default spacing if pos_x == 0, no spacing if pos_x != 0
10355 //      spacing_w >= 0  : enforce spacing amount
10356 void ImGui::SameLine(float pos_x, float spacing_w)
10357 {
10358     ImGuiWindow* window = GetCurrentWindow();
10359     if (window->SkipItems)
10360         return;
10361
10362     ImGuiContext& g = *GImGui;
10363     if (pos_x != 0.0f)
10364     {
10365         if (spacing_w < 0.0f) spacing_w = 0.0f;
10366         window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + pos_x + spacing_w + window->DC.GroupOffsetX + window->DC.ColumnsOffsetX;
10367         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
10368     }
10369     else
10370     {
10371         if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
10372         window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
10373         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
10374     }
10375     window->DC.CurrentLineHeight = window->DC.PrevLineHeight;
10376     window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
10377 }
10378
10379 void ImGui::NewLine()
10380 {
10381     ImGuiWindow* window = GetCurrentWindow();
10382     if (window->SkipItems)
10383         return;
10384
10385     ImGuiContext& g = *GImGui;
10386     const ImGuiLayoutType backup_layout_type = window->DC.LayoutType;
10387     window->DC.LayoutType = ImGuiLayoutType_Vertical;
10388     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.
10389         ItemSize(ImVec2(0,0));
10390     else
10391         ItemSize(ImVec2(0.0f, g.FontSize));
10392     window->DC.LayoutType = backup_layout_type;
10393 }
10394
10395 void ImGui::NextColumn()
10396 {
10397     ImGuiWindow* window = GetCurrentWindow();
10398     if (window->SkipItems || window->DC.ColumnsCount <= 1)
10399         return;
10400
10401     ImGuiContext& g = *GImGui;
10402     PopItemWidth();
10403     PopClipRect();
10404
10405     window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y);
10406     if (++window->DC.ColumnsCurrent < window->DC.ColumnsCount)
10407     {
10408         // Columns 1+ cancel out IndentX
10409         window->DC.ColumnsOffsetX = GetColumnOffset(window->DC.ColumnsCurrent) - window->DC.IndentX + g.Style.ItemSpacing.x;
10410         window->DrawList->ChannelsSetCurrent(window->DC.ColumnsCurrent);
10411     }
10412     else
10413     {
10414         window->DC.ColumnsCurrent = 0;
10415         window->DC.ColumnsOffsetX = 0.0f;
10416         window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY;
10417         window->DrawList->ChannelsSetCurrent(0);
10418     }
10419     window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
10420     window->DC.CursorPos.y = window->DC.ColumnsCellMinY;
10421     window->DC.CurrentLineHeight = 0.0f;
10422     window->DC.CurrentLineTextBaseOffset = 0.0f;
10423
10424     PushColumnClipRect();
10425     PushItemWidth(GetColumnWidth() * 0.65f);  // FIXME: Move on columns setup
10426 }
10427
10428 int ImGui::GetColumnIndex()
10429 {
10430     ImGuiWindow* window = GetCurrentWindowRead();
10431     return window->DC.ColumnsCurrent;
10432 }
10433
10434 int ImGui::GetColumnsCount()
10435 {
10436     ImGuiWindow* window = GetCurrentWindowRead();
10437     return window->DC.ColumnsCount;
10438 }
10439
10440 static float OffsetNormToPixels(ImGuiWindow* window, float offset_norm)
10441 {
10442     return offset_norm * (window->DC.ColumnsMaxX - window->DC.ColumnsMinX);
10443 }
10444
10445 static float PixelsToOffsetNorm(ImGuiWindow* window, float offset)
10446 {
10447     return (offset - window->DC.ColumnsMinX) / (window->DC.ColumnsMaxX - window->DC.ColumnsMinX);
10448 }
10449
10450 static float GetDraggedColumnOffset(int column_index)
10451 {
10452     // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing
10453     // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning.
10454     ImGuiContext& g = *GImGui;
10455     ImGuiWindow* window = g.CurrentWindow;
10456     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.
10457     IM_ASSERT(g.ActiveId == window->DC.ColumnsSetId + ImGuiID(column_index));
10458
10459     float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x - window->Pos.x;
10460     x = ImMax(x, ImGui::GetColumnOffset(column_index-1) + g.Style.ColumnsMinSpacing);
10461     if ((window->DC.ColumnsFlags & ImGuiColumnsFlags_NoPreserveWidths))
10462         x = ImMin(x, ImGui::GetColumnOffset(column_index+1) - g.Style.ColumnsMinSpacing);
10463
10464     return x;
10465 }
10466
10467 float ImGui::GetColumnOffset(int column_index)
10468 {
10469     ImGuiWindow* window = GetCurrentWindowRead();
10470     if (column_index < 0)
10471         column_index = window->DC.ColumnsCurrent;
10472
10473     /*
10474     if (g.ActiveId)
10475     {
10476         ImGuiContext& g = *GImGui;
10477         const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index);
10478         if (g.ActiveId == column_id)
10479             return GetDraggedColumnOffset(column_index);
10480     }
10481     */
10482
10483     IM_ASSERT(column_index < window->DC.ColumnsData.Size);
10484     const float t = window->DC.ColumnsData[column_index].OffsetNorm;
10485     const float x_offset = ImLerp(window->DC.ColumnsMinX, window->DC.ColumnsMaxX, t);
10486     return x_offset;
10487 }
10488
10489 void ImGui::SetColumnOffset(int column_index, float offset)
10490 {
10491     ImGuiContext& g = *GImGui;
10492     ImGuiWindow* window = GetCurrentWindow();
10493     if (column_index < 0)
10494         column_index = window->DC.ColumnsCurrent;
10495
10496     IM_ASSERT(column_index < window->DC.ColumnsData.Size);
10497
10498     const bool preserve_width = !(window->DC.ColumnsFlags & ImGuiColumnsFlags_NoPreserveWidths) && (column_index < window->DC.ColumnsCount-1);
10499     const float width = preserve_width ? GetColumnWidth(column_index) : 0.0f;
10500
10501     if (!(window->DC.ColumnsFlags & ImGuiColumnsFlags_NoForceWithinWindow))
10502         offset = ImMin(offset, window->DC.ColumnsMaxX - g.Style.ColumnsMinSpacing * (window->DC.ColumnsCount - column_index));
10503     const float offset_norm = PixelsToOffsetNorm(window, offset);
10504
10505     const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index);
10506     window->DC.StateStorage->SetFloat(column_id, offset_norm);
10507     window->DC.ColumnsData[column_index].OffsetNorm = offset_norm;
10508
10509     if (preserve_width)
10510         SetColumnOffset(column_index + 1, offset + ImMax(g.Style.ColumnsMinSpacing, width));
10511 }
10512
10513 float ImGui::GetColumnWidth(int column_index)
10514 {
10515     ImGuiWindow* window = GetCurrentWindowRead();
10516     if (column_index < 0)
10517         column_index = window->DC.ColumnsCurrent;
10518
10519     return OffsetNormToPixels(window, window->DC.ColumnsData[column_index+1].OffsetNorm - window->DC.ColumnsData[column_index].OffsetNorm);
10520 }
10521
10522 void ImGui::SetColumnWidth(int column_index, float width)
10523 {
10524     ImGuiWindow* window = GetCurrentWindowRead();
10525     if (column_index < 0)
10526         column_index = window->DC.ColumnsCurrent;
10527
10528     SetColumnOffset(column_index+1, GetColumnOffset(column_index) + width);
10529 }
10530
10531 void ImGui::PushColumnClipRect(int column_index)
10532 {
10533     ImGuiWindow* window = GetCurrentWindowRead();
10534     if (column_index < 0)
10535         column_index = window->DC.ColumnsCurrent;
10536
10537     PushClipRect(window->DC.ColumnsData[column_index].ClipRect.Min, window->DC.ColumnsData[column_index].ClipRect.Max, false);
10538 }
10539
10540 void ImGui::BeginColumns(const char* id, int columns_count, ImGuiColumnsFlags flags)
10541 {
10542     ImGuiContext& g = *GImGui;
10543     ImGuiWindow* window = GetCurrentWindow();
10544
10545     IM_ASSERT(columns_count > 1);
10546     IM_ASSERT(window->DC.ColumnsCount == 1); // Nested columns are currently not supported
10547
10548     // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget.
10549     // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer.
10550     PushID(0x11223347 + (id ? 0 : columns_count));
10551     window->DC.ColumnsSetId = window->GetID(id ? id : "columns");
10552     PopID();
10553
10554     // Set state for first column
10555     window->DC.ColumnsCurrent = 0;
10556     window->DC.ColumnsCount = columns_count;
10557     window->DC.ColumnsFlags = flags;
10558
10559     const float content_region_width = (window->SizeContentsExplicit.x != 0.0f) ? (window->SizeContentsExplicit.x) : (window->Size.x -window->ScrollbarSizes.x);
10560     window->DC.ColumnsMinX = window->DC.IndentX - g.Style.ItemSpacing.x; // Lock our horizontal range
10561     //window->DC.ColumnsMaxX = content_region_width - window->Scroll.x -((window->Flags & ImGuiWindowFlags_NoScrollbar) ? 0 : g.Style.ScrollbarSize);// - window->WindowPadding().x;
10562     window->DC.ColumnsMaxX = content_region_width - window->Scroll.x;
10563     window->DC.ColumnsStartPosY = window->DC.CursorPos.y;
10564     window->DC.ColumnsStartMaxPosX = window->DC.CursorMaxPos.x;
10565     window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY = window->DC.CursorPos.y;
10566     window->DC.ColumnsOffsetX = 0.0f;
10567     window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
10568
10569     // Cache column offsets
10570     window->DC.ColumnsData.resize(columns_count + 1);
10571     for (int column_index = 0; column_index < columns_count + 1; column_index++)
10572     {
10573         const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index);
10574         KeepAliveID(column_id);
10575         const float default_t = column_index / (float)window->DC.ColumnsCount;
10576         float t = window->DC.StateStorage->GetFloat(column_id, default_t);
10577         if (!(window->DC.ColumnsFlags & ImGuiColumnsFlags_NoForceWithinWindow))
10578             t = ImMin(t, PixelsToOffsetNorm(window, window->DC.ColumnsMaxX - g.Style.ColumnsMinSpacing * (window->DC.ColumnsCount - column_index)));
10579         window->DC.ColumnsData[column_index].OffsetNorm = t;
10580     }
10581
10582     // Cache clipping rectangles
10583     for (int column_index = 0; column_index < columns_count; column_index++)
10584     {
10585         float clip_x1 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(column_index) - 1.0f);
10586         float clip_x2 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(column_index + 1) - 1.0f);
10587         window->DC.ColumnsData[column_index].ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX);
10588         window->DC.ColumnsData[column_index].ClipRect.ClipWith(window->ClipRect);
10589     }
10590
10591     window->DrawList->ChannelsSplit(window->DC.ColumnsCount);
10592     PushColumnClipRect();
10593     PushItemWidth(GetColumnWidth() * 0.65f);
10594 }
10595
10596 void ImGui::EndColumns()
10597 {
10598     ImGuiContext& g = *GImGui;
10599     ImGuiWindow* window = GetCurrentWindow();
10600     IM_ASSERT(window->DC.ColumnsCount > 1);
10601
10602     PopItemWidth();
10603     PopClipRect();
10604     window->DrawList->ChannelsMerge();
10605
10606     window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y);
10607     window->DC.CursorPos.y = window->DC.ColumnsCellMaxY;
10608     if (!(window->DC.ColumnsFlags & ImGuiColumnsFlags_GrowParentContentsSize))
10609         window->DC.CursorMaxPos.x = ImMax(window->DC.ColumnsStartMaxPosX, window->DC.ColumnsMaxX);  // Restore cursor max pos, as columns don't grow parent
10610
10611     // Draw columns borders and handle resize
10612     if (!(window->DC.ColumnsFlags & ImGuiColumnsFlags_NoBorder) && !window->SkipItems)
10613     {
10614         const float y1 = window->DC.ColumnsStartPosY;
10615         const float y2 = window->DC.CursorPos.y;
10616         int dragging_column = -1;
10617         for (int i = 1; i < window->DC.ColumnsCount; i++)
10618         {
10619             float x = window->Pos.x + GetColumnOffset(i);
10620             const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(i);
10621             const float column_hw = 4.0f; // Half-width for interaction
10622             const ImRect column_rect(ImVec2(x - column_hw, y1), ImVec2(x + column_hw, y2));
10623             if (IsClippedEx(column_rect, column_id, false))
10624                 continue;
10625             
10626             bool hovered = false, held = false;
10627             if (!(window->DC.ColumnsFlags & ImGuiColumnsFlags_NoResize))
10628             {
10629                 ButtonBehavior(column_rect, column_id, &hovered, &held);
10630                 if (hovered || held)
10631                     g.MouseCursor = ImGuiMouseCursor_ResizeEW;
10632                 if (held && g.ActiveIdIsJustActivated)
10633                     g.ActiveIdClickOffset.x -= column_hw; // Store from center of column line (we used a 8 wide rect for columns clicking). This is used by GetDraggedColumnOffset().
10634                 if (held)
10635                     dragging_column = i;
10636             }
10637
10638             // Draw column
10639             // We clip the Y boundaries CPU side because very long triangles are mishandled by some GPU drivers.
10640             const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
10641             const float xi = (float)(int)x;
10642             window->DrawList->AddLine(ImVec2(xi, ImMax(y1 + 1.0f, window->ClipRect.Min.y)), ImVec2(xi, ImMin(y2, window->ClipRect.Max.y)), col);
10643         }
10644
10645         // Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame.
10646         if (dragging_column != -1)
10647         {
10648             float x = GetDraggedColumnOffset(dragging_column);
10649             SetColumnOffset(dragging_column, x);
10650         }
10651     }
10652
10653     window->DC.ColumnsSetId = 0;
10654     window->DC.ColumnsCurrent = 0;
10655     window->DC.ColumnsCount = 1;
10656     window->DC.ColumnsFlags = 0;
10657     window->DC.ColumnsData.resize(0);
10658     window->DC.ColumnsOffsetX = 0.0f;
10659     window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
10660 }
10661
10662 // [2017/08: This is currently the only public API, while we are working on making BeginColumns/EndColumns user-facing]
10663 void ImGui::Columns(int columns_count, const char* id, bool border)
10664 {
10665     ImGuiWindow* window = GetCurrentWindow();
10666     IM_ASSERT(columns_count >= 1);
10667
10668     if (window->DC.ColumnsCount != columns_count && window->DC.ColumnsCount != 1)
10669         EndColumns();
10670     
10671     ImGuiColumnsFlags flags = (border ? 0 : ImGuiColumnsFlags_NoBorder);
10672     //flags |= ImGuiColumnsFlags_NoPreserveWidths; // NB: Legacy behavior
10673     if (columns_count != 1)
10674         BeginColumns(id, columns_count, flags);
10675 }
10676
10677 void ImGui::Indent(float indent_w)
10678 {
10679     ImGuiContext& g = *GImGui;
10680     ImGuiWindow* window = GetCurrentWindow();
10681     window->DC.IndentX += (indent_w > 0.0f) ? indent_w : g.Style.IndentSpacing;
10682     window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX;
10683 }
10684
10685 void ImGui::Unindent(float indent_w)
10686 {
10687     ImGuiContext& g = *GImGui;
10688     ImGuiWindow* window = GetCurrentWindow();
10689     window->DC.IndentX -= (indent_w > 0.0f) ? indent_w : g.Style.IndentSpacing;
10690     window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX;
10691 }
10692
10693 void ImGui::TreePush(const char* str_id)
10694 {
10695     ImGuiWindow* window = GetCurrentWindow();
10696     Indent();
10697     window->DC.TreeDepth++;
10698     PushID(str_id ? str_id : "#TreePush");
10699 }
10700
10701 void ImGui::TreePush(const void* ptr_id)
10702 {
10703     ImGuiWindow* window = GetCurrentWindow();
10704     Indent();
10705     window->DC.TreeDepth++;
10706     PushID(ptr_id ? ptr_id : (const void*)"#TreePush");
10707 }
10708
10709 void ImGui::TreePushRawID(ImGuiID id)
10710 {
10711     ImGuiWindow* window = GetCurrentWindow();
10712     Indent();
10713     window->DC.TreeDepth++;
10714     window->IDStack.push_back(id);
10715 }
10716
10717 void ImGui::TreePop()
10718 {
10719     ImGuiWindow* window = GetCurrentWindow();
10720     Unindent();
10721     window->DC.TreeDepth--;
10722     PopID();
10723 }
10724
10725 void ImGui::Value(const char* prefix, bool b)
10726 {
10727     Text("%s: %s", prefix, (b ? "true" : "false"));
10728 }
10729
10730 void ImGui::Value(const char* prefix, int v)
10731 {
10732     Text("%s: %d", prefix, v);
10733 }
10734
10735 void ImGui::Value(const char* prefix, unsigned int v)
10736 {
10737     Text("%s: %d", prefix, v);
10738 }
10739
10740 void ImGui::Value(const char* prefix, float v, const char* float_format)
10741 {
10742     if (float_format)
10743     {
10744         char fmt[64];
10745         ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format);
10746         Text(fmt, prefix, v);
10747     }
10748     else
10749     {
10750         Text("%s: %.3f", prefix, v);
10751     }
10752 }
10753
10754 //-----------------------------------------------------------------------------
10755 // PLATFORM DEPENDENT HELPERS
10756 //-----------------------------------------------------------------------------
10757
10758 #if defined(_WIN32) && !defined(_WINDOWS_) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS))
10759 #undef WIN32_LEAN_AND_MEAN
10760 #define WIN32_LEAN_AND_MEAN
10761 #include <windows.h>
10762 #endif
10763
10764 // Win32 API clipboard implementation
10765 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
10766
10767 #ifdef _MSC_VER
10768 #pragma comment(lib, "user32")
10769 #endif
10770
10771 static const char* GetClipboardTextFn_DefaultImpl(void*)
10772 {
10773     static ImVector<char> buf_local;
10774     buf_local.clear();
10775     if (!OpenClipboard(NULL))
10776         return NULL;
10777     HANDLE wbuf_handle = GetClipboardData(CF_UNICODETEXT);
10778     if (wbuf_handle == NULL)
10779     {
10780         CloseClipboard();
10781         return NULL;
10782     }
10783     if (ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle))
10784     {
10785         int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
10786         buf_local.resize(buf_len);
10787         ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL);
10788     }
10789     GlobalUnlock(wbuf_handle);
10790     CloseClipboard();
10791     return buf_local.Data;
10792 }
10793
10794 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
10795 {
10796     if (!OpenClipboard(NULL))
10797         return;
10798     const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
10799     HGLOBAL wbuf_handle = GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
10800     if (wbuf_handle == NULL)
10801     {
10802         CloseClipboard();
10803         return;
10804     }
10805     ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle);
10806     ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
10807     GlobalUnlock(wbuf_handle);
10808     EmptyClipboard();
10809     SetClipboardData(CF_UNICODETEXT, wbuf_handle);
10810     CloseClipboard();
10811 }
10812
10813 #else
10814
10815 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
10816 static const char* GetClipboardTextFn_DefaultImpl(void*)
10817 {
10818     ImGuiContext& g = *GImGui;
10819     return g.PrivateClipboard.empty() ? NULL : g.PrivateClipboard.begin();
10820 }
10821
10822 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
10823 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
10824 {
10825     ImGuiContext& g = *GImGui;
10826     g.PrivateClipboard.clear();
10827     const char* text_end = text + strlen(text);
10828     g.PrivateClipboard.resize((int)(text_end - text) + 1);
10829     memcpy(&g.PrivateClipboard[0], text, (size_t)(text_end - text));
10830     g.PrivateClipboard[(int)(text_end - text)] = 0;
10831 }
10832
10833 #endif
10834
10835 // Win32 API IME support (for Asian languages, etc.)
10836 #if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
10837
10838 #include <imm.h>
10839 #ifdef _MSC_VER
10840 #pragma comment(lib, "imm32")
10841 #endif
10842
10843 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
10844 {
10845     // Notify OS Input Method Editor of text input position
10846     if (HWND hwnd = (HWND)GImGui->IO.ImeWindowHandle)
10847         if (HIMC himc = ImmGetContext(hwnd))
10848         {
10849             COMPOSITIONFORM cf;
10850             cf.ptCurrentPos.x = x;
10851             cf.ptCurrentPos.y = y;
10852             cf.dwStyle = CFS_FORCE_POSITION;
10853             ImmSetCompositionWindow(himc, &cf);
10854         }
10855 }
10856
10857 #else
10858
10859 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
10860
10861 #endif
10862
10863 //-----------------------------------------------------------------------------
10864 // HELP
10865 //-----------------------------------------------------------------------------
10866
10867 void ImGui::ShowMetricsWindow(bool* p_open)
10868 {
10869     if (ImGui::Begin("ImGui Metrics", p_open))
10870     {
10871         ImGui::Text("ImGui %s", ImGui::GetVersion());
10872         ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
10873         ImGui::Text("%d vertices, %d indices (%d triangles)", ImGui::GetIO().MetricsRenderVertices, ImGui::GetIO().MetricsRenderIndices, ImGui::GetIO().MetricsRenderIndices / 3);
10874         ImGui::Text("%d allocations", ImGui::GetIO().MetricsAllocs);
10875         static bool show_clip_rects = true;
10876         ImGui::Checkbox("Show clipping rectangles when hovering an ImDrawCmd", &show_clip_rects);
10877         ImGui::Separator();
10878
10879         struct Funcs
10880         {
10881             static void NodeDrawList(ImDrawList* draw_list, const char* label)
10882             {
10883                 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);
10884                 if (draw_list == ImGui::GetWindowDrawList())
10885                 {
10886                     ImGui::SameLine();
10887                     ImGui::TextColored(ImColor(255,100,100), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
10888                     if (node_open) ImGui::TreePop();
10889                     return;
10890                 }
10891                 if (!node_open)
10892                     return;
10893
10894                 ImDrawList* overlay_draw_list = &GImGui->OverlayDrawList;   // Render additional visuals into the top-most draw list
10895                 overlay_draw_list->PushClipRectFullScreen();
10896                 int elem_offset = 0;
10897                 for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
10898                 {
10899                     if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0)
10900                         continue;
10901                     if (pcmd->UserCallback)
10902                     {
10903                         ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
10904                         continue;
10905                     }
10906                     ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
10907                     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);
10908                     if (show_clip_rects && ImGui::IsItemHovered())
10909                     {
10910                         ImRect clip_rect = pcmd->ClipRect;
10911                         ImRect vtxs_rect;
10912                         for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++)
10913                             vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos);
10914                         clip_rect.Floor(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,255,0,255));
10915                         vtxs_rect.Floor(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,0,255,255));
10916                     }
10917                     if (!pcmd_node_open)
10918                         continue;
10919                     ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
10920                     while (clipper.Step())
10921                         for (int prim = clipper.DisplayStart, vtx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++)
10922                         {
10923                             char buf[300];
10924                             char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf);
10925                             ImVec2 triangles_pos[3];
10926                             for (int n = 0; n < 3; n++, vtx_i++)
10927                             {
10928                                 ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[vtx_i] : vtx_i];
10929                                 triangles_pos[n] = v.pos;
10930                                 buf_p += ImFormatString(buf_p, (int)(buf_end - buf_p), "%s %04d { pos = (%8.2f,%8.2f), uv = (%.6f,%.6f), col = %08X }\n", (n == 0) ? "vtx" : "   ", vtx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
10931                             }
10932                             ImGui::Selectable(buf, false);
10933                             if (ImGui::IsItemHovered())
10934                                 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
10935                         }
10936                     ImGui::TreePop();
10937                 }
10938                 overlay_draw_list->PopClipRect();
10939                 ImGui::TreePop();
10940             }
10941
10942             static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label)
10943             {
10944                 if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size))
10945                     return;
10946                 for (int i = 0; i < windows.Size; i++)
10947                     Funcs::NodeWindow(windows[i], "Window");
10948                 ImGui::TreePop();
10949             }
10950
10951             static void NodeWindow(ImGuiWindow* window, const char* label)
10952             {
10953                 if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window))
10954                     return;
10955                 NodeDrawList(window->DrawList, "DrawList");
10956                 ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y);
10957                 if (ImGui::IsItemHovered())
10958                     GImGui->OverlayDrawList.AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255,255,0,255));
10959                 ImGui::BulletText("Scroll: (%.2f,%.2f)", window->Scroll.x, window->Scroll.y);
10960                 ImGui::BulletText("Active: %d, WriteAccessed: %d", window->Active, window->WriteAccessed);
10961                 if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
10962                 if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
10963                 ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair));
10964                 ImGui::TreePop();
10965             }
10966         };
10967
10968         ImGuiContext& g = *GImGui;                // Access private state
10969         Funcs::NodeWindows(g.Windows, "Windows");
10970         if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.RenderDrawLists[0].Size))
10971         {
10972             for (int layer = 0; layer < IM_ARRAYSIZE(g.RenderDrawLists); layer++)
10973                 for (int i = 0; i < g.RenderDrawLists[layer].Size; i++)
10974                     Funcs::NodeDrawList(g.RenderDrawLists[0][i], "DrawList");
10975             ImGui::TreePop();
10976         }
10977         if (ImGui::TreeNode("Popups", "Open Popups Stack (%d)", g.OpenPopupStack.Size))
10978         {
10979             for (int i = 0; i < g.OpenPopupStack.Size; i++)
10980             {
10981                 ImGuiWindow* window = g.OpenPopupStack[i].Window;
10982                 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" : "");
10983             }
10984             ImGui::TreePop();
10985         }
10986         if (ImGui::TreeNode("Basic state"))
10987         {
10988             ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
10989             ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
10990             ImGui::Text("HoveredId: 0x%08X/0x%08X (%.2f sec)", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not
10991             ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec)", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer);
10992             ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
10993             ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
10994             ImGui::TreePop();
10995         }
10996     }
10997     ImGui::End();
10998 }
10999
11000 //-----------------------------------------------------------------------------
11001
11002 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
11003 // 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.
11004 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
11005 #include "imgui_user.inl"
11006 #endif
11007
11008 //-----------------------------------------------------------------------------